From e2adf17eeb0dbeb213b59705b7d4f7df4376c889 Mon Sep 17 00:00:00 2001 From: 22127125 Date: Wed, 4 Mar 2026 21:13:18 +0700 Subject: [PATCH 01/19] add conditional database fields in UI for advanced node.js template --- .../examples/advanced-template/template.yaml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/apps/portal/examples/advanced-template/template.yaml b/apps/portal/examples/advanced-template/template.yaml index 2b037c1..91da460 100644 --- a/apps/portal/examples/advanced-template/template.yaml +++ b/apps/portal/examples/advanced-template/template.yaml @@ -43,6 +43,39 @@ spec: type: string description: The name of the repository (e.g. my-service) + - title: Database Configuration + properties: + requireDatabase: + title: Add Database + type: boolean + description: Check to provision a database for this project + default: false + dependencies: + requireDatabase: + oneOf: + - properties: + requireDatabase: + const: false + - properties: + requireDatabase: + const: true + databaseType: + title: Database Type + type: string + enum: [postgres] + default: postgres + databaseName: + title: Database Name + type: string + description: The name of the database to create + databaseVersion: + title: Database Version + type: string + description: Select the PostgreSQL version + enum: ['13', '14', '15', '16'] + default: '15' + required: [databaseType, databaseName, databaseVersion] + - title: Repository & Webhook required: - repoUrl From a5dfffba3b267b2a4704a6ee0c161dbb7741a944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=E1=BB=93=20Ph=C6=B0=E1=BB=9Bc=20Ngh=C4=A9a?= <118056302+nghiaz160904@users.noreply.github.com> Date: Wed, 4 Mar 2026 21:23:56 +0700 Subject: [PATCH 02/19] Feat(operator): auto-generate database credentials (#45) --- apps/operator/config/rbac/role.yaml | 7 - .../internal/controller/database_resources.go | 294 ++++++++++ .../controller/database_resources_test.go | 527 ++++++++++++++++++ .../controller/heliosapp_controller.go | 14 +- 4 files changed, 834 insertions(+), 8 deletions(-) create mode 100644 apps/operator/internal/controller/database_resources.go create mode 100644 apps/operator/internal/controller/database_resources_test.go diff --git a/apps/operator/config/rbac/role.yaml b/apps/operator/config/rbac/role.yaml index 4c21891..ef8fe77 100644 --- a/apps/operator/config/rbac/role.yaml +++ b/apps/operator/config/rbac/role.yaml @@ -8,13 +8,6 @@ rules: - "" resources: - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - services verbs: - create diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go new file mode 100644 index 0000000..e314004 --- /dev/null +++ b/apps/operator/internal/controller/database_resources.go @@ -0,0 +1,294 @@ +// database_resources.go handles database credential generation and Secret creation +// for components with database traits. +// +// The CUE engine generates ConfigMaps with database metadata (host, port, name), +// but credentials (username, password) are generated by this Go code for security +// reasons - secrets should never be stored in CUE definitions or GitOps repos. +package controller + +import ( + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "math/big" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" +) + +const ( + // DefaultPasswordLength is the length of generated passwords + DefaultPasswordLength = 32 + + // DefaultUsernameLength is the length of generated usernames + DefaultUsernameLength = 16 + + // PasswordCharset contains valid characters for password generation + // Includes uppercase, lowercase, digits, and special characters + PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" + + // UsernameCharset contains valid characters for username generation + // Lowercase letters and digits only for database compatibility + UsernameCharset = "abcdefghijklmnopqrstuvwxyz0123456789" + + // DatabaseTraitType is the trait type identifier for database traits + DatabaseTraitType = "database" +) + +// DatabaseCredentials holds generated database credentials +type DatabaseCredentials struct { + Username string + Password string +} + +// DatabaseTraitProperties represents the properties of a database trait +type DatabaseTraitProperties struct { + DBType string `json:"dbType"` + DBName string `json:"dbName"` + Port int `json:"port"` + Version string `json:"version"` + Storage string `json:"storage"` +} + +// GenerateSecurePassword generates a cryptographically secure random password +func GenerateSecurePassword(length int) (string, error) { + if length <= 0 { + length = DefaultPasswordLength + } + + password := make([]byte, length) + charsetLen := big.NewInt(int64(len(PasswordCharset))) + + for i := 0; i < length; i++ { + idx, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + password[i] = PasswordCharset[idx.Int64()] + } + + return string(password), nil +} + +// GenerateSecureUsername generates a cryptographically secure random username +func GenerateSecureUsername(length int) (string, error) { + if length <= 0 { + length = DefaultUsernameLength + } + + // Ensure username starts with a letter (database requirement) + username := make([]byte, length) + lettersOnly := "abcdefghijklmnopqrstuvwxyz" + lettersLen := big.NewInt(int64(len(lettersOnly))) + + // First character must be a letter + idx, err := rand.Int(rand.Reader, lettersLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + username[0] = lettersOnly[idx.Int64()] + + // Rest can be letters or digits + charsetLen := big.NewInt(int64(len(UsernameCharset))) + for i := 1; i < length; i++ { + idx, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + username[i] = UsernameCharset[idx.Int64()] + } + + return string(username), nil +} + +// GenerateCredentials generates a new set of database credentials +func GenerateCredentials() (*DatabaseCredentials, error) { + username, err := GenerateSecureUsername(DefaultUsernameLength) + if err != nil { + return nil, fmt.Errorf("failed to generate username: %w", err) + } + + password, err := GenerateSecurePassword(DefaultPasswordLength) + if err != nil { + return nil, fmt.Errorf("failed to generate password: %w", err) + } + + return &DatabaseCredentials{ + Username: username, + Password: password, + }, nil +} + +// GenerateDatabaseSecret creates a Kubernetes Secret containing database credentials +func GenerateDatabaseSecret(namespace, secretName, componentName string, creds *DatabaseCredentials, dbHost string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{ + "app": componentName, + "helios.io/managed-by": "operator", + "helios.io/secret-type": "database-credentials", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "DB_USER": []byte(creds.Username), + "DB_PASS": []byte(creds.Password), + "DB_HOST": []byte(dbHost), + }, + } +} + +// GetDatabaseSecretName returns the conventional secret name for a component +func GetDatabaseSecretName(componentName string) string { + return fmt.Sprintf("%s-db-secret", componentName) +} + +// GetDatabaseHost returns the conventional database host for a component +func GetDatabaseHost(componentName string) string { + return fmt.Sprintf("%s-db", componentName) +} + +// ExtractDatabaseTraits extracts all database traits from HeliosApp components +func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { + ComponentName string + Properties DatabaseTraitProperties +} { + var dbTraits []struct { + ComponentName string + Properties DatabaseTraitProperties + } + + for _, component := range app.Spec.Components { + for _, trait := range component.Traits { + if strings.ToLower(trait.Type) == DatabaseTraitType { + var props DatabaseTraitProperties + if trait.Properties != nil && trait.Properties.Raw != nil { + if err := json.Unmarshal(trait.Properties.Raw, &props); err != nil { + // Log error but continue - don't fail the entire reconciliation + continue + } + } + dbTraits = append(dbTraits, struct { + ComponentName string + Properties DatabaseTraitProperties + }{ + ComponentName: component.Name, + Properties: props, + }) + } + } + } + + return dbTraits +} + +// reconcileDatabaseSecrets ensures database credential secrets exist for all +// components with database traits. If a secret already exists, it is not modified +// to preserve existing credentials. +func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping secret creation") + return nil + } + + for _, dbTrait := range dbTraits { + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + dbHost := GetDatabaseHost(dbTrait.ComponentName) + + // Check if secret already exists + existingSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{ + Name: secretName, + Namespace: app.Namespace, + }, existingSecret) + + if err == nil { + // Secret already exists - do not overwrite to preserve credentials + log.Info("Database secret already exists, skipping", + "component", dbTrait.ComponentName, + "secret", secretName) + continue + } + + if !errors.IsNotFound(err) { + // Unexpected error + log.Error(err, "Failed to check for existing database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to check for database secret %s: %w", secretName, err) + } + + // Secret doesn't exist - generate new credentials + log.Info("Generating database credentials", + "component", dbTrait.ComponentName, + "secret", secretName) + + creds, err := GenerateCredentials() + if err != nil { + log.Error(err, "Failed to generate database credentials", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to generate credentials for %s: %w", dbTrait.ComponentName, err) + } + + // Create the secret + secret := GenerateDatabaseSecret(app.Namespace, secretName, dbTrait.ComponentName, creds, dbHost) + + // Set owner reference so secret is garbage collected with the HeliosApp + if err := ctrl.SetControllerReference(app, secret, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to set owner reference for secret %s: %w", secretName, err) + } + + if err := r.Create(ctx, secret); err != nil { + if errors.IsAlreadyExists(err) { + // Race condition - secret was created between our check and create + log.Info("Database secret was created concurrently, skipping", + "component", dbTrait.ComponentName, + "secret", secretName) + continue + } + log.Error(err, "Failed to create database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to create database secret %s: %w", secretName, err) + } + + log.Info("Successfully created database secret", + "component", dbTrait.ComponentName, + "secret", secretName, + "dbHost", dbHost) + } + + return nil +} + +// GenerateBase64Token generates a random base64-encoded token +// Useful for generating secure webhook secrets or API tokens +func GenerateBase64Token(byteLength int) (string, error) { + if byteLength <= 0 { + byteLength = 32 + } + + bytes := make([]byte, byteLength) + if _, err := rand.Read(bytes); err != nil { + return "", fmt.Errorf("failed to generate random bytes: %w", err) + } + + return base64.StdEncoding.EncodeToString(bytes), nil +} diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go new file mode 100644 index 0000000..41e5384 --- /dev/null +++ b/apps/operator/internal/controller/database_resources_test.go @@ -0,0 +1,527 @@ +package controller + +import ( + "context" + "encoding/json" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" +) + +func TestGenerateSecurePassword(t *testing.T) { + tests := []struct { + name string + length int + }{ + {"default length", 0}, + {"short password", 8}, + {"long password", 64}, + {"standard length", DefaultPasswordLength}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + password, err := GenerateSecurePassword(tt.length) + if err != nil { + t.Fatalf("GenerateSecurePassword failed: %v", err) + } + + expectedLen := tt.length + if expectedLen <= 0 { + expectedLen = DefaultPasswordLength + } + + if len(password) != expectedLen { + t.Errorf("Expected password length %d, got %d", expectedLen, len(password)) + } + + // Verify characters are from the charset + for _, c := range password { + found := false + for _, allowed := range PasswordCharset { + if c == allowed { + found = true + break + } + } + if !found { + t.Errorf("Invalid character %c in password", c) + } + } + }) + } +} + +func TestGenerateSecureUsername(t *testing.T) { + tests := []struct { + name string + length int + }{ + {"default length", 0}, + {"short username", 8}, + {"long username", 32}, + {"standard length", DefaultUsernameLength}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + username, err := GenerateSecureUsername(tt.length) + if err != nil { + t.Fatalf("GenerateSecureUsername failed: %v", err) + } + + expectedLen := tt.length + if expectedLen <= 0 { + expectedLen = DefaultUsernameLength + } + + if len(username) != expectedLen { + t.Errorf("Expected username length %d, got %d", expectedLen, len(username)) + } + + // Verify first character is a letter (database requirement) + firstChar := username[0] + if firstChar < 'a' || firstChar > 'z' { + t.Errorf("First character %c must be a lowercase letter", firstChar) + } + + // Verify characters are from the charset + for _, c := range username { + found := false + for _, allowed := range UsernameCharset { + if c == rune(allowed) { + found = true + break + } + } + if !found { + t.Errorf("Invalid character %c in username", c) + } + } + }) + } +} + +func TestGenerateCredentials(t *testing.T) { + creds, err := GenerateCredentials() + if err != nil { + t.Fatalf("GenerateCredentials failed: %v", err) + } + + if creds.Username == "" { + t.Error("Username should not be empty") + } + + if creds.Password == "" { + t.Error("Password should not be empty") + } + + if len(creds.Username) != DefaultUsernameLength { + t.Errorf("Expected username length %d, got %d", DefaultUsernameLength, len(creds.Username)) + } + + if len(creds.Password) != DefaultPasswordLength { + t.Errorf("Expected password length %d, got %d", DefaultPasswordLength, len(creds.Password)) + } +} + +func TestGenerateCredentialsUniqueness(t *testing.T) { + // Generate multiple credentials and ensure they are unique + credentials := make(map[string]bool) + iterations := 100 + + for i := 0; i < iterations; i++ { + creds, err := GenerateCredentials() + if err != nil { + t.Fatalf("GenerateCredentials failed on iteration %d: %v", i, err) + } + + key := creds.Username + ":" + creds.Password + if credentials[key] { + t.Errorf("Duplicate credentials generated on iteration %d", i) + } + credentials[key] = true + } +} + +func TestGenerateDatabaseSecret(t *testing.T) { + namespace := "test-namespace" + secretName := "my-app-db-secret" + componentName := "my-app" + dbHost := "my-app-db" + + creds := &DatabaseCredentials{ + Username: "testuser", + Password: "testpassword123", + } + + secret := GenerateDatabaseSecret(namespace, secretName, componentName, creds, dbHost) + + // Verify metadata + if secret.Name != secretName { + t.Errorf("Expected secret name %q, got %q", secretName, secret.Name) + } + + if secret.Namespace != namespace { + t.Errorf("Expected namespace %q, got %q", namespace, secret.Namespace) + } + + // Verify labels + expectedLabels := map[string]string{ + "app": componentName, + "helios.io/managed-by": "operator", + "helios.io/secret-type": "database-credentials", + } + for k, v := range expectedLabels { + if secret.Labels[k] != v { + t.Errorf("Expected label %s=%s, got %s", k, v, secret.Labels[k]) + } + } + + // Verify secret data + if string(secret.Data["DB_USER"]) != creds.Username { + t.Errorf("Expected DB_USER %q, got %q", creds.Username, string(secret.Data["DB_USER"])) + } + + if string(secret.Data["DB_PASS"]) != creds.Password { + t.Errorf("Expected DB_PASS %q, got %q", creds.Password, string(secret.Data["DB_PASS"])) + } + + if string(secret.Data["DB_HOST"]) != dbHost { + t.Errorf("Expected DB_HOST %q, got %q", dbHost, string(secret.Data["DB_HOST"])) + } + + // Verify secret type + if secret.Type != corev1.SecretTypeOpaque { + t.Errorf("Expected secret type %v, got %v", corev1.SecretTypeOpaque, secret.Type) + } +} + +func TestGetDatabaseSecretName(t *testing.T) { + tests := []struct { + componentName string + expected string + }{ + {"my-app", "my-app-db-secret"}, + {"api-server", "api-server-db-secret"}, + {"backend", "backend-db-secret"}, + } + + for _, tt := range tests { + t.Run(tt.componentName, func(t *testing.T) { + result := GetDatabaseSecretName(tt.componentName) + if result != tt.expected { + t.Errorf("Expected %q, got %q", tt.expected, result) + } + }) + } +} + +func TestGetDatabaseHost(t *testing.T) { + tests := []struct { + componentName string + expected string + }{ + {"my-app", "my-app-db"}, + {"api-server", "api-server-db"}, + {"backend", "backend-db"}, + } + + for _, tt := range tests { + t.Run(tt.componentName, func(t *testing.T) { + result := GetDatabaseHost(tt.componentName) + if result != tt.expected { + t.Errorf("Expected %q, got %q", tt.expected, result) + } + }) + } +} + +func TestExtractDatabaseTraits(t *testing.T) { + // Create a HeliosApp with database traits + dbProps := map[string]interface{}{ + "dbType": "postgres", + "dbName": "mydb", + "version": "16", + } + dbPropsJSON, _ := json.Marshal(dbProps) + + app := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "default", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "api-server", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "service", + Properties: &runtime.RawExtension{ + Raw: []byte(`{"port": 8080}`), + }, + }, + { + Type: "database", + Properties: &runtime.RawExtension{ + Raw: dbPropsJSON, + }, + }, + }, + }, + { + Name: "frontend", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "service", + Properties: &runtime.RawExtension{ + Raw: []byte(`{"port": 3000}`), + }, + }, + }, + }, + }, + }, + } + + dbTraits := ExtractDatabaseTraits(app) + + if len(dbTraits) != 1 { + t.Fatalf("Expected 1 database trait, got %d", len(dbTraits)) + } + + trait := dbTraits[0] + if trait.ComponentName != "api-server" { + t.Errorf("Expected component name %q, got %q", "api-server", trait.ComponentName) + } + + if trait.Properties.DBType != "postgres" { + t.Errorf("Expected dbType %q, got %q", "postgres", trait.Properties.DBType) + } + + if trait.Properties.DBName != "mydb" { + t.Errorf("Expected dbName %q, got %q", "mydb", trait.Properties.DBName) + } +} + +func TestReconcileDatabaseSecrets(t *testing.T) { + // Create a fake client + scheme := runtime.NewScheme() + _ = corev1.AddToScheme(scheme) + _ = appv1alpha1.AddToScheme(scheme) + + dbProps := map[string]interface{}{ + "dbType": "postgres", + "dbName": "mydb", + "version": "16", + } + dbPropsJSON, _ := json.Marshal(dbProps) + + app := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "default", + UID: "test-uid-123", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "api-server", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "database", + Properties: &runtime.RawExtension{ + Raw: dbPropsJSON, + }, + }, + }, + }, + }, + }, + } + + t.Run("CreateNewSecret", func(t *testing.T) { + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(app). + Build() + + r := &HeliosAppReconciler{ + Client: client, + Scheme: scheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseSecrets(ctx, app) + if err != nil { + t.Fatalf("reconcileDatabaseSecrets failed: %v", err) + } + + // Verify secret was created + secret := &corev1.Secret{} + err = client.Get(ctx, types.NamespacedName{ + Name: "api-server-db-secret", + Namespace: "default", + }, secret) + if err != nil { + t.Fatalf("Failed to get created secret: %v", err) + } + + // Verify secret contains required keys + if _, ok := secret.Data["DB_USER"]; !ok { + t.Error("Secret missing DB_USER key") + } + if _, ok := secret.Data["DB_PASS"]; !ok { + t.Error("Secret missing DB_PASS key") + } + if _, ok := secret.Data["DB_HOST"]; !ok { + t.Error("Secret missing DB_HOST key") + } + }) + + t.Run("ExistingSecretPreserved", func(t *testing.T) { + existingSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "api-server-db-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "DB_USER": []byte("existing-user"), + "DB_PASS": []byte("existing-pass"), + "DB_HOST": []byte("existing-host"), + }, + } + + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(app, existingSecret). + Build() + + r := &HeliosAppReconciler{ + Client: client, + Scheme: scheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseSecrets(ctx, app) + if err != nil { + t.Fatalf("reconcileDatabaseSecrets failed: %v", err) + } + + // Verify existing secret was not modified + secret := &corev1.Secret{} + err = client.Get(ctx, types.NamespacedName{ + Name: "api-server-db-secret", + Namespace: "default", + }, secret) + if err != nil { + t.Fatalf("Failed to get secret: %v", err) + } + + // The existing secret should preserve the original values + if string(secret.Data["DB_USER"]) != "existing-user" { + t.Errorf("Expected existing DB_USER to be preserved, got %s", string(secret.Data["DB_USER"])) + } + }) + + t.Run("NoDatabaseTraits", func(t *testing.T) { + appWithoutDB := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "no-db-app", + Namespace: "default", + UID: "test-uid-456", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "frontend", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "service", + Properties: &runtime.RawExtension{ + Raw: []byte(`{"port": 3000}`), + }, + }, + }, + }, + }, + }, + } + + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(appWithoutDB). + Build() + + r := &HeliosAppReconciler{ + Client: client, + Scheme: scheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseSecrets(ctx, appWithoutDB) + if err != nil { + t.Fatalf("reconcileDatabaseSecrets should not fail for app without database traits: %v", err) + } + + // Verify no secret was created + secretList := &corev1.SecretList{} + err = client.List(ctx, secretList) + if err != nil { + t.Fatalf("Failed to list secrets: %v", err) + } + + if len(secretList.Items) != 0 { + t.Errorf("Expected no secrets, got %d", len(secretList.Items)) + } + }) +} + +func TestGenerateBase64Token(t *testing.T) { + tests := []struct { + name string + byteLength int + }{ + {"default", 0}, + {"16 bytes", 16}, + {"32 bytes", 32}, + {"64 bytes", 64}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + token, err := GenerateBase64Token(tt.byteLength) + if err != nil { + t.Fatalf("GenerateBase64Token failed: %v", err) + } + + if token == "" { + t.Error("Token should not be empty") + } + + // Verify it's valid base64 + // Base64 length = ceil(byteLength * 8 / 6) * 6 / 8 * 4 / 3 + // Simplified: base64 length ≈ byteLength * 4/3, rounded up to multiple of 4 + expectedLen := tt.byteLength + if expectedLen <= 0 { + expectedLen = 32 + } + // Base64 encoding produces 4 characters for every 3 bytes + expectedBase64Len := ((expectedLen + 2) / 3) * 4 + if len(token) != expectedBase64Len { + t.Errorf("Expected base64 length %d, got %d", expectedBase64Len, len(token)) + } + }) + } +} diff --git a/apps/operator/internal/controller/heliosapp_controller.go b/apps/operator/internal/controller/heliosapp_controller.go index 5c2c566..ba69c7a 100644 --- a/apps/operator/internal/controller/heliosapp_controller.go +++ b/apps/operator/internal/controller/heliosapp_controller.go @@ -59,7 +59,7 @@ type HeliosAppReconciler struct { // +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/finalizers,verbs=update // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete // Reconcile handles the reconciliation loop for HeliosApp // Controller does NOT iterate components/traits - all orchestration is in CUE @@ -116,6 +116,18 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } + // ------------------------------------------------------------------ + // PHASE 0.5: Database Credential Secrets + // Generate and store secure credentials for components with database traits. + // Secrets are created BEFORE GitOps sync to ensure credentials exist + // when the application is deployed. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseSecrets(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile database secrets") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret creation failed: %v", err)) + return ctrl.Result{}, err + } + // ------------------------------------------------------------------ // PHASE 0.6: Trigger Initial PipelineRun (if not already done) // ------------------------------------------------------------------ From 6ba077f087f62ba9a9603676c62b78281b41203e Mon Sep 17 00:00:00 2001 From: NgocAnhDo26 Date: Wed, 4 Mar 2026 23:34:04 +0700 Subject: [PATCH 03/19] feat(scaffolder): add DatabasePicker extension for database selection --- apps/portal/packages/app/src/App.tsx | 9 ++++++++- .../DatabasePickerExtension/DatabasePicker.tsx | 14 ++++++++++++++ .../DatabasePickerExtension/extension.ts | 13 +++++++++++++ .../scaffolder/DatabasePickerExtension/index.ts | 2 ++ apps/portal/packages/app/src/scaffolder/index.ts | 1 + 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx create mode 100644 apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts create mode 100644 apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts create mode 100644 apps/portal/packages/app/src/scaffolder/index.ts diff --git a/apps/portal/packages/app/src/App.tsx b/apps/portal/packages/app/src/App.tsx index e509612..0f1087b 100644 --- a/apps/portal/packages/app/src/App.tsx +++ b/apps/portal/packages/app/src/App.tsx @@ -38,6 +38,7 @@ import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; import { NotificationsPage } from '@backstage/plugin-notifications'; import { SignalsDisplay } from '@backstage/plugin-signals'; +import { DatabasePickerExtension } from './scaffolder'; const app = createApp({ apis, @@ -74,6 +75,8 @@ const app = createApp({ }, }); +const scaffolderFieldExtensions = [DatabasePickerExtension]; + const routes = ( } /> @@ -93,7 +96,11 @@ const routes = ( - } /> + }> + {scaffolderFieldExtensions.map(Extension => ( + + ))} + } /> ) => { + // Todo: Implementation + return <>; +}; diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts new file mode 100644 index 0000000..8f1ca0d --- /dev/null +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -0,0 +1,13 @@ +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; +import { DatabasePicker } from './DatabasePicker'; + +export const DatabasePickerExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: 'DatabasePicker', + component: DatabasePicker, + validation: (value, validation, context) => { + // Todo: Implement validation logic + }, + }), +); diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts new file mode 100644 index 0000000..6634724 --- /dev/null +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts @@ -0,0 +1,2 @@ +export * from './DatabasePicker'; +export * from './extension'; \ No newline at end of file diff --git a/apps/portal/packages/app/src/scaffolder/index.ts b/apps/portal/packages/app/src/scaffolder/index.ts new file mode 100644 index 0000000..03b3db3 --- /dev/null +++ b/apps/portal/packages/app/src/scaffolder/index.ts @@ -0,0 +1 @@ +export * from './DatabasePickerExtension/index'; \ No newline at end of file From 8598e31067e26874c15568244fe3056b544bcdaa Mon Sep 17 00:00:00 2001 From: NgocAnhDo26 Date: Thu, 5 Mar 2026 22:22:40 +0700 Subject: [PATCH 04/19] feat(setup): add Taskfile and environment configuration for local development - Introduced a Taskfile to automate setup and development tasks for the Helios Platform. - Added a .env.example file to provide a template for environment variables. - Updated .gitignore to exclude .env files while keeping .env.example. - Enhanced documentation in APP_STARTUP_GUIDE.md and SETUP.md for quick start and manual setup instructions. - Implemented prerequisite check scripts for both Windows and Unix-like systems. --- .env.example | 40 +++++++ .gitignore | 4 + Taskfile.yml | 243 ++++++++++++++++++++++++++++++++++++++ docs/APP_STARTUP_GUIDE.md | 117 ++++++++++++++++-- docs/SETUP.md | 24 +++- scripts/check-prereqs.bat | 155 ++++++++++++++++++++++++ scripts/check-prereqs.sh | 160 +++++++++++++++++++++++++ 7 files changed, 731 insertions(+), 12 deletions(-) create mode 100644 .env.example create mode 100644 Taskfile.yml create mode 100644 scripts/check-prereqs.bat create mode 100755 scripts/check-prereqs.sh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..da0bee1 --- /dev/null +++ b/.env.example @@ -0,0 +1,40 @@ +# ============================================================================= +# Helios Platform - Environment Variables +# ============================================================================= +# Copy this file to .env and fill in your values: +# cp .env.example .env +# +# This single .env at the repo root is the source of truth for all credentials. +# The Taskfile distributes these to operator and portal contexts automatically. +# ============================================================================= + +# ----------------------------------------------------------------------------- +# GitHub Integration (required) +# ----------------------------------------------------------------------------- +# Personal Access Token with 'repo' and 'workflow' scopes +GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +GITHUB_USER=your-github-username +GITHUB_ORG=helios-platform-team + +# ----------------------------------------------------------------------------- +# GitHub OAuth App (required for portal login) +# ----------------------------------------------------------------------------- +# Create an OAuth App at https://github.com/settings/developers +# Homepage URL: http://localhost:3000 +# Callback URL: http://localhost:7007/api/auth/github/handler/frame +AUTH_GITHUB_CLIENT_ID= +AUTH_GITHUB_CLIENT_SECRET= + +# ----------------------------------------------------------------------------- +# Docker Registry (required for Tekton pipelines) +# ----------------------------------------------------------------------------- +DOCKER_SERVER=https://index.docker.io/v1/ +DOCKER_USERNAME= +DOCKER_PASSWORD= +DOCKER_EMAIL= + +# ----------------------------------------------------------------------------- +# Git Author (optional, used by operator for GitOps commits) +# ----------------------------------------------------------------------------- +GIT_AUTHOR_NAME=Helios Operator +GIT_AUTHOR_EMAIL=operator@helios.io diff --git a/.gitignore b/.gitignore index feb5847..4c97ca2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ # SQLite *.sqlite apps/portal/packages/backend/*.sqlite + +# Environment secrets +.env +!.env.example diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..0cfd1e5 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,243 @@ +# ============================================================================= +# Helios Platform - Root Taskfile +# ============================================================================= +# Usage: +# task check Verify all prerequisites are installed +# task setup Bootstrap the full local environment (cluster + deps) +# task dev Run operator + portal concurrently +# task test Run all tests +# task clean Tear down the k3d cluster +# +# First-time setup: +# cp .env.example .env # fill in your credentials +# task setup # ~5-10 minutes +# task dev # opens operator + portal +# +# Cross-platform: +# Linux/macOS: works natively +# Windows: requires Git Bash or WSL (for bash-based sub-scripts) +# ============================================================================= +version: '3' + +dotenv: ['.env'] + +vars: + CLUSTER_NAME: helios-dev + ARGOCD_PORT: '8080' + KUBECTL_PROXY_PORT: '8001' + +set: [errexit, pipefail] + +# ============================================================================= +# Prerequisite Checking +# ============================================================================= +tasks: + check: + desc: Verify all development prerequisites are installed + cmds: + - bash scripts/check-prereqs.sh + + check:env: + desc: Verify prerequisites and .env configuration + cmds: + - bash scripts/check-prereqs.sh --env + + # =========================================================================== + # Setup Tasks + # =========================================================================== + setup: + desc: Bootstrap the full local development environment + deps: [check:env] + cmds: + - task: setup:cluster + - task: setup:tekton + - task: setup:argocd + - task: setup:crds + - task: setup:tekton-resources + - task: setup:tekton-rbac + - task: setup:credentials + - task: setup:portal-deps + - echo "" + - echo "=============================================" + - echo " Helios local environment is ready!" + - echo " Run 'task dev' to start developing." + - echo "=============================================" + + setup:cluster: + desc: Create a k3d cluster (idempotent) + status: + - k3d cluster list 2>/dev/null | grep -q '{{.CLUSTER_NAME}}' + cmds: + - echo "Creating k3d cluster '{{.CLUSTER_NAME}}'..." + - k3d cluster create {{.CLUSTER_NAME}} --agents 1 --wait + - kubectl cluster-info + + setup:tekton: + desc: Install Tekton Pipeline, Triggers, and Interceptors + cmds: + - echo "Installing Tekton Pipeline..." + - kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml + - echo "Installing Tekton Triggers..." + - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml + - echo "Installing Tekton Interceptors..." + - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml + - echo "Waiting for Tekton Pipelines controller..." + - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=120s + - echo "Patching Tekton feature flags..." + - >- + kubectl patch configmap feature-flags -n tekton-pipelines + -p '{"data":{"disable-affinity-assistant":"true","coschedule":"disabled"}}' + - kubectl rollout restart deployment tekton-pipelines-controller -n tekton-pipelines + - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=120s + + setup:argocd: + desc: Install ArgoCD + cmds: + - kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f - + - echo "Installing ArgoCD..." + - kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml + - echo "Waiting for ArgoCD server..." + - kubectl rollout status deployment/argocd-server -n argocd --timeout=180s + + setup:crds: + desc: Install Helios CRDs into the cluster + dir: apps/operator + cmds: + - make install + + setup:tekton-resources: + desc: Export CUE definitions and apply Tekton tasks + cmds: + - echo "Exporting Tekton tasks from CUE..." + - cue export ./cue/definitions/tekton/tasks/*.cue --out yaml > apps/operator/tekton/tasks.yaml + - echo "Applying Tekton resources..." + - kubectl apply -f apps/operator/tekton/tasks.yaml + + setup:tekton-rbac: + desc: Grant Tekton Triggers SA the permissions to create PipelineRuns + cmds: + - >- + kubectl create clusterrolebinding tekton-triggers-sa-admin + --clusterrole=cluster-admin + --serviceaccount=default:tekton-triggers-sa + --dry-run=client -o yaml | kubectl apply -f - + + setup:credentials: + desc: Create Docker registry secret and link to pipeline SA + preconditions: + - sh: '[ -n "$DOCKER_USERNAME" ]' + msg: "DOCKER_USERNAME is not set in .env" + - sh: '[ -n "$DOCKER_PASSWORD" ]' + msg: "DOCKER_PASSWORD is not set in .env" + cmds: + - >- + kubectl create secret docker-registry docker-credentials + --docker-server=${DOCKER_SERVER:-https://index.docker.io/v1/} + --docker-username=$DOCKER_USERNAME + --docker-password=$DOCKER_PASSWORD + --docker-email=${DOCKER_EMAIL:-dev@helios.io} + --dry-run=client -o yaml | kubectl apply -f - + - >- + kubectl patch sa pipeline + -p '{"secrets": [{"name": "docker-credentials"}]}' + 2>/dev/null || true + + setup:portal-deps: + desc: Install Backstage portal dependencies + dir: apps/portal + cmds: + - yarn install + + # =========================================================================== + # Development Tasks + # =========================================================================== + dev: + desc: Run operator and portal concurrently + deps: [dev:operator, dev:portal] + + dev:operator: + desc: Run the Helios operator locally + dir: apps/operator + env: + HELIOS_CUE_PATH: '{{.ROOT_DIR}}/cue' + GITHUB_TOKEN: $GITHUB_TOKEN + GITHUB_USER: $GITHUB_USER + cmds: + - make run + + dev:portal: + desc: Run the Backstage portal with ArgoCD + kubectl proxy + dir: apps/portal + env: + AUTH_GITHUB_CLIENT_ID: $AUTH_GITHUB_CLIENT_ID + AUTH_GITHUB_CLIENT_SECRET: $AUTH_GITHUB_CLIENT_SECRET + GITHUB_TOKEN: $GITHUB_TOKEN + GITHUB_ORG: $GITHUB_ORG + cmds: + - task: dev:portal:proxy + - task: dev:portal:start + + dev:portal:proxy: + desc: Start kubectl proxy and ArgoCD port-forward + internal: true + cmds: + - echo "Starting ArgoCD port-forward (localhost:{{.ARGOCD_PORT}})..." + - kubectl port-forward -n argocd svc/argocd-server {{.ARGOCD_PORT}}:443 &>/dev/null & + - sleep 2 + - echo "Starting kubectl proxy (localhost:{{.KUBECTL_PROXY_PORT}})..." + - kubectl proxy --port={{.KUBECTL_PROXY_PORT}} &>/dev/null & + - sleep 1 + + dev:portal:start: + desc: Generate ArgoCD token and start Backstage + internal: true + dir: apps/portal + cmds: + - | + ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) + TOKEN_JSON=$(curl -k -s -X POST \ + -H "Content-Type: application/json" \ + -d "{\"username\":\"admin\",\"password\":\"$ARGOCD_PASS\"}" \ + https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session) + export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | sed 's/.*"token":"\([^"]*\)".*/\1/') + if [ -z "$ARGOCD_AUTH_TOKEN" ] || [ "${#ARGOCD_AUTH_TOKEN}" -lt 20 ]; then + echo "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." + else + echo "ArgoCD token generated." + fi + yarn start + + # =========================================================================== + # Testing Tasks + # =========================================================================== + test: + desc: Run all tests + cmds: + - task: test:operator + - task: test:portal + + test:operator: + desc: Run operator unit tests + dir: apps/operator + cmds: + - make test + + test:portal: + desc: Run portal tests + dir: apps/portal + cmds: + - yarn test + + # =========================================================================== + # Teardown Tasks + # =========================================================================== + clean: + desc: Delete the k3d cluster and clean up + cmds: + - task: clean:cluster + + clean:cluster: + desc: Delete the k3d cluster + cmds: + - k3d cluster delete {{.CLUSTER_NAME}} + - echo "Cluster '{{.CLUSTER_NAME}}' deleted." diff --git a/docs/APP_STARTUP_GUIDE.md b/docs/APP_STARTUP_GUIDE.md index 6be7477..c0fa5c8 100644 --- a/docs/APP_STARTUP_GUIDE.md +++ b/docs/APP_STARTUP_GUIDE.md @@ -4,11 +4,100 @@ --- -## Prerequisites +## Quick Start (Recommended) -### Environment Variables +The fastest way to get up and running uses [Task](https://taskfile.dev/) to automate all setup steps. -Create a `.env` file in `/apps/portal` with the following content (replace with your values): +### 1. Install Task + +```bash +# Linux +sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin + +# macOS (Homebrew) +brew install go-task + +# Windows (Scoop) +scoop install task + +# Windows (Chocolatey) +choco install go-task +``` + +> **Windows users:** Task requires a POSIX shell for its commands. Install [Git for Windows](https://git-scm.com/download/win) (includes Git Bash) or use [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install). You can also run `scripts\check-prereqs.bat` directly from `cmd.exe` to verify tools before using Task. + +### 2. Configure credentials + +```bash +cp .env.example .env +# Edit .env and fill in your GitHub PAT, OAuth app, and Docker Hub credentials +``` + +### 3. Verify prerequisites + +```bash +task check +``` + +### 4. Bootstrap the environment + +This creates a k3d cluster (lightweight k3s-in-Docker), installs Tekton, ArgoCD, CRDs, and all dependencies (~5-10 min): + +```bash +task setup +``` + +### 5. Start developing + +Runs the Go operator and Backstage portal concurrently: + +```bash +task dev +``` + +The portal will be available at http://localhost:3000 and the backend API at http://localhost:7007. + +### Other useful commands + +| Command | Description | +|---------|-------------| +| `task check` | Verify all prerequisites are installed | +| `task setup` | Bootstrap the full local environment | +| `task dev` | Run operator + portal concurrently | +| `task dev:operator` | Run only the operator | +| `task dev:portal` | Run only the portal | +| `task test` | Run all tests | +| `task clean` | Delete the k3d cluster | + +--- + +## Manual Setup (Step-by-Step Reference) + +The sections below describe each step that `task setup` performs automatically. Use these for troubleshooting or if you prefer manual control. + +### Prerequisites + +#### Required Tools + +Ensure you have the following installed (run `task check` to verify): + +- **Go** >= 1.24 ([install](https://go.dev/dl/)) +- **Docker** ([install](https://docs.docker.com/get-docker/)) +- **kubectl** ([install](https://kubernetes.io/docs/tasks/tools/)) +- **k3d** ([install](https://k3d.io/) or `curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash`) +- **CUE** (`go install cuelang.org/go/cmd/cue@latest`) +- **Node.js** >= 22 ([install](https://nodejs.org/) or `nvm install 22`) +- **Yarn** 4 (`corepack enable && corepack prepare yarn@4 --activate`) + +#### Environment Variables + +Create a `.env` file at the **repository root** with all credentials (see `.env.example`): + +```bash +cp .env.example .env +``` + +The Taskfile distributes these to the operator and portal automatically. If running manually, you also need a `.env` in `apps/portal/` with at minimum: ```env AUTH_GITHUB_CLIENT_ID= @@ -19,10 +108,10 @@ GITHUB_TOKEN= --- -## Step 1: Create a Fresh Kind Cluster +## Step 1: Create a Fresh k3d Cluster ```bash -kind create cluster --name helios-dev +k3d cluster create helios-dev --agents 1 --wait ``` --- @@ -56,12 +145,12 @@ kubectl rollout restart deployment tekton-pipelines-controller -n tekton-pipelin ### 2.4 Apply Compatibility Patch (Kubernetes < 1.28 Only) > [!WARNING] -> **Only apply this patch if your Kind node version is below 1.28.** +> **Only apply this patch if your k3s node version is below 1.28.** > -> Check the version in the terminal output after running `kind create cluster`. Newer Tekton versions (v0.50+) officially require Kubernetes v1.28+. +> k3d typically ships with a recent k3s version (1.28+), so this patch is rarely needed. Check with `kubectl version`. ```bash -# PATCH: Fix incompatibility with Kubernetes 1.27+ (Prevent CrashLoopBackOff) +# PATCH: Fix incompatibility with older Kubernetes (Prevent CrashLoopBackOff) kubectl set env deployment/tekton-pipelines-controller -n tekton-pipelines KUBERNETES_MIN_VERSION=1.20.0 kubectl set env deployment/tekton-pipelines-webhook -n tekton-pipelines KUBERNETES_MIN_VERSION=1.20.0 ``` @@ -274,9 +363,19 @@ kubectl get pods -n default | grep "advanced-nodejs-app-v14-run" ## Quick Reference +### Automated (Taskfile) + +| Command | Description | +|---------|-------------| +| `task setup` | Steps 1-8 in one command | +| `task dev` | Run operator + portal | +| `task clean` | Tear down cluster | + +### Manual + | Step | Description | Command | |------|-------------|---------| -| 1 | Create cluster | `kind create cluster --name helios-dev` | +| 1 | Create cluster | `k3d cluster create helios-dev --agents 1 --wait` | | 2 | Install Tekton | `kubectl apply -f https://storage.googleapis.com/tekton-releases/...` | | 3 | Install ArgoCD | `kubectl apply -n argocd -f https://...argo-cd/.../install.yaml` | | 4 | Run operator | `make -C apps/operator run` | diff --git a/docs/SETUP.md b/docs/SETUP.md index 2af996e..68b5c4b 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -2,13 +2,18 @@ This guide explains how to install necessary development tools for the Helios Platform. +> **Tip:** For a fully automated setup, see the [Quick Start](APP_STARTUP_GUIDE.md#quick-start-recommended) in the App Startup Guide. Run `task check` to verify your tooling at any time. + ## 1. Prerequisites Ensure you have the following installed: +- **Task**: [taskfile.dev](https://taskfile.dev/) (the project task runner) - **Go**: v1.24.0+ - **Docker**: 17.03+ - **kubectl**: v1.11.3+ +- **Node.js**: v22+ (for Backstage portal) +- **Yarn**: v4+ (`corepack enable && corepack prepare yarn@4 --activate`) ## 2. Install CLI Tools @@ -18,8 +23,15 @@ Install the essential CLIs for template management and local cluster testing: # Install CUE go install cuelang.org/go/cmd/cue@latest -# Install Kind -go install sigs.k8s.io/kind@latest +# Install k3d (lightweight k3s-in-Docker) +# Linux / macOS +curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash + +# Windows (Scoop) +scoop install k3d + +# Windows (Chocolatey) +choco install k3d ``` ## 3. Configure PATH @@ -27,10 +39,16 @@ go install sigs.k8s.io/kind@latest Ensure the Go binary directory is in your shell's `PATH` to run the tools globally: ```bash -# Add to ~/.zshrc or ~/.bashrc +# Linux / macOS: add to ~/.zshrc or ~/.bashrc export PATH="$PATH:$(go env GOPATH)/bin" ``` +On **Windows**, add `%GOPATH%\bin` to your system PATH via System Environment Variables, or run: + +```powershell +[System.Environment]::SetEnvironmentVariable("PATH", $env:PATH + ";" + (go env GOPATH) + "\bin", "User") +``` + ## 4. Install Operator Development Tools Download project-specific versions of Kustomize, Operator SDK, and Test Binaries: diff --git a/scripts/check-prereqs.bat b/scripts/check-prereqs.bat new file mode 100644 index 0000000..90981b1 --- /dev/null +++ b/scripts/check-prereqs.bat @@ -0,0 +1,155 @@ +@echo off +REM ============================================================================= +REM Helios Platform - Prerequisite Checker (Windows) +REM ============================================================================= +REM Verifies that all required tools are installed and properly configured. +REM Can be run standalone or via `task check` (requires Git Bash or WSL). +REM +REM Usage: scripts\check-prereqs.bat [--env] +REM --env Also validate that .env exists and required variables are set +REM ============================================================================= +setlocal enabledelayedexpansion + +set "ERRORS=0" +set "WARNINGS=0" +set "CHECK_ENV=0" + +REM Parse arguments +for %%a in (%*) do ( + if "%%a"=="--env" set "CHECK_ENV=1" +) + +echo. +echo ============================== +echo Helios Platform - Prereqs +echo ============================== +echo. + +REM --------------------------------------------------------------------------- +REM Core Tools +REM --------------------------------------------------------------------------- +echo [Core Tools] + +call :check_tool "go" "go version" +call :check_tool "docker" "docker --version" +call :check_tool "kubectl" "kubectl version --client --short 2>nul" +call :check_tool "k3d" "k3d version" +call :check_tool "cue" "cue version" + +echo. +echo [Node.js / Frontend] + +call :check_tool "node" "node --version" +call :check_tool "yarn" "yarn --version" + +echo. +echo [Runtime Checks] + +docker info >nul 2>&1 +if %errorlevel% equ 0 ( + echo [OK] Docker daemon is running +) else ( + echo [FAIL] Docker daemon is not running. Start Docker Desktop first. + set /a ERRORS+=1 +) + +if exist "%USERPROFILE%\.kube\config" ( + echo [OK] Kubeconfig found +) else if defined KUBECONFIG ( + echo [OK] Kubeconfig found via KUBECONFIG env var +) else ( + echo [WARN] No kubeconfig found. One will be created by 'task setup:cluster'. + set /a WARNINGS+=1 +) + +REM --------------------------------------------------------------------------- +REM Optional: .env validation +REM --------------------------------------------------------------------------- +if %CHECK_ENV% equ 1 ( + echo. + echo [Environment Variables] + + REM Resolve repo root relative to this script + set "SCRIPT_DIR=%~dp0" + set "ENV_FILE=!SCRIPT_DIR!..\.env" + + if not exist "!ENV_FILE!" ( + echo [FAIL] .env file not found at repo root. Run: copy .env.example .env + set /a ERRORS+=1 + ) else ( + echo [OK] .env file exists + + REM Source .env by parsing key=value lines + for /f "usebackq tokens=1,* delims==" %%i in ("!ENV_FILE!") do ( + set "line=%%i" + REM Skip comment lines + if not "!line:~0,1!"=="#" ( + if not "%%j"=="" set "%%i=%%j" + ) + ) + + REM Check required variables + call :check_env_var "GITHUB_TOKEN" + call :check_env_var "GITHUB_USER" + call :check_env_var "AUTH_GITHUB_CLIENT_ID" + call :check_env_var "AUTH_GITHUB_CLIENT_SECRET" + ) +) + +REM --------------------------------------------------------------------------- +REM Summary +REM --------------------------------------------------------------------------- +echo. +echo ============================== +if %ERRORS% gtr 0 ( + echo %ERRORS% error^(s^) and %WARNINGS% warning^(s^). + echo Fix the errors above before proceeding. + exit /b 1 +) else if %WARNINGS% gtr 0 ( + echo All required tools found. %WARNINGS% warning^(s^) to review. + exit /b 0 +) else ( + echo All checks passed! + exit /b 0 +) + +REM --------------------------------------------------------------------------- +REM Subroutines +REM --------------------------------------------------------------------------- + +:check_tool +REM %~1 = tool name, %~2 = version command +where %~1 >nul 2>&1 +if %errorlevel% equ 0 ( + for /f "delims=" %%v in ('%~2 2^>^&1') do ( + echo [OK] %~1 - %%v + goto :eof + ) + echo [OK] %~1 found +) else ( + echo [FAIL] %~1 not found. Please install it. + set /a ERRORS+=1 +) +goto :eof + +:check_env_var +REM %~1 = variable name +if not defined %~1 ( + echo [FAIL] %~1 is not set in .env + set /a ERRORS+=1 + goto :eof +) +set "val=!%~1!" +if "!val!"=="" ( + echo [FAIL] %~1 is empty in .env + set /a ERRORS+=1 +) else if "!val:~0,8!"=="ghp_xxxx" ( + echo [FAIL] %~1 still has placeholder value in .env + set /a ERRORS+=1 +) else if "!val:~0,5!"=="your-" ( + echo [FAIL] %~1 still has placeholder value in .env + set /a ERRORS+=1 +) else ( + echo [OK] %~1 is configured +) +goto :eof diff --git a/scripts/check-prereqs.sh b/scripts/check-prereqs.sh new file mode 100755 index 0000000..f23f738 --- /dev/null +++ b/scripts/check-prereqs.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +# ============================================================================= +# Helios Platform - Prerequisite Checker +# ============================================================================= +# Verifies that all required tools are installed and properly configured. +# Can be run standalone or via `task check`. +# +# Usage: ./scripts/check-prereqs.sh [--env] +# --env Also validate that .env exists and required variables are set +# ============================================================================= +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BOLD='\033[1m' +NC='\033[0m' + +ERRORS=0 +WARNINGS=0 + +pass() { echo -e " ${GREEN}[OK]${NC} $1"; } +warn() { echo -e " ${YELLOW}[WARN]${NC} $1"; WARNINGS=$((WARNINGS + 1)); } +fail() { echo -e " ${RED}[FAIL]${NC} $1"; ERRORS=$((ERRORS + 1)); } + +# --------------------------------------------------------------------------- +# Version comparison: returns 0 if $1 >= $2 +# --------------------------------------------------------------------------- +version_gte() { + printf '%s\n%s' "$2" "$1" | sort -t. -k1,1n -k2,2n -k3,3n -C +} + +# --------------------------------------------------------------------------- +# Check a single binary +# $1 = binary name +# $2 = minimum version (empty string = any) +# $3 = version extraction command +# $4 = install hint +# --------------------------------------------------------------------------- +check_tool() { + local name="$1" min_ver="$2" ver_cmd="$3" hint="$4" + + if ! command -v "$name" &>/dev/null; then + fail "$name not found. Install: $hint" + return + fi + + if [[ -n "$min_ver" ]]; then + local actual_ver + actual_ver=$(eval "$ver_cmd" 2>/dev/null || echo "unknown") + if [[ "$actual_ver" == "unknown" ]]; then + warn "$name installed but could not determine version" + elif version_gte "$actual_ver" "$min_ver"; then + pass "$name $actual_ver (>= $min_ver)" + else + fail "$name $actual_ver is below minimum $min_ver. $hint" + fi + else + pass "$name $(command -v "$name")" + fi +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +echo -e "\n${BOLD}Helios Platform - Prerequisite Check${NC}\n" +echo "----------------------------------------------" + +echo -e "\n${BOLD}Core Tools${NC}" +check_tool "go" "1.24" \ + "go version | grep -oP 'go\K[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1" \ + "https://go.dev/dl/" + +check_tool "docker" "" \ + "docker --version | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1" \ + "https://docs.docker.com/get-docker/" + +check_tool "kubectl" "" \ + "kubectl version --client -o json 2>/dev/null | grep -oP '\"gitVersion\":\\s*\"v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1" \ + "https://kubernetes.io/docs/tasks/tools/" + +check_tool "k3d" "" \ + "k3d version | head -1 | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+'" \ + "https://k3d.io/ or: curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash" + +check_tool "cue" "" \ + "cue version | head -1 | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+'" \ + "go install cuelang.org/go/cmd/cue@latest" + +echo -e "\n${BOLD}Node.js / Frontend${NC}" +check_tool "node" "22.0.0" \ + "node --version | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+'" \ + "https://nodejs.org/ or use nvm: nvm install 22" + +check_tool "yarn" "" \ + "yarn --version 2>/dev/null | head -1" \ + "corepack enable && corepack prepare yarn@4 --activate" + +echo -e "\n${BOLD}Runtime Checks${NC}" + +if docker info &>/dev/null; then + pass "Docker daemon is running" +else + fail "Docker daemon is not running. Start Docker first." +fi + +if [[ -f "$HOME/.kube/config" ]] || [[ -n "${KUBECONFIG:-}" ]]; then + pass "Kubeconfig found" +else + warn "No kubeconfig found (~/.kube/config). One will be created when you run 'task setup:cluster'." +fi + +# --------------------------------------------------------------------------- +# Optional: .env validation (--env flag) +# --------------------------------------------------------------------------- +CHECK_ENV=false +for arg in "$@"; do + [[ "$arg" == "--env" ]] && CHECK_ENV=true +done + +if $CHECK_ENV; then + echo -e "\n${BOLD}Environment Variables (.env)${NC}" + + REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + ENV_FILE="${REPO_ROOT}/.env" + + if [[ ! -f "$ENV_FILE" ]]; then + fail ".env file not found at repo root. Run: cp .env.example .env" + else + pass ".env file exists" + + REQUIRED_VARS=(GITHUB_TOKEN GITHUB_USER AUTH_GITHUB_CLIENT_ID AUTH_GITHUB_CLIENT_SECRET) + set -a; source "$ENV_FILE"; set +a + + for var in "${REQUIRED_VARS[@]}"; do + val="${!var:-}" + if [[ -z "$val" || "$val" == ghp_xxxx* || "$val" == "your-"* ]]; then + fail "$var is not set (or still has placeholder value) in .env" + else + pass "$var is configured" + fi + done + fi +fi + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +echo "" +echo "----------------------------------------------" +if [[ $ERRORS -gt 0 ]]; then + echo -e "${RED}${BOLD}$ERRORS error(s)${NC} and ${YELLOW}$WARNINGS warning(s)${NC}. Fix the errors above before proceeding." + exit 1 +elif [[ $WARNINGS -gt 0 ]]; then + echo -e "${GREEN}${BOLD}All required tools found.${NC} ${YELLOW}$WARNINGS warning(s)${NC} to review." + exit 0 +else + echo -e "${GREEN}${BOLD}All checks passed!${NC}" + exit 0 +fi From 0c1561d55e7e8cfc29b951f19a8fcca6413634d3 Mon Sep 17 00:00:00 2001 From: PhamHoangKha1403 Date: Sat, 7 Mar 2026 18:38:40 +0700 Subject: [PATCH 05/19] feat: implement postgres provisioning logic for database trait (#34) --- .../internal/controller/database_resources.go | 321 +++++++++++++++ .../controller/database_resources_test.go | 374 ++++++++++++++++++ .../controller/heliosapp_controller.go | 13 + .../internal/controller/suite_test.go | 15 + cue/definitions/traits/database.cue | 22 +- 5 files changed, 739 insertions(+), 6 deletions(-) diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index e314004..64cc099 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -15,10 +15,13 @@ import ( "math/big" "strings" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -42,6 +45,21 @@ const ( // DatabaseTraitType is the trait type identifier for database traits DatabaseTraitType = "database" + + // DefaultPostgresVersion is the default Postgres image tag when not specified. + DefaultPostgresVersion = "16" + + // DefaultPostgresPort is the default port for Postgres. + DefaultPostgresPort = 5432 + + // DefaultDatabaseStorage is the default PVC size for database volumes. + DefaultDatabaseStorage = "1Gi" + + // PostgresDataPath is the mount path for Postgres data directory. + PostgresDataPath = "/var/lib/postgresql/data" + + // PostgresDataSubPath is the subPath within the PVC to avoid lost+found issues. + PostgresDataSubPath = "pgdata" ) // DatabaseCredentials holds generated database credentials @@ -278,6 +296,309 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app return nil } +// reconcileDatabaseInstance provisions database StatefulSets and headless +// Services for components with database traits. This runs AFTER +// reconcileDatabaseSecrets so that the credential Secret already exists +// when the StatefulSet is created. +func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping instance provisioning") + return nil + } + + for _, dbTrait := range dbTraits { + // Only provision postgres instances for now + if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { + log.V(1).Info("Skipping non-postgres database type", + "component", dbTrait.ComponentName, + "dbType", dbTrait.Properties.DBType) + continue + } + + dbHost := GetDatabaseHost(dbTrait.ComponentName) + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + + // Determine effective database name + effectiveDBName := dbTrait.Properties.DBName + if effectiveDBName == "" { + effectiveDBName = fmt.Sprintf("%s-db", dbTrait.ComponentName) + } + + // Determine version — CUE schema requires version!, but we + // guard here defensively in case of direct API usage. + version := dbTrait.Properties.Version + if version == "" { + version = DefaultPostgresVersion + } + + // Determine port + port := dbTrait.Properties.Port + if port <= 0 { + port = DefaultPostgresPort + } + + // Determine storage + storage := dbTrait.Properties.Storage + if storage == "" { + storage = DefaultDatabaseStorage + } + + // --- StatefulSet --- + sts := GenerateDatabaseStatefulSet( + app.Namespace, dbHost, secretName, effectiveDBName, version, storage, int32(port), + ) + + if err := ctrl.SetControllerReference(app, sts, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database StatefulSet", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to set owner reference for StatefulSet %s: %w", dbHost, err) + } + + existingSts := &appsv1.StatefulSet{} + err := r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) + if err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to check for StatefulSet %s: %w", dbHost, err) + } + + log.Info("Creating database StatefulSet", + "component", dbTrait.ComponentName, + "statefulset", dbHost, + "image", fmt.Sprintf("postgres:%s", version)) + + if err := r.Create(ctx, sts); err != nil { + if errors.IsAlreadyExists(err) { + log.Info("Database StatefulSet was created concurrently, skipping", + "component", dbTrait.ComponentName) + } else { + return fmt.Errorf("failed to create StatefulSet %s: %w", dbHost, err) + } + } + } else { + log.Info("Database StatefulSet already exists, skipping", + "component", dbTrait.ComponentName, + "statefulset", dbHost) + } + + // --- Headless Service --- + svc := GenerateDatabaseService(app.Namespace, dbHost, int32(port)) + + if err := ctrl.SetControllerReference(app, svc, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database Service", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to set owner reference for Service %s: %w", dbHost, err) + } + + existingSvc := &corev1.Service{} + err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSvc) + if err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to check for Service %s: %w", dbHost, err) + } + + log.Info("Creating database headless Service", + "component", dbTrait.ComponentName, + "service", dbHost) + + if err := r.Create(ctx, svc); err != nil { + if errors.IsAlreadyExists(err) { + log.Info("Database Service was created concurrently, skipping", + "component", dbTrait.ComponentName) + } else { + return fmt.Errorf("failed to create Service %s: %w", dbHost, err) + } + } + } else { + log.Info("Database Service already exists, skipping", + "component", dbTrait.ComponentName, + "service", dbHost) + } + + log.Info("Successfully reconciled database instance", + "component", dbTrait.ComponentName, + "statefulset", dbHost, + "dbName", effectiveDBName) + } + + return nil +} + +// GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. +// The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and +// uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. +func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) *appsv1.StatefulSet { + replicas := int32(1) + labels := map[string]string{ + "app": name, + "helios.io/managed-by": "operator", + "helios.io/trait": "database", + "helios.io/db-type": "postgres", + } + + return &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: appsv1.StatefulSetSpec{ + ServiceName: name, + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": name}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "postgres", + Image: fmt.Sprintf("postgres:%s", version), + Ports: []corev1.ContainerPort{ + { + ContainerPort: port, + Name: "postgres", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "POSTGRES_DB", + Value: dbName, + }, + { + Name: "POSTGRES_USER", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "DB_USER", + }, + }, + }, + { + Name: "POSTGRES_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "DB_PASS", + }, + }, + }, + { + // PGDATA tells Postgres where to store cluster data. + // Must match volumeMount + subPath to avoid lost+found conflicts. + Name: "PGDATA", + Value: PostgresDataPath + "/" + PostgresDataSubPath, + }, + { + // Ensure consistent UTF-8 encoding for all databases. + Name: "POSTGRES_INITDB_ARGS", + Value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + MountPath: PostgresDataPath, + SubPath: PostgresDataSubPath, + }, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resourceMustParse("100m"), + corev1.ResourceMemory: resourceMustParse("256Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resourceMustParse("500m"), + corev1.ResourceMemory: resourceMustParse("512Mi"), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName}, + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 10, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName}, + }, + }, + InitialDelaySeconds: 30, + PeriodSeconds: 10, + }, + }, + }, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "data", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resourceMustParse(storage), + }, + }, + }, + }, + }, + }, + } +} + +// GenerateDatabaseService creates a headless Service for a database StatefulSet. +// The headless Service (clusterIP: None) provides stable DNS resolution +// so resolves to the database pod. +func GenerateDatabaseService(namespace, name string, port int32) *corev1.Service { + labels := map[string]string{ + "app": name, + "helios.io/managed-by": "operator", + "helios.io/trait": "database", + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "None", + Selector: map[string]string{"app": name}, + Ports: []corev1.ServicePort{ + { + Port: port, + TargetPort: intstr.FromInt32(port), + Name: "db", + }, + }, + }, + } +} + +// resourceMustParse is a helper to parse resource quantities. Panics on invalid input. +func resourceMustParse(s string) resource.Quantity { + return resource.MustParse(s) +} + // GenerateBase64Token generates a random base64-encoded token // Useful for generating secure webhook secrets or API tokens func GenerateBase64Token(byteLength int) (string, error) { diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 41e5384..16e2b2d 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "testing" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -525,3 +526,376 @@ func TestGenerateBase64Token(t *testing.T) { }) } } + +func TestGenerateDatabaseStatefulSet(t *testing.T) { + sts := GenerateDatabaseStatefulSet( + "test-ns", "api-server-db", "api-server-db-secret", + "my_custom_db", "16", "2Gi", 5432, + ) + + // Verify metadata + if sts.Name != "api-server-db" { + t.Errorf("Expected name %q, got %q", "api-server-db", sts.Name) + } + if sts.Namespace != "test-ns" { + t.Errorf("Expected namespace %q, got %q", "test-ns", sts.Namespace) + } + + // Verify labels + if sts.Labels["helios.io/db-type"] != "postgres" { + t.Errorf("Expected db-type label %q, got %q", "postgres", sts.Labels["helios.io/db-type"]) + } + if sts.Labels["helios.io/trait"] != "database" { + t.Errorf("Expected trait label %q, got %q", "database", sts.Labels["helios.io/trait"]) + } + + // Verify replicas + if *sts.Spec.Replicas != 1 { + t.Errorf("Expected 1 replica, got %d", *sts.Spec.Replicas) + } + + // Verify serviceName + if sts.Spec.ServiceName != "api-server-db" { + t.Errorf("Expected serviceName %q, got %q", "api-server-db", sts.Spec.ServiceName) + } + + // Verify container + containers := sts.Spec.Template.Spec.Containers + if len(containers) != 1 { + t.Fatalf("Expected 1 container, got %d", len(containers)) + } + + container := containers[0] + if container.Image != "postgres:16" { + t.Errorf("Expected image %q, got %q", "postgres:16", container.Image) + } + + // Verify POSTGRES_DB env var (the core acceptance criteria) + foundPostgresDB := false + for _, env := range container.Env { + if env.Name == "POSTGRES_DB" { + foundPostgresDB = true + if env.Value != "my_custom_db" { + t.Errorf("Expected POSTGRES_DB value %q, got %q", "my_custom_db", env.Value) + } + } + if env.Name == "POSTGRES_USER" { + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Error("POSTGRES_USER should reference a secret") + } else { + if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + t.Errorf("Expected secret name %q, got %q", + "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + } + if env.ValueFrom.SecretKeyRef.Key != "DB_USER" { + t.Errorf("Expected secret key %q, got %q", + "DB_USER", env.ValueFrom.SecretKeyRef.Key) + } + } + } + if env.Name == "POSTGRES_PASSWORD" { + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Error("POSTGRES_PASSWORD should reference a secret") + } else { + if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + t.Errorf("Expected secret name %q, got %q", + "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + } + if env.ValueFrom.SecretKeyRef.Key != "DB_PASS" { + t.Errorf("Expected secret key %q, got %q", + "DB_PASS", env.ValueFrom.SecretKeyRef.Key) + } + } + } + } + if !foundPostgresDB { + t.Error("POSTGRES_DB env var not found in container") + } + + // Verify PGDATA env var + foundPGDATA := false + for _, env := range container.Env { + if env.Name == "PGDATA" { + foundPGDATA = true + expectedPGDATA := PostgresDataPath + "/" + PostgresDataSubPath + if env.Value != expectedPGDATA { + t.Errorf("Expected PGDATA value %q, got %q", expectedPGDATA, env.Value) + } + } + } + if !foundPGDATA { + t.Error("PGDATA env var not found in container") + } + + // Verify POSTGRES_INITDB_ARGS env var + foundInitDB := false + for _, env := range container.Env { + if env.Name == "POSTGRES_INITDB_ARGS" { + foundInitDB = true + } + } + if !foundInitDB { + t.Error("POSTGRES_INITDB_ARGS env var not found in container") + } + + // Verify livenessProbe exists + if container.LivenessProbe == nil { + t.Error("LivenessProbe should be set on Postgres container") + } + + // Verify volume claim template + if len(sts.Spec.VolumeClaimTemplates) != 1 { + t.Fatalf("Expected 1 VolumeClaimTemplate, got %d", len(sts.Spec.VolumeClaimTemplates)) + } + vct := sts.Spec.VolumeClaimTemplates[0] + storageQty := vct.Spec.Resources.Requests[corev1.ResourceStorage] + if storageQty.String() != "2Gi" { + t.Errorf("Expected storage %q, got %q", "2Gi", storageQty.String()) + } +} + +func TestGenerateDatabaseService(t *testing.T) { + svc := GenerateDatabaseService("test-ns", "api-server-db", 5432) + + // Verify metadata + if svc.Name != "api-server-db" { + t.Errorf("Expected name %q, got %q", "api-server-db", svc.Name) + } + if svc.Namespace != "test-ns" { + t.Errorf("Expected namespace %q, got %q", "test-ns", svc.Namespace) + } + + // Verify headless (clusterIP: None) + if svc.Spec.ClusterIP != "None" { + t.Errorf("Expected clusterIP %q, got %q", "None", svc.Spec.ClusterIP) + } + + // Verify selector + if svc.Spec.Selector["app"] != "api-server-db" { + t.Errorf("Expected selector app=%q, got %q", "api-server-db", svc.Spec.Selector["app"]) + } + + // Verify port + if len(svc.Spec.Ports) != 1 { + t.Fatalf("Expected 1 port, got %d", len(svc.Spec.Ports)) + } + if svc.Spec.Ports[0].Port != 5432 { + t.Errorf("Expected port 5432, got %d", svc.Spec.Ports[0].Port) + } + if svc.Spec.Ports[0].Name != "db" { + t.Errorf("Expected port name %q, got %q", "db", svc.Spec.Ports[0].Name) + } +} + +func TestReconcileDatabaseInstance(t *testing.T) { + + dbProps := map[string]interface{}{ + "dbType": "postgres", + "dbName": "my_custom_db", + "version": "16", + "storage": "2Gi", + } + dbPropsJSON, _ := json.Marshal(dbProps) + + app := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "default", + UID: "test-uid-789", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "api-server", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "database", + Properties: &runtime.RawExtension{ + Raw: dbPropsJSON, + }, + }, + }, + }, + }, + }, + } + + t.Run("CreatesStatefulSetAndService", func(t *testing.T) { + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(app). + Build() + + r := &HeliosAppReconciler{ + Client: fakeClient, + Scheme: fullScheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseInstance(ctx, app) + if err != nil { + t.Fatalf("reconcileDatabaseInstance failed: %v", err) + } + + // Verify StatefulSet was created + stsList := &appsv1.StatefulSetList{} + err = fakeClient.List(ctx, stsList) + if err != nil { + t.Fatalf("Failed to list StatefulSets: %v", err) + } + if len(stsList.Items) != 1 { + t.Fatalf("Expected 1 StatefulSet, got %d", len(stsList.Items)) + } + + sts := stsList.Items[0] + if sts.Name != "api-server-db" { + t.Errorf("Expected StatefulSet name %q, got %q", "api-server-db", sts.Name) + } + + // Verify POSTGRES_DB env var + containers := sts.Spec.Template.Spec.Containers + if len(containers) != 1 { + t.Fatalf("Expected 1 container, got %d", len(containers)) + } + foundDB := false + for _, env := range containers[0].Env { + if env.Name == "POSTGRES_DB" && env.Value == "my_custom_db" { + foundDB = true + } + } + if !foundDB { + t.Error("POSTGRES_DB env var not found with expected value") + } + + // Verify headless Service was created + svcList := &corev1.ServiceList{} + err = fakeClient.List(ctx, svcList) + if err != nil { + t.Fatalf("Failed to list Services: %v", err) + } + if len(svcList.Items) != 1 { + t.Fatalf("Expected 1 Service, got %d", len(svcList.Items)) + } + if svcList.Items[0].Spec.ClusterIP != "None" { + t.Errorf("Expected headless Service (clusterIP: None), got %q", svcList.Items[0].Spec.ClusterIP) + } + }) + + t.Run("SkipsWhenNoTraits", func(t *testing.T) { + appWithoutDB := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "no-db-app", + Namespace: "default", + UID: "test-uid-no-db", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "frontend", + Type: "web-service", + }, + }, + }, + } + + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(appWithoutDB). + Build() + + r := &HeliosAppReconciler{ + Client: fakeClient, + Scheme: fullScheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseInstance(ctx, appWithoutDB) + if err != nil { + t.Fatalf("reconcileDatabaseInstance should not fail for app without database traits: %v", err) + } + + // Verify no StatefulSet or Service was created + stsList := &appsv1.StatefulSetList{} + _ = fakeClient.List(ctx, stsList) + if len(stsList.Items) != 0 { + t.Errorf("Expected no StatefulSets, got %d", len(stsList.Items)) + } + + svcList := &corev1.ServiceList{} + _ = fakeClient.List(ctx, svcList) + if len(svcList.Items) != 0 { + t.Errorf("Expected no Services, got %d", len(svcList.Items)) + } + }) + + t.Run("SkipsNonPostgresType", func(t *testing.T) { + redisProps := map[string]interface{}{ + "dbType": "redis", + "version": "7", + } + redisPropsJSON, _ := json.Marshal(redisProps) + + appWithRedis := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "redis-app", + Namespace: "default", + UID: "test-uid-redis", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "cache", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "database", + Properties: &runtime.RawExtension{ + Raw: redisPropsJSON, + }, + }, + }, + }, + }, + }, + } + + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(appWithRedis). + Build() + + r := &HeliosAppReconciler{ + Client: fakeClient, + Scheme: fullScheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseInstance(ctx, appWithRedis) + if err != nil { + t.Fatalf("reconcileDatabaseInstance should not fail for redis type: %v", err) + } + + // Verify no StatefulSet was created (only postgres is provisioned) + stsList := &appsv1.StatefulSetList{} + _ = fakeClient.List(ctx, stsList) + if len(stsList.Items) != 0 { + t.Errorf("Expected no StatefulSets for redis type, got %d", len(stsList.Items)) + } + }) +} diff --git a/apps/operator/internal/controller/heliosapp_controller.go b/apps/operator/internal/controller/heliosapp_controller.go index ba69c7a..86fbb6f 100644 --- a/apps/operator/internal/controller/heliosapp_controller.go +++ b/apps/operator/internal/controller/heliosapp_controller.go @@ -128,6 +128,18 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } + // ------------------------------------------------------------------ + // PHASE 0.7: Database Instance Provisioning + // Provision StatefulSets and headless Services for database traits. + // Runs AFTER secrets so that the credential Secret already exists + // when the database pod starts. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseInstance(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile database instance") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database instance provisioning failed: %v", err)) + return ctrl.Result{}, err + } + // ------------------------------------------------------------------ // PHASE 0.6: Trigger Initial PipelineRun (if not already done) // ------------------------------------------------------------------ @@ -507,6 +519,7 @@ func (r *HeliosAppReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&appv1alpha1.HeliosApp{}). Owns(&appsv1.Deployment{}). + Owns(&appsv1.StatefulSet{}). Owns(&corev1.Service{}). Owns(&networkingv1.Ingress{}). Watches( diff --git a/apps/operator/internal/controller/suite_test.go b/apps/operator/internal/controller/suite_test.go index aeb402a..d57dd21 100644 --- a/apps/operator/internal/controller/suite_test.go +++ b/apps/operator/internal/controller/suite_test.go @@ -102,6 +102,21 @@ var _ = AfterSuite(func() { // properly set up, run 'make setup-envtest' beforehand. func getFirstFoundEnvTestBinaryDir() string { basePath := filepath.Join("..", "..", "bin", "k8s") + var found string + _ = filepath.WalkDir(basePath, func(path string, d os.DirEntry, err error) error { + if err != nil { + return nil // skip unreadable dirs + } + if !d.IsDir() && (d.Name() == "etcd" || d.Name() == "etcd.exe") { + found = filepath.Dir(path) + return filepath.SkipAll + } + return nil + }) + if found != "" { + return found + } + // Fallback: return first subdirectory (original behavior) entries, err := os.ReadDir(basePath) if err != nil { logf.Log.Error(err, "Failed to read directory", "path", basePath) diff --git a/cue/definitions/traits/database.cue b/cue/definitions/traits/database.cue index 02cb04d..08a1ba9 100644 --- a/cue/definitions/traits/database.cue +++ b/cue/definitions/traits/database.cue @@ -3,8 +3,14 @@ package traits import "strings" // DatabaseTrait — declares database requirements for a Component. -// Renders a ConfigMap with connection metadata. Credentials (Secret) are -// generated by the HeliosAppReconciler (see issue #33), not by CUE. +// Renders: +// 1. ConfigMap with connection metadata (all dbTypes) +// +// StatefulSet + Service provisioning is handled directly by the +// Go Operator (reconcileDatabaseInstance), NOT by CUE output. +// +// Credentials (Secret) are generated by the HeliosAppReconciler +// (see issue #33), not by CUE. // // Usage via the trait system: // traits: [{ @@ -48,10 +54,14 @@ _#defaultPorts: { "\(_p.name)-db", ][0] + // Conventional names shared by ConfigMap and Operator provisioning. + let _dbHostName = "\(_p.name)-db" + let _secretName = "\(_p.name)-db-secret" + outputs: { // ConfigMap: non-sensitive connection metadata for the application. - // DB_SECRET_NAME follows a convention so the app (and Operator) - // know where to find the credentials once they are generated. + // The app uses these values to connect to the database provisioned + // by the Go Operator. configmap: { apiVersion: "v1" kind: "ConfigMap" @@ -65,12 +75,12 @@ _#defaultPorts: { } data: { DB_TYPE: _p.dbType - DB_HOST: "\(_p.name)-db" + DB_HOST: _dbHostName DB_PORT: "\(_p.port)" DB_NAME: _effectiveDBName DB_VERSION: _p.version DB_STORAGE: _p.storage - DB_SECRET_NAME: "\(_p.name)-db-secret" + DB_SECRET_NAME: _secretName } } } From adf66148886a457979331ff903fe5036262f416c Mon Sep 17 00:00:00 2001 From: 22127125 Date: Sat, 7 Mar 2026 21:39:56 +0700 Subject: [PATCH 06/19] impl: add databasePicker component logic --- .../examples/advanced-template/template.yaml | 36 ++--------- apps/portal/packages/app/src/App.tsx | 13 ++-- .../DatabasePicker.tsx | 61 ++++++++++++++++--- .../DatabasePickerExtension/extension.ts | 13 ++-- .../packages/app/src/scaffolder/index.ts | 2 +- 5 files changed, 78 insertions(+), 47 deletions(-) diff --git a/apps/portal/examples/advanced-template/template.yaml b/apps/portal/examples/advanced-template/template.yaml index 91da460..6d0e8f0 100644 --- a/apps/portal/examples/advanced-template/template.yaml +++ b/apps/portal/examples/advanced-template/template.yaml @@ -43,38 +43,14 @@ spec: type: string description: The name of the repository (e.g. my-service) + # --- UPDATED DATABASE CONFIGURATION STEP --- - title: Database Configuration properties: - requireDatabase: - title: Add Database - type: boolean - description: Check to provision a database for this project - default: false - dependencies: - requireDatabase: - oneOf: - - properties: - requireDatabase: - const: false - - properties: - requireDatabase: - const: true - databaseType: - title: Database Type - type: string - enum: [postgres] - default: postgres - databaseName: - title: Database Name - type: string - description: The name of the database to create - databaseVersion: - title: Database Version - type: string - description: Select the PostgreSQL version - enum: ['13', '14', '15', '16'] - default: '15' - required: [databaseType, databaseName, databaseVersion] + databaseConfig: + title: Database Settings + type: object + ui:field: DatabasePicker + # --- END OF NEW STEP --- - title: Repository & Webhook required: diff --git a/apps/portal/packages/app/src/App.tsx b/apps/portal/packages/app/src/App.tsx index 0f1087b..f66801d 100644 --- a/apps/portal/packages/app/src/App.tsx +++ b/apps/portal/packages/app/src/App.tsx @@ -38,6 +38,7 @@ import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; import { NotificationsPage } from '@backstage/plugin-notifications'; import { SignalsDisplay } from '@backstage/plugin-signals'; +import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder-react'; import { DatabasePickerExtension } from './scaffolder'; const app = createApp({ @@ -96,11 +97,13 @@ const routes = ( - }> - {scaffolderFieldExtensions.map(Extension => ( - - ))} - + + + + + + } /> } /> ) => { - // Todo: Implementation - return <>; -}; +}: FieldExtensionComponentProps) => { + // Default to 'none' if no data is present + const dbType = formData?.dbType || 'none'; + const dbName = formData?.dbName || ''; + + const handleTypeChange = (event: React.ChangeEvent<{ value: unknown }>) => { + const newType = event.target.value as string; + onChange({ + dbType: newType, + // Clear the dbName if they switch back to "No Database" + ...(newType === 'postgres' ? { dbName: dbName } : { dbName: '' }), + }); + }; + + const handleNameChange = (event: React.ChangeEvent) => { + onChange({ + dbType, + dbName: event.target.value, + }); + }; + + return ( +
+ + Database Type + + + + {dbType === 'postgres' && ( + 0 && !dbName} + /> + )} +
+ ); +}; \ No newline at end of file diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts index 8f1ca0d..2cd7b6c 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -2,12 +2,17 @@ import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; import { DatabasePicker } from './DatabasePicker'; -export const DatabasePickerExtension = scaffolderPlugin.provide( +export const DatabasePickerExtension: any = scaffolderPlugin.provide( createScaffolderFieldExtension({ name: 'DatabasePicker', component: DatabasePicker, - validation: (value, validation, context) => { - // Todo: Implement validation logic + validation: (value: any, validation: any) => { + // Custom validation: If postgres is selected, dbName MUST be provided + if (value?.dbType === 'postgres') { + if (!value?.dbName) { + validation.addError('Database Name is required when PostgreSQL is selected'); + } + } }, }), -); +); \ No newline at end of file diff --git a/apps/portal/packages/app/src/scaffolder/index.ts b/apps/portal/packages/app/src/scaffolder/index.ts index 03b3db3..951310d 100644 --- a/apps/portal/packages/app/src/scaffolder/index.ts +++ b/apps/portal/packages/app/src/scaffolder/index.ts @@ -1 +1 @@ -export * from './DatabasePickerExtension/index'; \ No newline at end of file +export { DatabasePickerExtension } from './DatabasePickerExtension/extension'; \ No newline at end of file From 590987e1f7ff78a4eaf4275c9610865204e9841c Mon Sep 17 00:00:00 2001 From: NgocAnhDo26 Date: Sun, 8 Mar 2026 17:46:16 +0700 Subject: [PATCH 07/19] fix(env): update GIT_AUTHOR_NAME format and remove unused task from Taskfile - Changed GIT_AUTHOR_NAME in .env.example to use quotes for consistency. - Removed the setup:tekton-resources task from Taskfile.yml as it is no longer needed. --- .env.example | 2 +- Taskfile.yml | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index da0bee1..203d1a4 100644 --- a/.env.example +++ b/.env.example @@ -36,5 +36,5 @@ DOCKER_EMAIL= # ----------------------------------------------------------------------------- # Git Author (optional, used by operator for GitOps commits) # ----------------------------------------------------------------------------- -GIT_AUTHOR_NAME=Helios Operator +GIT_AUTHOR_NAME="Helios Operator" GIT_AUTHOR_EMAIL=operator@helios.io diff --git a/Taskfile.yml b/Taskfile.yml index 0cfd1e5..1da6ccf 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -53,7 +53,6 @@ tasks: - task: setup:tekton - task: setup:argocd - task: setup:crds - - task: setup:tekton-resources - task: setup:tekton-rbac - task: setup:credentials - task: setup:portal-deps @@ -95,7 +94,7 @@ tasks: cmds: - kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f - - echo "Installing ArgoCD..." - - kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml + - kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml - echo "Waiting for ArgoCD server..." - kubectl rollout status deployment/argocd-server -n argocd --timeout=180s @@ -105,14 +104,6 @@ tasks: cmds: - make install - setup:tekton-resources: - desc: Export CUE definitions and apply Tekton tasks - cmds: - - echo "Exporting Tekton tasks from CUE..." - - cue export ./cue/definitions/tekton/tasks/*.cue --out yaml > apps/operator/tekton/tasks.yaml - - echo "Applying Tekton resources..." - - kubectl apply -f apps/operator/tekton/tasks.yaml - setup:tekton-rbac: desc: Grant Tekton Triggers SA the permissions to create PipelineRuns cmds: From c635aaaa116d46393a294f69a9a4c0d646a5af10 Mon Sep 17 00:00:00 2001 From: hoangphuc841 Date: Sun, 8 Mar 2026 21:44:48 +0700 Subject: [PATCH 08/19] fix(setup scripts): fix the scripts for setting for windows compatibility --- Taskfile.yml | 74 ++++++++++++++++++++++++++------------- scripts/check-prereqs.bat | 2 +- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 1da6ccf..07c3e83 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -35,12 +35,18 @@ tasks: check: desc: Verify all development prerequisites are installed cmds: - - bash scripts/check-prereqs.sh + - cmd: bash scripts/check-prereqs.sh + platforms: [linux, darwin] + - cmd: cmd /c scripts\\check-prereqs.bat + platforms: [windows] check:env: desc: Verify prerequisites and .env configuration cmds: - - bash scripts/check-prereqs.sh --env + - cmd: bash scripts/check-prereqs.sh --env + platforms: [linux, darwin] + - cmd: cmd /c scripts\\check-prereqs.bat --env + platforms: [windows] # =========================================================================== # Setup Tasks @@ -68,7 +74,8 @@ tasks: - k3d cluster list 2>/dev/null | grep -q '{{.CLUSTER_NAME}}' cmds: - echo "Creating k3d cluster '{{.CLUSTER_NAME}}'..." - - k3d cluster create {{.CLUSTER_NAME}} --agents 1 --wait + - k3d cluster create {{.CLUSTER_NAME}} --agents 1 --wait --api-port 0.0.0.0:6550 + - kubectl config set-cluster k3d-{{.CLUSTER_NAME}} --server=https://localhost:6550 - kubectl cluster-info setup:tekton: @@ -81,13 +88,13 @@ tasks: - echo "Installing Tekton Interceptors..." - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml - echo "Waiting for Tekton Pipelines controller..." - - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=120s + - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=600s - echo "Patching Tekton feature flags..." - >- kubectl patch configmap feature-flags -n tekton-pipelines -p '{"data":{"disable-affinity-assistant":"true","coschedule":"disabled"}}' - kubectl rollout restart deployment tekton-pipelines-controller -n tekton-pipelines - - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=120s + - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=600s setup:argocd: desc: Install ArgoCD @@ -96,7 +103,7 @@ tasks: - echo "Installing ArgoCD..." - kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml - echo "Waiting for ArgoCD server..." - - kubectl rollout status deployment/argocd-server -n argocd --timeout=180s + - kubectl rollout status deployment/argocd-server -n argocd --timeout=300s setup:crds: desc: Install Helios CRDs into the cluster @@ -154,7 +161,10 @@ tasks: GITHUB_TOKEN: $GITHUB_TOKEN GITHUB_USER: $GITHUB_USER cmds: - - make run + - cmd: make run + platforms: [linux, darwin] + - cmd: go run ./cmd/main.go + platforms: [windows] dev:portal: desc: Run the Backstage portal with ArgoCD + kubectl proxy @@ -173,30 +183,46 @@ tasks: internal: true cmds: - echo "Starting ArgoCD port-forward (localhost:{{.ARGOCD_PORT}})..." - - kubectl port-forward -n argocd svc/argocd-server {{.ARGOCD_PORT}}:443 &>/dev/null & - - sleep 2 + - cmd: kubectl port-forward -n argocd svc/argocd-server {{.ARGOCD_PORT}}:443 &>/dev/null & + platforms: [linux, darwin] + - cmd: powershell -Command "Start-Process kubectl -ArgumentList 'port-forward', '-n', 'argocd', 'svc/argocd-server', '{{.ARGOCD_PORT}}:443' -WindowStyle Hidden" + platforms: [windows] + - cmd: sleep 2 + platforms: [linux, darwin] + - cmd: powershell -Command "Start-Sleep -Seconds 2" + platforms: [windows] - echo "Starting kubectl proxy (localhost:{{.KUBECTL_PROXY_PORT}})..." - - kubectl proxy --port={{.KUBECTL_PROXY_PORT}} &>/dev/null & - - sleep 1 + - cmd: kubectl proxy --port={{.KUBECTL_PROXY_PORT}} &>/dev/null & + platforms: [linux, darwin] + - cmd: powershell -Command "Start-Process kubectl -ArgumentList 'proxy', '--port={{.KUBECTL_PROXY_PORT}}' -WindowStyle Hidden" + platforms: [windows] + - cmd: sleep 1 + platforms: [linux, darwin] + - cmd: powershell -Command "Start-Sleep -Seconds 1" + platforms: [windows] dev:portal:start: desc: Generate ArgoCD token and start Backstage internal: true dir: apps/portal cmds: - - | - ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) - TOKEN_JSON=$(curl -k -s -X POST \ - -H "Content-Type: application/json" \ - -d "{\"username\":\"admin\",\"password\":\"$ARGOCD_PASS\"}" \ - https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session) - export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | sed 's/.*"token":"\([^"]*\)".*/\1/') - if [ -z "$ARGOCD_AUTH_TOKEN" ] || [ "${#ARGOCD_AUTH_TOKEN}" -lt 20 ]; then - echo "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." - else - echo "ArgoCD token generated." - fi - yarn start + - cmd: | + ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) + TOKEN_JSON=$(curl -k -s -X POST \ + -H "Content-Type: application/json" \ + -d "{\"username\":\"admin\",\"password\":\"$ARGOCD_PASS\"}" \ + https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session) + export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | sed 's/.*"token":"\([^"]*\)".*/\1/') + if [ -z "$ARGOCD_AUTH_TOKEN" ] || [ "${#ARGOCD_AUTH_TOKEN}" -lt 20 ]; then + echo "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." + else + echo "ArgoCD token generated." + fi + yarn start + platforms: [linux, darwin] + - cmd: | + powershell -Command "$pass=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}'))); $json=curl.exe -k -s -X POST -H 'Content-Type: application/json' -d \"{\`\"username\`\":\`\"admin\`\",\`\"password\`\":\`\"$pass\`\"}\" https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session; if($json -match '\"token\":\"([^\"]+)\"'){ $env:ARGOCD_AUTH_TOKEN=$matches[1]; echo 'ArgoCD token generated.' } else { echo 'WARNING: Could not generate ArgoCD token. ArgoCD features may not work.' }; yarn start" + platforms: [windows] # =========================================================================== # Testing Tasks diff --git a/scripts/check-prereqs.bat b/scripts/check-prereqs.bat index 90981b1..ac95be0 100644 --- a/scripts/check-prereqs.bat +++ b/scripts/check-prereqs.bat @@ -32,7 +32,7 @@ echo [Core Tools] call :check_tool "go" "go version" call :check_tool "docker" "docker --version" -call :check_tool "kubectl" "kubectl version --client --short 2>nul" +call :check_tool "kubectl" "kubectl version --client" call :check_tool "k3d" "k3d version" call :check_tool "cue" "cue version" From b317f4c5a2edf4cf66c283b4255872a0090e9895 Mon Sep 17 00:00:00 2001 From: hoangphuc841 Date: Sun, 8 Mar 2026 23:22:28 +0700 Subject: [PATCH 09/19] fix(setup): resolve windows compatibility for crd generation and argocd token --- Taskfile.yml | 19 ++++++++++++++----- scripts/start-portal.ps1 | 31 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 scripts/start-portal.ps1 diff --git a/Taskfile.yml b/Taskfile.yml index 07c3e83..5657fb9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,4 +1,4 @@ -# ============================================================================= +# ============================================================================= # Helios Platform - Root Taskfile # ============================================================================= # Usage: @@ -89,6 +89,13 @@ tasks: - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml - echo "Waiting for Tekton Pipelines controller..." - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=600s + - echo "Waiting for Tekton Pipelines webhook..." + - kubectl rollout status deployment/tekton-pipelines-webhook -n tekton-pipelines --timeout=600s + - echo "Waiting for webhook endpoints to register..." + - cmd: sleep 10 + platforms: [linux, darwin] + - cmd: powershell -Command "Start-Sleep -Seconds 10" + platforms: [windows] - echo "Patching Tekton feature flags..." - >- kubectl patch configmap feature-flags -n tekton-pipelines @@ -103,13 +110,16 @@ tasks: - echo "Installing ArgoCD..." - kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml - echo "Waiting for ArgoCD server..." - - kubectl rollout status deployment/argocd-server -n argocd --timeout=300s + - kubectl rollout status deployment/argocd-server -n argocd --timeout=600s setup:crds: desc: Install Helios CRDs into the cluster dir: apps/operator cmds: - - make install + - cmd: make install + platforms: [linux, darwin] + - cmd: kubectl kustomize config/crd | kubectl apply -f - + platforms: [windows] setup:tekton-rbac: desc: Grant Tekton Triggers SA the permissions to create PipelineRuns @@ -220,8 +230,7 @@ tasks: fi yarn start platforms: [linux, darwin] - - cmd: | - powershell -Command "$pass=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}'))); $json=curl.exe -k -s -X POST -H 'Content-Type: application/json' -d \"{\`\"username\`\":\`\"admin\`\",\`\"password\`\":\`\"$pass\`\"}\" https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session; if($json -match '\"token\":\"([^\"]+)\"'){ $env:ARGOCD_AUTH_TOKEN=$matches[1]; echo 'ArgoCD token generated.' } else { echo 'WARNING: Could not generate ArgoCD token. ArgoCD features may not work.' }; yarn start" + - cmd: powershell -ExecutionPolicy Bypass -File ../../scripts/start-portal.ps1 -ArgocdPort {{.ARGOCD_PORT}} platforms: [windows] # =========================================================================== diff --git a/scripts/start-portal.ps1 b/scripts/start-portal.ps1 new file mode 100644 index 0000000..4954fc7 --- /dev/null +++ b/scripts/start-portal.ps1 @@ -0,0 +1,31 @@ +param( + [string]$ArgocdPort = "8080" +) + +# Generate ArgoCD auth token +try { + $passB64 = kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' + $pass = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($passB64)).Trim() + + $body = @{ username = "admin"; password = $pass } | ConvertTo-Json -Compress + + # PowerShell 5.1 compatibility for skipping SSL checks + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } + + $parsed = Invoke-RestMethod -Uri "https://127.0.0.1:${ArgocdPort}/api/v1/session" ` + -Method Post ` + -ContentType "application/json" ` + -Body $body + if ($parsed.token) { + $env:ARGOCD_AUTH_TOKEN = $parsed.token + Write-Host "ArgoCD token generated." + } else { + Write-Host "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." + } +} catch { + Write-Host "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." + Write-Host "Error: $_" +} + +# Start Backstage +yarn start From 4b59a033baffabb46ff7cf039a19bcdff38dc807 Mon Sep 17 00:00:00 2001 From: PhamHoangKha1403 Date: Mon, 9 Mar 2026 02:45:40 +0700 Subject: [PATCH 10/19] fix: address CodeRabbit review feedback for postgres provisioning" -m "- Move database provisioning above image validation guard - Fix panic on invalid storage by using ParseQuantity - Add PGPORT env var and update health probes to use custom port - Handle StatefulSet and Service drift by actively updating mutable fields --- .../internal/controller/database_resources.go | 53 +++++++++++--- .../controller/database_resources_test.go | 71 ++++++++++++++++--- .../controller/heliosapp_controller.go | 28 ++++---- 3 files changed, 119 insertions(+), 33 deletions(-) diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index 64cc099..e363578 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -347,9 +347,14 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app } // --- StatefulSet --- - sts := GenerateDatabaseStatefulSet( + sts, err := GenerateDatabaseStatefulSet( app.Namespace, dbHost, secretName, effectiveDBName, version, storage, int32(port), ) + if err != nil { + log.Error(err, "Failed to generate database StatefulSet", + "component", dbTrait.ComponentName, "storage", storage) + return fmt.Errorf("failed to generate StatefulSet for %s: %w", dbHost, err) + } if err := ctrl.SetControllerReference(app, sts, r.Scheme); err != nil { log.Error(err, "Failed to set owner reference for database StatefulSet", @@ -358,7 +363,7 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app } existingSts := &appsv1.StatefulSet{} - err := r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) + err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) if err != nil { if !errors.IsNotFound(err) { return fmt.Errorf("failed to check for StatefulSet %s: %w", dbHost, err) @@ -378,9 +383,22 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app } } } else { - log.Info("Database StatefulSet already exists, skipping", + // Handle StatefulSet drift: update spec to match the new template + log.Info("Database StatefulSet already exists, updating if necessary", "component", dbTrait.ComponentName, "statefulset", dbHost) + + // We only update the mutable fields (Replicas, Template) + updatedSts := existingSts.DeepCopy() + updatedSts.Spec.Replicas = sts.Spec.Replicas + updatedSts.Spec.Template = sts.Spec.Template + + // We need to preserve the existing VolumeClaimTemplates when updating + updatedSts.Spec.VolumeClaimTemplates = existingSts.Spec.VolumeClaimTemplates + + if err := r.Update(ctx, updatedSts); err != nil { + return fmt.Errorf("failed to update StatefulSet %s: %w", dbHost, err) + } } // --- Headless Service --- @@ -412,9 +430,16 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app } } } else { - log.Info("Database Service already exists, skipping", + log.Info("Database Service already exists, updating if necessary", "component", dbTrait.ComponentName, "service", dbHost) + + updatedSvc := existingSvc.DeepCopy() + updatedSvc.Spec.Ports = svc.Spec.Ports + + if err := r.Update(ctx, updatedSvc); err != nil { + return fmt.Errorf("failed to update Service %s: %w", dbHost, err) + } } log.Info("Successfully reconciled database instance", @@ -429,7 +454,12 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app // GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. // The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and // uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. -func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) *appsv1.StatefulSet { +func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) (*appsv1.StatefulSet, error) { + storageQty, err := resource.ParseQuantity(storage) + if err != nil { + return nil, fmt.Errorf("invalid storage size format %q: %w", storage, err) + } + replicas := int32(1) labels := map[string]string{ "app": name, @@ -503,6 +533,11 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s Name: "POSTGRES_INITDB_ARGS", Value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C", }, + { + // Explicitly set the custom port so that postgres knows to listen on it. + Name: "PGPORT", + Value: fmt.Sprintf("%d", port), + }, }, VolumeMounts: []corev1.VolumeMount{ { @@ -524,7 +559,7 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s ReadinessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName}, + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, }, }, InitialDelaySeconds: 5, @@ -533,7 +568,7 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName}, + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, }, }, InitialDelaySeconds: 30, @@ -554,14 +589,14 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s }, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceStorage: resourceMustParse(storage), + corev1.ResourceStorage: storageQty, }, }, }, }, }, }, - } + }, nil } // GenerateDatabaseService creates a headless Service for a database StatefulSet. diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 16e2b2d..42e09c3 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -3,6 +3,7 @@ package controller import ( "context" "encoding/json" + "strings" "testing" appsv1 "k8s.io/api/apps/v1" @@ -528,14 +529,18 @@ func TestGenerateBase64Token(t *testing.T) { } func TestGenerateDatabaseStatefulSet(t *testing.T) { - sts := GenerateDatabaseStatefulSet( - "test-ns", "api-server-db", "api-server-db-secret", + sts, err := GenerateDatabaseStatefulSet( + "test-ns", "my-app-db", "my-app-db-secret", "my_custom_db", "16", "2Gi", 5432, ) + if err != nil { + t.Fatalf("GenerateDatabaseStatefulSet failed: %v", err) + } + // Verify metadata - if sts.Name != "api-server-db" { - t.Errorf("Expected name %q, got %q", "api-server-db", sts.Name) + if sts.Name != "my-app-db" { + t.Errorf("Expected name %q, got %q", "my-app-db", sts.Name) } if sts.Namespace != "test-ns" { t.Errorf("Expected namespace %q, got %q", "test-ns", sts.Namespace) @@ -555,8 +560,8 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { } // Verify serviceName - if sts.Spec.ServiceName != "api-server-db" { - t.Errorf("Expected serviceName %q, got %q", "api-server-db", sts.Spec.ServiceName) + if sts.Spec.ServiceName != "my-app-db" { + t.Errorf("Expected serviceName %q, got %q", "my-app-db", sts.Spec.ServiceName) } // Verify container @@ -570,6 +575,10 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { t.Errorf("Expected image %q, got %q", "postgres:16", container.Image) } + // Verify ports + if len(container.Ports) != 1 || container.Ports[0].ContainerPort != 5432 { + t.Errorf("Expected container port 5432, got %v", container.Ports) + } // Verify POSTGRES_DB env var (the core acceptance criteria) foundPostgresDB := false for _, env := range container.Env { @@ -583,9 +592,9 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { t.Error("POSTGRES_USER should reference a secret") } else { - if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + if env.ValueFrom.SecretKeyRef.Name != "my-app-db-secret" { t.Errorf("Expected secret name %q, got %q", - "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + "my-app-db-secret", env.ValueFrom.SecretKeyRef.Name) } if env.ValueFrom.SecretKeyRef.Key != "DB_USER" { t.Errorf("Expected secret key %q, got %q", @@ -597,9 +606,9 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { t.Error("POSTGRES_PASSWORD should reference a secret") } else { - if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + if env.ValueFrom.SecretKeyRef.Name != "my-app-db-secret" { t.Errorf("Expected secret name %q, got %q", - "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + "my-app-db-secret", env.ValueFrom.SecretKeyRef.Name) } if env.ValueFrom.SecretKeyRef.Key != "DB_PASS" { t.Errorf("Expected secret key %q, got %q", @@ -638,9 +647,38 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { t.Error("POSTGRES_INITDB_ARGS env var not found in container") } - // Verify livenessProbe exists + // Verify PGPORT env var + foundPGPORT := false + for _, env := range container.Env { + if env.Name == "PGPORT" { + foundPGPORT = true + if env.Value != "5432" { + t.Errorf("Expected PGPORT value %q, got %q", "5432", env.Value) + } + } + } + if !foundPGPORT { + t.Error("PGPORT env var not found in container") + } + + // Verify livenessProbe exists and uses custom port if container.LivenessProbe == nil { t.Error("LivenessProbe should be set on Postgres container") + } else { + cmdStr := strings.Join(container.LivenessProbe.Exec.Command, " ") + if !strings.Contains(cmdStr, "-p $(PGPORT)") { + t.Errorf("LivenessProbe command missing custom port flag. Got: %s", cmdStr) + } + } + + // Verify readinessProbe uses custom port + if container.ReadinessProbe == nil { + t.Error("ReadinessProbe should be set on Postgres container") + } else { + cmdStr := strings.Join(container.ReadinessProbe.Exec.Command, " ") + if !strings.Contains(cmdStr, "-p $(PGPORT)") { + t.Errorf("ReadinessProbe command missing custom port flag. Got: %s", cmdStr) + } } // Verify volume claim template @@ -654,6 +692,17 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { } } +func TestGenerateDatabaseStatefulSet_InvalidStorage(t *testing.T) { + _, err := GenerateDatabaseStatefulSet("default", "my-app-db", "my-app-db-secret", "my_custom_db", "16", "invalid-size", 5432) + + if err == nil { + t.Fatal("Expected error for invalid storage size, got nil") + } + if !strings.Contains(err.Error(), "invalid storage size format") { + t.Errorf("Expected error to mention invalid storage format, got %v", err) + } +} + func TestGenerateDatabaseService(t *testing.T) { svc := GenerateDatabaseService("test-ns", "api-server-db", 5432) diff --git a/apps/operator/internal/controller/heliosapp_controller.go b/apps/operator/internal/controller/heliosapp_controller.go index 86fbb6f..f0441f2 100644 --- a/apps/operator/internal/controller/heliosapp_controller.go +++ b/apps/operator/internal/controller/heliosapp_controller.go @@ -85,19 +85,6 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") - for _, comp := range appModel.App.Components { - // We can add more specific checks here based on component type - // For now, checks if 'image' property exists and is not empty for all components - // assuming all workloads need an image. - if img, ok := comp.Properties["image"].(string); !ok || img == "" { - msg := fmt.Sprintf("Component '%s' is waiting for image (likely building). Status: Pending.", comp.Name) - log.Info(msg) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhasePending, msg) - return ctrl.Result{}, nil // Wait for next update (CI/CD will update CR with image) - } - } - // 3. Render via CUE Engine manifestBytes, err := r.CueEngine.Render(appModel) if err != nil { @@ -140,6 +127,21 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } + // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") + // This validation is for application workloads (GitOps pipeline downstream). + // We run this AFTER DB provisioning so databases can come up while app is building. + for _, comp := range appModel.App.Components { + // We can add more specific checks here based on component type + // For now, checks if 'image' property exists and is not empty for all components + // assuming all workloads need an image. + if img, ok := comp.Properties["image"].(string); !ok || img == "" { + msg := fmt.Sprintf("Component '%s' is waiting for image (likely building). Status: Pending.", comp.Name) + log.Info(msg) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhasePending, msg) + return ctrl.Result{}, nil // Wait for next update (CI/CD will update CR with image) + } + } + // ------------------------------------------------------------------ // PHASE 0.6: Trigger Initial PipelineRun (if not already done) // ------------------------------------------------------------------ From 5633f5aa84038856ea269689f16d7b424aaf1e0f Mon Sep 17 00:00:00 2001 From: PhamHoangKha1403 Date: Thu, 12 Mar 2026 12:21:13 +0700 Subject: [PATCH 11/19] [fix]Delete extra rows --- cue/definitions/traits/database.cue | 1 - 1 file changed, 1 deletion(-) diff --git a/cue/definitions/traits/database.cue b/cue/definitions/traits/database.cue index 08a1ba9..e6af6be 100644 --- a/cue/definitions/traits/database.cue +++ b/cue/definitions/traits/database.cue @@ -68,7 +68,6 @@ _#defaultPorts: { metadata: { name: "\(_p.name)-db-config" labels: { - app: _p.name "helios.io/managed-by": "operator" "helios.io/trait": "database" } From 4cad016431594ed88b8b2cf24619699b26d070ae Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 15 Mar 2026 10:49:33 +0700 Subject: [PATCH 12/19] feat: implement automated secret injection and NestJS Prisma template (#39) Add Phase 0.9 to the operator reconcile loop that injects DB_HOST, DB_USER, DB_PASS env vars (via secretKeyRef) into backend Deployments. Create a NestJS + Prisma Backstage template with Prisma 7 driver adapter support. Operator changes: - Add InjectDatabaseEnvVars() and reconcileDatabaseSecretInjection() to patch live Deployments with database credential references - Add 6 unit/integration tests for injection logic (idempotency, empty containers, missing deployments) - Update RBAC role for statefulsets and ingresses - Bump golangci-lint to v2.11.3, CUE language to v0.16.0 NestJS Prisma template: - Full Backstage scaffolder template with NestJS 11 + Prisma 7 + pg adapter - prisma.config.ts for Prisma 7 datasource URL (replaces schema.prisma url) - PrismaService with namespace import (import * as pg) for CommonJS compat - Multi-stage Dockerfile with migration support - Helios GitOps manifest with database trait Documentation: - Manual verification guide for k3d cluster testing - Implementation plan, task tracking, and walkthrough docs - Claude Code skills for implementation workflow and Go/CUE guidelines Co-Authored-By: Claude Opus 4.6 --- .claude/skills/implement-task/SKILL.md | 137 + .claude/skills/use-modern-cue/SKILL.md | 361 + .claude/skills/use-modern-go/SKILL.md | 291 + apps/operator/Makefile | 2 +- .../config/manager/kustomization.yaml | 2 + apps/operator/config/rbac/role.yaml | 13 + apps/operator/go.mod | 101 +- apps/operator/go.sum | 206 +- .../internal/controller/database_resources.go | 99 + .../controller/database_resources_test.go | 296 +- .../controller/heliosapp_controller.go | 12 + .../content/gitops/helios-app.yaml | 35 + .../content/source/.env.example | 9 + .../content/source/Dockerfile | 40 + .../content/source/catalog-info.yaml | 12 + .../content/source/nest-cli.json | 8 + .../content/source/package-lock.json | 8987 +++++++++++++++++ .../content/source/package.json | 58 + .../content/source/prisma.config.ts | 27 + .../prisma/migrations/migration_lock.toml | 3 + .../content/source/prisma/schema.prisma | 23 + .../content/source/src/app.controller.ts | 17 + .../content/source/src/app.module.ts | 11 + .../content/source/src/app.service.ts | 15 + .../content/source/src/main.ts | 13 + .../source/src/prisma/prisma.module.ts | 9 + .../source/src/prisma/prisma.service.ts | 61 + .../content/source/tsconfig.build.json | 4 + .../content/source/tsconfig.json | 24 + .../nestjs-prisma-template/template.yaml | 190 + cue/cue.mod/module.cue | 2 +- docs/manual-verification-guide.md | 540 + implementation_plan.md | 115 + task.md | 28 + walkthrough.md | 2596 +++++ 35 files changed, 14203 insertions(+), 144 deletions(-) create mode 100644 .claude/skills/implement-task/SKILL.md create mode 100644 .claude/skills/use-modern-cue/SKILL.md create mode 100644 .claude/skills/use-modern-go/SKILL.md create mode 100644 apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/.env.example create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/Dockerfile create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/nest-cli.json create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/package-lock.json create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/package.json create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/prisma.config.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/prisma/migrations/migration_lock.toml create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/src/app.controller.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/src/app.module.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/src/app.service.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.module.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.build.json create mode 100644 apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.json create mode 100644 apps/portal/examples/nestjs-prisma-template/template.yaml create mode 100644 docs/manual-verification-guide.md create mode 100644 implementation_plan.md create mode 100644 task.md create mode 100644 walkthrough.md diff --git a/.claude/skills/implement-task/SKILL.md b/.claude/skills/implement-task/SKILL.md new file mode 100644 index 0000000..30e8753 --- /dev/null +++ b/.claude/skills/implement-task/SKILL.md @@ -0,0 +1,137 @@ +--- +name: implement-task +description: Implementation workflow for the Helios Platform operator. Use when implementing features, fixing bugs, or making any code changes. Enforces best practices, testing, and verification. +--- + +# Implementation Task Workflow + +## Detected Stack + +!`grep -rh "^go " --include="go.mod" . 2>/dev/null | head -1 | grep . && grep -rh 'language.*version' --include="module.cue" cue.mod/ cue/cue.mod/ 2>/dev/null | head -1 | grep . || echo "unknown stack"` + +## Workflow + +When asked to implement a task, follow this exact sequence. Do NOT skip steps. + +--- + +### 1. Understand & Plan + +- Read and understand the full scope of the task +- Identify all files that need to change +- Check existing code patterns in the codebase — follow them +- If the task is non-trivial (>3 files), create a brief plan and get user approval before coding + +### 2. Implement with Best Practices + +**Code quality principles — apply ALL of these:** + +- **SOLID** — Single responsibility, open/closed, Liskov substitution, interface segregation, dependency inversion +- **DRY** — Don't repeat yourself. Extract reusable functions, constants, and definitions +- **KISS** — Keep it simple. Prefer clarity over cleverness +- **YAGNI** — Don't build what isn't needed yet +- **Separation of Concerns** — Each module/file/function has one clear purpose +- **Defensive Programming** — Validate inputs, handle edge cases, never swallow errors +- **Fail Fast** — Return errors immediately, don't accumulate them silently + +**Go-specific (use `use-modern-go` skill):** + +- Use modern Go idioms: `any`, `cmp.Or`, `slices`, `maps`, `errors.Is/As` +- Proper error wrapping with `fmt.Errorf("context: %w", err)` +- Table-driven tests with descriptive names +- Context propagation for cancellation and timeouts +- Meaningful variable/function names — no single-letter names outside loops +- Comments on exported types and functions (godoc convention) + +**CUE-specific (use `use-modern-cue` skill):** + +- Use `#` definitions for schemas, `_` for hidden fields, `let` for intermediates +- Validate with constraints, not imperative logic +- Follow the module's established patterns (registries, builders, etc.) +- File-level `@extern(embed)` when using `@embed` + +**Non-functional requirements:** + +- **Performance** — avoid unnecessary allocations, use efficient algorithms +- **Reliability** — handle all error paths, use retries where appropriate +- **Observability** — add structured logging at key decision points +- **Security** — no hardcoded secrets, validate all external input + +### 3. Test Everything + +After implementation, run ALL of these. Do NOT stop at the first passing test. + +```sh +# From apps/operator/ directory: + +# 1. Compilation check — must pass cleanly +go build ./... + +# 2. Static analysis +go vet ./... + +# 3. Full test suite with envtest (real K8s API server) +make test + +# 4. Build the operator binary +make build + +# 5. Verify CUE evaluation (if CUE files changed) +# Run CUE-related tests which validate CUE rendering +go test ./internal/cue/... -v -count=1 +``` + +**If any test fails:** +1. Read the error output carefully +2. Fix the root cause (not symptoms) +3. Re-run ALL tests, not just the one that failed +4. If a fix is complex or risky, explain the issue and ask the user before proceeding + +**If a dependency/infrastructure issue prevents testing:** +1. Document what couldn't be tested and why +2. Explain the risk to the user +3. Suggest how to test manually + +### 4. Verify & Report + +After all tests pass, verify: + +- [ ] `go build ./...` — compiles cleanly +- [ ] `go vet ./...` — no issues +- [ ] `make test` — all tests pass with envtest +- [ ] `make build` — operator binary builds +- [ ] No warnings in any output +- [ ] CRD manifests regenerated if API types changed (`controller-gen` runs in `make build`) + +**Report to user:** +- What was implemented (brief) +- What was tested (specific results) +- Any caveats or follow-up items + +--- + +## Rollback Strategy + +If something doesn't work after implementation: + +1. Identify the specific change that broke things +2. Revert that change only (not everything) +3. Re-run all tests to confirm the rollback works +4. Explain what went wrong and propose an alternative approach + +--- + +## Project-Specific Context + +**Directory structure:** +- `apps/operator/` — Go operator (controllers, CUE engine, GitOps) +- `cue/` — CUE definitions (schemas, tekton, components, traits, engine) +- `apps/operator/internal/cue/` — Go↔CUE bridge (engine.go, tekton.go) +- `apps/operator/internal/controller/` — Kubernetes controllers +- `apps/operator/internal/gitops/` — Git operations + +**Key patterns:** +- CUE handles resource generation (Tasks, Pipeline, Triggers, Ingress) +- Go handles runtime resources (PipelineRun, RBAC, ArgoCD Application) +- Controller reconciles HeliosApp CRD → CUE rendering → GitOps sync → ArgoCD +- Tests use envtest (real K8s API) + CUE engine unit tests + E2E validation tests diff --git a/.claude/skills/use-modern-cue/SKILL.md b/.claude/skills/use-modern-cue/SKILL.md new file mode 100644 index 0000000..9c6d0ee --- /dev/null +++ b/.claude/skills/use-modern-cue/SKILL.md @@ -0,0 +1,361 @@ +--- +name: use-modern-cue +description: Apply modern CUE language syntax guidelines based on project's CUE version. Use when user asks for modern CUE code guidelines. +--- + +# Modern CUE Guidelines + +## Detected CUE Version + +!`grep -rh '"language"' --include="module.cue" cue.mod/ 2>/dev/null | grep -oP 'version:\s*"v?\K[^"]+' | head -1 | grep . || cue version 2>/dev/null | head -1 | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1 | grep . || echo unknown` + +## How to Use This Skill + +DO NOT search for module.cue files or try to detect the version yourself. Use ONLY the version shown above. + +**If version detected (not "unknown"):** + +- Say: "This project is using CUE X.XX.X, so I'll stick to modern CUE best practices and freely use language features up to and including this version. If you'd prefer a different target version, just let me know." +- Do NOT list features, do NOT ask for confirmation + +**If version is "unknown":** + +- Say: "Could not detect CUE version in this repository" +- Use AskUserQuestion: "Which CUE version should I target?" → [0.14] / [0.15] / [0.16] + +**When writing CUE code**, use ALL features from this document up to the target version: + +- Prefer modern patterns over deprecated/legacy ones +- Never use features from newer CUE versions than the target +- Always follow the best practices and avoid anti-patterns listed below + +--- + +## Core CUE Principles + +### Types Are Values + +CUE unifies types and values into a single lattice. A field can hold a type constraint, a concrete value, or something in between. + +```cue +port: int // type constraint +port: >0 & <65536 // narrower constraint +port: 8080 // concrete value +``` + +### Unification (`&`) + +Fundamental, commutative, associative, and idempotent — order never matters. + +```cue +a: {name: string, port: int} +a: {name: "web", replicas: 1} +// Result: a: {name: "web", port: int, replicas: 1} +``` + +### Definitions (`#`) and Hidden Definitions (`_#`) + +`#` defines schemas — closed structs, NOT emitted in output. `_#` for package-internal schemas. + +```cue +#Service: { + name: string + port: int & >0 & <=65535 + replicas: int | *1 +} + +myService: #Service & {name: "api", port: 8080} + +_#internalConfig: {debug: bool | *false} +``` + +### Hidden Fields (`_`) + +Not exported, exempt from closed struct rules. Use for internal values. + +```cue +_basePort: 8000 +services: { + api: {port: _basePort} + grpc: {port: _basePort + 1000} +} +``` + +### Disjunctions (`|`), Defaults (`*`), Optional (`?`), Required (`!`) + +```cue +#Deployment: { + name!: string // MUST be specified + protocol: "HTTP" | "HTTPS" | *"HTTPS" // enum with default + replicas?: int & >0 // MAY be specified + annotations?: {[string]: string} +} +``` + +### Closed vs Open Structs + +Definitions are closed by default. Add `...` to allow additional fields. + +```cue +#Strict: {name: string, port: int} // no extra fields +#Flexible: {name: string, port: int, ...} // extra fields allowed +``` + +### Pattern Constraints, Comprehensions, Interpolation, `let` + +```cue +// Pattern constraint: all string-keyed fields must satisfy #Service +[string]: #Service + +// List comprehension +ports: [for s in services {s.port}] + +// Field comprehension with conditional +rendered: { + for name, svc in services { + (name): {image: "reg/\(name):latest", port: svc.port} + } +} + +// Conditional field inclusion +if env == "prod" {replicas: 3} + +// let for intermediate computation (not emitted) +let _fullName = "app-\(name)" +metadata: labels: app: _fullName +``` + +### Validators and Constraints + +```cue +import "strings" + +#Label: { + key: string & =~"^[a-z][a-z0-9-]*$" + value: strings.MinRunes(1) & strings.MaxRunes(63) +} +#Port: int & >0 & <=65535 +``` + +### Builtin Functions + +```cue +len("hello") // 5 +close({a: 1}) // closes an open struct +and([int, >0, <100]) // unifies: int & >0 & <100 +or(["a", "b", "c"]) // disjunction: "a" | "b" | "c" +``` + +--- + +## Standard Library (Key Packages) + +```cue +import ("strings"; "list"; "regexp"; "math"; "encoding/json"; "encoding/yaml"; "struct"; "net"; "strconv") + +// strings +strings.Join(["a","b"], ",") strings.Split("a,b", ",") strings.Contains("ab", "a") +strings.HasPrefix/HasSuffix strings.Replace strings.ToUpper/ToLower strings.TrimSpace +strings.MinRunes(1) strings.MaxRunes(63) + +// list +list.Concat([[1,2],[3,4]]) list.Repeat([0],3) list.Contains([1,2],2) +list.Sort([3,1,2], list.Ascending) list.FlattenN list.UniqueItems +list.MinItems(1) list.MaxItems(10) + +// regexp +regexp.Match("^[a-z]+$", "hello") regexp.Find regexp.FindAll regexp.ReplaceAll + +// math +math.Floor math.Ceil math.Abs math.Pow + +// encoding +json.Marshal({a:1}) json.Unmarshal(str) json.Validate(str, schema) +yaml.Marshal yaml.Validate + +// struct +struct.MinFields(1) struct.MaxFields(10) + +// net +net.IPv4 net.IP net.FQDN +net.InCIDR net.ParseCIDR net.CompareIP // v0.16+ + +// strconv +strconv.ParseNumber // v0.16+: parse CUE numbers like "1Ki" +``` + +--- + +## Breaking Changes by Version + +| Version | Breaking Change | +|---------|----------------| +| **v0.9** | `language.version` REQUIRED in `module.cue` | +| **v0.11** | List arithmetic (`+`, `*`) removed → use `list.Concat`, `list.Repeat` | +| **v0.12** | `cue.Value.Decode` returns `int64` (not `int`) for CUE integers | +| **v0.12** | `@embed` stable — requires `@extern(embed)` file attribute | +| **v0.13** | Removed deprecated `cue.Runtime` methods | +| **v0.14** | Multiline strings require trailing newline | +| **v0.14** | Custom errors: `error("msg")` builtin | +| **v0.14** | JSON Schema generation: `cue def --out jsonschema` | +| **v0.16** | Multiline trailing newline **strictly enforced** | +| **v0.16** | `cue` commands inside `cue.mod/` directory now fail | +| **v0.16** | `cue mod publish` no longer ignores sub-dirs with `go.mod` | +| **v0.16** | `#"""#` accepted as string literal for `"` | +| **v0.16** | `cmdreferencepkg` experiment stabilized (always on) | +| **v0.16** | Removed deprecated: `cue/ast.Node.Comments`, `cue/parser.FromVersion`, `cue.Instance.Eval` | + +## Key Features by Version + +| Version | Feature | +|---------|---------| +| **v0.10** | `@if(tag)` conditional file inclusion, `@tag(name)` value injection, `@ignore()` | +| **v0.12** | File embedding: `@extern(embed)` + `@embed(file="x.json")` | +| **v0.13** | `cue refactor imports`, `cue mod mirror`, keywords as field labels | +| **v0.14** | `error()` builtin, `cue def --out jsonschema`, `cue fix --exp`, `cue help experiments` | +| **v0.15** | Full LSP support (go-to-def, find-refs, rename, completion, hover) | +| **v0.16** | `net.InCIDR/ParseCIDR/CompareIP`, `strconv.ParseNumber`, `tool/file.Symlink` | +| **v0.16** | Up to 80% faster evaluation, 60% less memory, embedded JSON/YAML LSP support | + +--- + +## Module & Package Best Practices + +### Module Declaration (v0.9+, REQUIRED) + +```cue +// cue.mod/module.cue +module: "github.com/myorg/myproject@v0" +language: version: "v0.16.0" + +deps: { + "github.com/some/dep@v0": v: "v0.1.0" +} +``` + +**ALWAYS** declare `language.version`. Never omit it. + +### Import Best Practices + +```cue +import ( + "strings" // built-in (no domain) + "list" + "github.com/myorg/project/schema" // user-defined (fully-qualified) + k8s "k8s.io/api/apps/v1" // named import for conflicts +) +``` + +- ALWAYS use absolute import paths (no relative imports) +- Group built-in and user-defined imports separately + +### Package Organization + +- One package per directory +- `package` declaration at top of every file +- Definitions in same package accessible across files without imports +- Hidden fields (`_`) scoped to package — not externally accessible +- Do NOT place CUE code inside `cue.mod/` (v0.16+: this errors) + +### File Embedding (v0.12+) + +```cue +@extern(embed) +package config + +data: _ @embed(file="config.json") +readme: string @embed(file="README.md", type=text) +cert: bytes @embed(file="cert.pem", type=binary) +templates: _ @embed(glob="templates/*.json") +optional: _ @embed(glob="overrides/*.yaml", allowEmptyGlob) +``` + +ALWAYS use `@extern(embed)` at file level. Only files within the same CUE module can be embedded. + +--- + +## CLI Quick Reference + +```sh +cue mod init github.com/myorg/project@v0 # init module +cue mod tidy # clean deps +cue fmt ./... # format +cue eval ./... # evaluate +cue eval -c ./... # require concreteness +cue vet data.yaml schema.cue -d '#Schema' # validate +cue export ./... --out json # export JSON +cue export ./... --out yaml # export YAML +cue def --out jsonschema ./... # JSON Schema (v0.14+) +cue fix ./... # fix deprecated syntax +cue get go k8s.io/api/apps/v1 # generate CUE from Go +cue refactor imports old/path new/path # refactor imports (v0.13+) +``` + +--- + +## Anti-Patterns + +### ❌ List arithmetic (removed in v0.11) + +```cue +// BAD // GOOD +combined: [1,2] + [3,4] import "list" +repeated: [0] * 3 combined: list.Concat([[1,2],[3,4]]) + repeated: list.Repeat([0], 3) +``` + +### ❌ Missing `language.version` + +```cue +// BAD // GOOD +module: "example.com/foo@v0" module: "example.com/foo@v0" + language: version: "v0.16.0" +``` + +### ❌ Overly permissive types + +```cue +// BAD — defeats CUE's purpose // GOOD — constrain narrowly +config: _ config: #AppConfig +``` + +### ❌ Concrete values in definitions + +```cue +// BAD — mixes data and schema // GOOD — definitions are constraints +#Service: {name: "my-svc"} #Service: {name: string & strings.MinRunes(1)} +``` + +### ❌ Repeated constraints + +```cue +// BAD // GOOD +svcA: port: int & >0 & <=65535 #Port: int & >0 & <=65535 +svcB: port: int & >0 & <=65535 svcA: port: #Port + svcB: port: #Port +``` + +### ❌ `@embed` without `@extern(embed)` (v0.12+) + +```cue +// BAD // GOOD +package config @extern(embed) +data: _ @embed(file="d.json") package config + data: _ @embed(file="d.json") +``` + +### ❌ Relative imports + +```cue +// BAD // GOOD +import "./utils" import "github.com/myorg/project/utils" +``` + +### ❌ Multiline strings without trailing newline (v0.14+) + +```cue +// BAD // GOOD +msg: """ msg: """ + Hello""" Hello + """ +``` diff --git a/.claude/skills/use-modern-go/SKILL.md b/.claude/skills/use-modern-go/SKILL.md new file mode 100644 index 0000000..3755a1a --- /dev/null +++ b/.claude/skills/use-modern-go/SKILL.md @@ -0,0 +1,291 @@ +--- +name: use-modern-go +description: Apply modern Go syntax guidelines based on project's Go version. Use when user ask for modern Go code guidelines. +--- + +# Modern Go Guidelines + +## Detected Go Version + +!`grep -rh "^go " --include="go.mod" . 2>/dev/null | cut -d' ' -f2 | sort | uniq -c | sort -nr | head -1 | xargs | cut -d' ' -f2 | grep . || echo unknown` + +## How to Use This Skill + +DO NOT search for go.mod files or try to detect the version yourself. Use ONLY the version shown above. + +**If version detected (not "unknown"):** +- Say: "This project is using Go X.XX, so I’ll stick to modern Go best practices and freely use language features up to and including this version. If you’d prefer a different target version, just let me know." +- Do NOT list features, do NOT ask for confirmation + +**If version is "unknown":** +- Say: "Could not detect Go version in this repository" +- Use AskUserQuestion: "Which Go version should I target?" → [1.23] / [1.24] / [1.25] / [1.26] + +**When writing Go code**, use ALL features from this document up to the target version: +- Prefer modern built-ins and packages (`slices`, `maps`, `cmp`) over legacy patterns +- Never use features from newer Go versions than the target +- Never use outdated patterns when a modern alternative is available + +--- + +## Features by Go Version + +### Go 1.0+ + +- `time.Since`: `time.Since(start)` instead of `time.Now().Sub(start)` + +### Go 1.8+ + +- `time.Until`: `time.Until(deadline)` instead of `deadline.Sub(time.Now())` + +### Go 1.13+ + +- `errors.Is`: `errors.Is(err, target)` instead of `err == target` (works with wrapped errors) + +### Go 1.18+ + +- `any`: Use `any` instead of `interface{}` +- `bytes.Cut`: `before, after, found := bytes.Cut(b, sep)` instead of Index+slice +- `strings.Cut`: `before, after, found := strings.Cut(s, sep)` + +### Go 1.19+ + +- `fmt.Appendf`: `buf = fmt.Appendf(buf, "x=%d", x)` instead of `[]byte(fmt.Sprintf(...))` +- `atomic.Bool`/`atomic.Int64`/`atomic.Pointer[T]`: Type-safe atomics instead of `atomic.StoreInt32` + +```go +var flag atomic.Bool +flag.Store(true) +if flag.Load() { ... } + +var ptr atomic.Pointer[Config] +ptr.Store(cfg) +``` + +### Go 1.20+ + +- `strings.Clone`: `strings.Clone(s)` to copy string without sharing memory +- `bytes.Clone`: `bytes.Clone(b)` to copy byte slice +- `strings.CutPrefix/CutSuffix`: `if rest, ok := strings.CutPrefix(s, "pre:"); ok { ... }` +- `errors.Join`: `errors.Join(err1, err2)` to combine multiple errors +- `context.WithCancelCause`: `ctx, cancel := context.WithCancelCause(parent)` then `cancel(err)` +- `context.Cause`: `context.Cause(ctx)` to get the error that caused cancellation + +### Go 1.21+ + +**Built-ins:** +- `min`/`max`: `max(a, b)` instead of if/else comparisons +- `clear`: `clear(m)` to delete all map entries, `clear(s)` to zero slice elements + +**slices package:** +- `slices.Contains`: `slices.Contains(items, x)` instead of manual loops +- `slices.Index`: `slices.Index(items, x)` returns index (-1 if not found) +- `slices.IndexFunc`: `slices.IndexFunc(items, func(item T) bool { return item.ID == id })` +- `slices.SortFunc`: `slices.SortFunc(items, func(a, b T) int { return cmp.Compare(a.X, b.X) })` +- `slices.Sort`: `slices.Sort(items)` for ordered types +- `slices.Max`/`slices.Min`: `slices.Max(items)` instead of manual loop +- `slices.Reverse`: `slices.Reverse(items)` instead of manual swap loop +- `slices.Compact`: `slices.Compact(items)` removes consecutive duplicates in-place +- `slices.Clip`: `slices.Clip(s)` removes unused capacity +- `slices.Clone`: `slices.Clone(s)` creates a copy + +**maps package:** +- `maps.Clone`: `maps.Clone(m)` instead of manual map iteration +- `maps.Copy`: `maps.Copy(dst, src)` copies entries from src to dst +- `maps.DeleteFunc`: `maps.DeleteFunc(m, func(k K, v V) bool { return condition })` + +**sync package:** +- `sync.OnceFunc`: `f := sync.OnceFunc(func() { ... })` instead of `sync.Once` + wrapper +- `sync.OnceValue`: `getter := sync.OnceValue(func() T { return computeValue() })` + +**context package:** +- `context.AfterFunc`: `stop := context.AfterFunc(ctx, cleanup)` runs cleanup on cancellation +- `context.WithTimeoutCause`: `ctx, cancel := context.WithTimeoutCause(parent, d, err)` +- `context.WithDeadlineCause`: Similar with deadline instead of duration + +### Go 1.22+ + +**Loops:** +- `for i := range n`: `for i := range len(items)` instead of `for i := 0; i < len(items); i++` +- Loop variables are now safe to capture in goroutines (each iteration has its own copy) + +**cmp package:** +- `cmp.Or`: `cmp.Or(flag, env, config, "default")` returns first non-zero value + +```go +// Instead of: +name := os.Getenv("NAME") +if name == "" { + name = "default" +} +// Use: +name := cmp.Or(os.Getenv("NAME"), "default") +``` + +**reflect package:** +- `reflect.TypeFor`: `reflect.TypeFor[T]()` instead of `reflect.TypeOf((*T)(nil)).Elem()` + +**net/http:** +- Enhanced `http.ServeMux` patterns: `mux.HandleFunc("GET /api/{id}", handler)` with method and path params +- `r.PathValue("id")` to get path parameters + +### Go 1.23+ + +- `maps.Keys(m)` / `maps.Values(m)` return iterators +- `slices.Collect(iter)` not manual loop to build slice from iterator +- `slices.Sorted(iter)` to collect and sort in one step + +```go +keys := slices.Collect(maps.Keys(m)) // not: for k := range m { keys = append(keys, k) } +sortedKeys := slices.Sorted(maps.Keys(m)) // collect + sort +for k := range maps.Keys(m) { process(k) } // iterate directly +``` + +**time package** + +- `time.Tick`: Use `time.Tick` freely — as of Go 1.23, the garbage collector can recover unreferenced tickers, even if they haven't been stopped. The Stop method is no longer necessary to help the garbage collector. There is no longer any reason to prefer NewTicker when Tick will do. + +### Go 1.24+ + +- `t.Context()` not `context.WithCancel(context.Background())` in tests. + ALWAYS use t.Context() when a test function needs a context. + +Before: +```go +func TestFoo(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + result := doSomething(ctx) +} +``` +After: +```go +func TestFoo(t *testing.T) { + ctx := t.Context() + result := doSomething(ctx) +} +``` + +- `omitzero` not `omitempty` in JSON struct tags. + ALWAYS use omitzero for time.Duration, time.Time, structs, slices, maps. + +Before: +```go +type Config struct { + Timeout time.Duration `json:"timeout,omitempty"` // doesn't work for Duration! +} +``` +After: +```go +type Config struct { + Timeout time.Duration `json:"timeout,omitzero"` +} +``` + +- `b.Loop()` not `for i := 0; i < b.N; i++` in benchmarks. + ALWAYS use b.Loop() for the main loop in benchmark functions. + +Before: +```go +func BenchmarkFoo(b *testing.B) { + for i := 0; i < b.N; i++ { + doWork() + } +} +``` +After: +```go +func BenchmarkFoo(b *testing.B) { + for b.Loop() { + doWork() + } +} +``` + +- `strings.SplitSeq` not `strings.Split` when iterating. + ALWAYS use SplitSeq/FieldsSeq when iterating over split results in a for-range loop. + +Before: +```go +for _, part := range strings.Split(s, ",") { + process(part) +} +``` +After: +```go +for part := range strings.SplitSeq(s, ",") { + process(part) +} +``` +Also: `strings.FieldsSeq`, `bytes.SplitSeq`, `bytes.FieldsSeq`. + +### Go 1.25+ + +- `wg.Go(fn)` not `wg.Add(1)` + `go func() { defer wg.Done(); ... }()`. + ALWAYS use wg.Go() when spawning goroutines with sync.WaitGroup. + +Before: +```go +var wg sync.WaitGroup +for _, item := range items { + wg.Add(1) + go func() { + defer wg.Done() + process(item) + }() +} +wg.Wait() +``` +After: +```go +var wg sync.WaitGroup +for _, item := range items { + wg.Go(func() { + process(item) + }) +} +wg.Wait() +``` + +### Go 1.26+ + +- `new(val)` not `x := val; &x` — returns pointer to any value. + Go 1.26 extends new() to accept expressions, not just types. + Type is inferred: new(0) → *int, new("s") → *string, new(T{}) → *T. + DO NOT use `x := val; &x` pattern — always use new(val) directly. + DO NOT use redundant casts like new(int(0)) — just write new(0). + Common use case: struct fields with pointer types. + +Before: +```go +timeout := 30 +debug := true +cfg := Config{ + Timeout: &timeout, + Debug: &debug, +} +``` +After: +```go +cfg := Config{ + Timeout: new(30), // *int + Debug: new(true), // *bool +} +``` + +- `errors.AsType[T](err)` not `errors.As(err, &target)`. + ALWAYS use errors.AsType when checking if error matches a specific type. + +Before: +```go +var pathErr *os.PathError +if errors.As(err, &pathErr) { + handle(pathErr) +} +``` +After: +```go +if pathErr, ok := errors.AsType[*os.PathError](err); ok { + handle(pathErr) +} +``` diff --git a/apps/operator/Makefile b/apps/operator/Makefile index 430a14c..2decfd1 100644 --- a/apps/operator/Makefile +++ b/apps/operator/Makefile @@ -242,7 +242,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.20.1 ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') #ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') -GOLANGCI_LINT_VERSION ?= v2.10.1 +GOLANGCI_LINT_VERSION ?= v2.11.3 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. diff --git a/apps/operator/config/manager/kustomization.yaml b/apps/operator/config/manager/kustomization.yaml index 9215473..f919b93 100644 --- a/apps/operator/config/manager/kustomization.yaml +++ b/apps/operator/config/manager/kustomization.yaml @@ -4,3 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller + newName: helios-operator + newTag: local diff --git a/apps/operator/config/rbac/role.yaml b/apps/operator/config/rbac/role.yaml index ef8fe77..a0f6593 100644 --- a/apps/operator/config/rbac/role.yaml +++ b/apps/operator/config/rbac/role.yaml @@ -47,6 +47,19 @@ rules: - apps resources: - deployments + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses verbs: - create - delete diff --git a/apps/operator/go.mod b/apps/operator/go.mod index 8d5f82f..85d77d0 100644 --- a/apps/operator/go.mod +++ b/apps/operator/go.mod @@ -1,9 +1,9 @@ module github.com/helios-platform-team/helios-platform/apps/operator -go 1.26 +go 1.26.1 require ( - cuelang.org/go v0.15.4 + cuelang.org/go v0.16.0 github.com/go-git/go-billy/v5 v5.8.0 github.com/go-git/go-git/v5 v5.17.0 github.com/onsi/ginkgo/v2 v2.28.1 @@ -11,28 +11,28 @@ require ( k8s.io/api v0.35.2 k8s.io/apimachinery v0.35.2 k8s.io/client-go v0.35.2 - sigs.k8s.io/controller-runtime v0.23.1 + sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/yaml v1.6.0 ) require ( cel.dev/expr v0.24.0 // indirect - cuelabs.dev/go/oci/ociregistry v0.0.0-20250722084951-074d06050084 // indirect - dario.cat/mergo v1.0.0 // indirect + cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 // indirect + dario.cat/mergo v1.0.2 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/ProtonMail/go-crypto v1.4.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudflare/circl v1.6.1 // indirect + github.com/cloudflare/circl v1.6.3 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/emicklei/proto v1.14.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/emicklei/proto v1.14.3 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -42,24 +42,34 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/swag v0.25.5 // indirect + github.com/go-openapi/swag/cmdutils v0.25.5 // indirect + github.com/go-openapi/swag/conv v0.25.5 // indirect + github.com/go-openapi/swag/fileutils v0.25.5 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect + github.com/go-openapi/swag/jsonutils v0.25.5 // indirect + github.com/go-openapi/swag/loading v0.25.5 // indirect + github.com/go-openapi/swag/mangling v0.25.5 // indirect + github.com/go-openapi/swag/netutils v0.25.5 // indirect + github.com/go-openapi/swag/stringutils v0.25.5 // indirect + github.com/go-openapi/swag/typeutils v0.25.5 // indirect + github.com/go-openapi/swag/yamlutils v0.25.5 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.26.0 // indirect - github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect + github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/kevinburke/ssh_config v1.6.0 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect @@ -67,17 +77,17 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.66.1 // indirect - github.com/prometheus/procfs v0.16.1 // indirect - github.com/protocolbuffers/txtpbfmt v0.0.0-20251016062345-16587c79cd91 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.20.1 // indirect + github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.3.1 // indirect - github.com/spf13/cobra v1.10.1 // indirect + github.com/sergi/go-diff v1.4.0 // indirect + github.com/skeema/knownhosts v1.3.2 // indirect + github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -93,36 +103,35 @@ require ( go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.47.0 // indirect + golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.49.0 // indirect - golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.39.0 // indirect - golang.org/x/text v0.33.0 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.41.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.43.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/grpc v1.72.2 // indirect - google.golang.org/protobuf v1.36.8 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.35.0 // indirect - k8s.io/apiserver v0.35.0 // indirect - k8s.io/component-base v0.35.0 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + k8s.io/apiextensions-apiserver v0.35.2 // indirect + k8s.io/apiserver v0.35.2 // indirect + k8s.io/component-base v0.35.2 // indirect + k8s.io/klog/v2 v2.140.0 // indirect + k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf // indirect + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) diff --git a/apps/operator/go.sum b/apps/operator/go.sum index c0fa712..fd8b3bc 100644 --- a/apps/operator/go.sum +++ b/apps/operator/go.sum @@ -1,18 +1,18 @@ cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= -cuelabs.dev/go/oci/ociregistry v0.0.0-20250722084951-074d06050084 h1:4k1yAtPvZJZQTu8DRY8muBo0LHv6TqtrE0AO5n6IPYs= -cuelabs.dev/go/oci/ociregistry v0.0.0-20250722084951-074d06050084/go.mod h1:4WWeZNxUO1vRoZWAHIG0KZOd6dA25ypyWuwD3ti0Tdc= -cuelang.org/go v0.15.4 h1:lrkTDhqy8dveHgX1ZLQ6WmgbhD8+rXa0fD25hxEKYhw= -cuelang.org/go v0.15.4/go.mod h1:NYw6n4akZcTjA7QQwJ1/gqWrrhsN4aZwhcAL0jv9rZE= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 h1:Zh+Ur3OsoWpvALHPLT45nOekHkgOt+IOfutBbPqM17I= +cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819/go.mod h1:WjmQxb+W6nVNCgj8nXrF24lIz95AHwnSl36tpjDZSU8= +cuelang.org/go v0.16.0 h1:mmt9SL/IzfSIiBKuP5wxdO4xLjvIHr3urpbjCDdMV5U= +cuelang.org/go v0.16.0/go.mod h1:4veMX+GpsK0B91b1seGXoozG80LJCczvG1M1Re/knxo= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= -github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ= +github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= @@ -27,23 +27,22 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/proto v1.14.2 h1:wJPxPy2Xifja9cEMrcA/g08art5+7CGJNFNk35iXC1I= -github.com/emicklei/proto v1.14.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/proto v1.14.3 h1:zEhlzNkpP8kN6utonKMzlPfIvy82t5Kb9mufaJxSe1Q= +github.com/emicklei/proto v1.14.3/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= @@ -79,14 +78,40 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU= +github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA= +github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c= +github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= +github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= +github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= +github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= +github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= +github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= +github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= +github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= +github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= +github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU= +github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14= +github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= +github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= +github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= +github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= +github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= +github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -101,15 +126,15 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw= +github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= @@ -118,18 +143,17 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY= +github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -140,8 +164,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= @@ -166,8 +188,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= +github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -176,22 +198,22 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= -github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/protocolbuffers/txtpbfmt v0.0.0-20251016062345-16587c79cd91 h1:s1LvMaU6mVwoFtbxv/rCZKE7/fwDmDY684FfUe4c1Io= -github.com/protocolbuffers/txtpbfmt v0.0.0-20251016062345-16587c79cd91/go.mod h1:JSbkp0BviKovYYt9XunS95M3mLPibE9bGg+Y95DsEEY= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94 h1:2PC6Ql3jipz1KvBlqUHjjk6v4aMwE86mfDu1XMH0LR8= +github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94/go.mod h1:JSbkp0BviKovYYt9XunS95M3mLPibE9bGg+Y95DsEEY= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= -github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= +github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -248,53 +270,53 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= -golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -312,31 +334,31 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw= k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60= -k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= -k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpApE29c0= +k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU= k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4= -k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds= +k8s.io/apiserver v0.35.2 h1:rb52v0CZGEL0FkhjS+I6jHflAp7fZ4MIaKcEHX7wmDk= +k8s.io/apiserver v0.35.2/go.mod h1:CROJUAu0tfjZLyYgSeBsBan2T7LUJGh0ucWwTCSSk7g= k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o= k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g= -k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94= -k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/component-base v0.35.2 h1:btgR+qNrpWuRSuvWSnQYsZy88yf5gVwemvz0yw79pGc= +k8s.io/component-base v0.35.2/go.mod h1:B1iBJjooe6xIJYUucAxb26RwhAjzx0gHnqO9htWIX+0= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf h1:btPscg4cMql0XdYK2jLsJcNEKmACJz8l+U7geC06FiM= +k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE= -sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= +sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= +sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs= -sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index e363578..e9f81c8 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -451,6 +451,105 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app return nil } +// databaseEnvVarNames lists the env var names injected by the operator +// for database credential secret injection. +var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} + +// InjectDatabaseEnvVars patches a Deployment's first container to include +// DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. +// The function is idempotent — if the env vars already exist with the correct +// secretKeyRef, no changes are made and it returns false. +func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { + if len(deploy.Spec.Template.Spec.Containers) == 0 { + return false + } + + container := &deploy.Spec.Template.Spec.Containers[0] + + // Build a set of existing env var names for fast lookup. + existingEnvs := make(map[string]bool, len(container.Env)) + for _, ev := range container.Env { + existingEnvs[ev.Name] = true + } + + changed := false + for _, envName := range databaseEnvVarNames { + if existingEnvs[envName] { + continue + } + container.Env = append(container.Env, corev1.EnvVar{ + Name: envName, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: envName, + }, + }, + }) + changed = true + } + + return changed +} + +// reconcileDatabaseSecretInjection patches live Deployments (deployed by ArgoCD) +// to inject DB_HOST, DB_USER, DB_PASS env vars from the operator-managed Secret. +// This runs AFTER database secrets and instances are provisioned so that the +// Secret already exists when the Deployment references it. +func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping secret injection") + return nil + } + + for _, dbTrait := range dbTraits { + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + deployName := dbTrait.ComponentName + + // Fetch the live Deployment (created by ArgoCD via GitOps). + deploy := &appsv1.Deployment{} + err := r.Get(ctx, types.NamespacedName{ + Name: deployName, + Namespace: app.Namespace, + }, deploy) + if err != nil { + if errors.IsNotFound(err) { + // Deployment may not exist yet (ArgoCD hasn't synced). + // This is expected — we'll inject on the next reconcile. + log.Info("Deployment not found yet, will inject on next reconcile", + "component", dbTrait.ComponentName, + "deployment", deployName) + continue + } + return fmt.Errorf("failed to get Deployment %s: %w", deployName, err) + } + + // Inject env vars if not already present. + if !InjectDatabaseEnvVars(deploy, secretName) { + log.V(1).Info("Database env vars already injected, skipping", + "component", dbTrait.ComponentName, + "deployment", deployName) + continue + } + + if err := r.Update(ctx, deploy); err != nil { + return fmt.Errorf("failed to update Deployment %s with database env vars: %w", deployName, err) + } + + log.Info("Successfully injected database env vars into Deployment", + "component", dbTrait.ComponentName, + "deployment", deployName, + "secret", secretName) + } + + return nil +} + // GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. // The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and // uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 42e09c3..cb6fde1 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -247,7 +247,7 @@ func TestGetDatabaseHost(t *testing.T) { func TestExtractDatabaseTraits(t *testing.T) { // Create a HeliosApp with database traits - dbProps := map[string]interface{}{ + dbProps := map[string]any{ "dbType": "postgres", "dbName": "mydb", "version": "16", @@ -321,7 +321,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { _ = corev1.AddToScheme(scheme) _ = appv1alpha1.AddToScheme(scheme) - dbProps := map[string]interface{}{ + dbProps := map[string]any{ "dbType": "postgres", "dbName": "mydb", "version": "16", @@ -738,7 +738,7 @@ func TestGenerateDatabaseService(t *testing.T) { func TestReconcileDatabaseInstance(t *testing.T) { - dbProps := map[string]interface{}{ + dbProps := map[string]any{ "dbType": "postgres", "dbName": "my_custom_db", "version": "16", @@ -889,7 +889,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { }) t.Run("SkipsNonPostgresType", func(t *testing.T) { - redisProps := map[string]interface{}{ + redisProps := map[string]any{ "dbType": "redis", "version": "7", } @@ -948,3 +948,291 @@ func TestReconcileDatabaseInstance(t *testing.T) { } }) } + +func TestInjectDatabaseEnvVars(t *testing.T) { + t.Run("InjectsAllEnvVars", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "api-server", + Image: "myregistry/api:v1", + Env: []corev1.EnvVar{ + {Name: "PORT", Value: "3000"}, + }, + }, + }, + }, + }, + }, + } + + changed := InjectDatabaseEnvVars(deploy, "api-server-db-secret") + if !changed { + t.Fatal("Expected InjectDatabaseEnvVars to return true (changed)") + } + + container := deploy.Spec.Template.Spec.Containers[0] + // Should have PORT + DB_HOST + DB_USER + DB_PASS = 4 + if len(container.Env) != 4 { + t.Fatalf("Expected 4 env vars, got %d", len(container.Env)) + } + + expectedEnvs := map[string]string{ + "DB_HOST": "DB_HOST", + "DB_USER": "DB_USER", + "DB_PASS": "DB_PASS", + } + for _, env := range container.Env { + if expectedKey, ok := expectedEnvs[env.Name]; ok { + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Errorf("Env %s should reference a secret", env.Name) + continue + } + if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + t.Errorf("Env %s: expected secret name %q, got %q", + env.Name, "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + } + if env.ValueFrom.SecretKeyRef.Key != expectedKey { + t.Errorf("Env %s: expected secret key %q, got %q", + env.Name, expectedKey, env.ValueFrom.SecretKeyRef.Key) + } + delete(expectedEnvs, env.Name) + } + } + if len(expectedEnvs) > 0 { + t.Errorf("Missing expected env vars: %v", expectedEnvs) + } + }) + + t.Run("Idempotent", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "api-server", Image: "myregistry/api:v1"}, + }, + }, + }, + }, + } + + // First injection + changed := InjectDatabaseEnvVars(deploy, "api-server-db-secret") + if !changed { + t.Fatal("Expected first injection to report changes") + } + firstCount := len(deploy.Spec.Template.Spec.Containers[0].Env) + + // Second injection — should be idempotent + changed = InjectDatabaseEnvVars(deploy, "api-server-db-secret") + if changed { + t.Error("Expected second injection to report no changes (idempotent)") + } + secondCount := len(deploy.Spec.Template.Spec.Containers[0].Env) + if firstCount != secondCount { + t.Errorf("Env var count changed after idempotent call: %d → %d", firstCount, secondCount) + } + }) + + t.Run("NoContainers", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{}, + }, + }, + }, + } + + changed := InjectDatabaseEnvVars(deploy, "test-secret") + if changed { + t.Error("Expected no changes for Deployment with no containers") + } + }) +} + +func TestReconcileDatabaseSecretInjection(t *testing.T) { + dbProps := map[string]any{ + "dbType": "postgres", + "dbName": "mydb", + "version": "16", + } + dbPropsJSON, _ := json.Marshal(dbProps) + + app := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "default", + UID: "test-uid-inject", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "api-server", + Type: "web-service", + Traits: []appv1alpha1.Trait{ + { + Type: "database", + Properties: &runtime.RawExtension{ + Raw: dbPropsJSON, + }, + }, + }, + }, + }, + }, + } + + t.Run("InjectsIntoExistingDeployment", func(t *testing.T) { + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + existingDeploy := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "api-server", + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "api-server"}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "api-server"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "api-server", + Image: "myregistry/api:v1", + Env: []corev1.EnvVar{ + {Name: "PORT", Value: "3000"}, + }, + }, + }, + }, + }, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(app, existingDeploy). + Build() + + r := &HeliosAppReconciler{ + Client: fakeClient, + Scheme: fullScheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseSecretInjection(ctx, app) + if err != nil { + t.Fatalf("reconcileDatabaseSecretInjection failed: %v", err) + } + + // Verify the Deployment was updated with DB env vars + updatedDeploy := &appsv1.Deployment{} + err = fakeClient.Get(ctx, types.NamespacedName{ + Name: "api-server", + Namespace: "default", + }, updatedDeploy) + if err != nil { + t.Fatalf("Failed to get updated Deployment: %v", err) + } + + container := updatedDeploy.Spec.Template.Spec.Containers[0] + expectedEnvNames := map[string]bool{ + "DB_HOST": false, + "DB_USER": false, + "DB_PASS": false, + } + for _, env := range container.Env { + if _, ok := expectedEnvNames[env.Name]; ok { + expectedEnvNames[env.Name] = true + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Errorf("Env %s should reference a secret", env.Name) + } else if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + t.Errorf("Env %s: expected secret name %q, got %q", + env.Name, "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + } + } + } + for name, found := range expectedEnvNames { + if !found { + t.Errorf("Expected env var %s not found in Deployment", name) + } + } + }) + + t.Run("SkipsWhenNoTraits", func(t *testing.T) { + appWithoutDB := &appv1alpha1.HeliosApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: "no-db-app", + Namespace: "default", + UID: "test-uid-no-inject", + }, + Spec: appv1alpha1.HeliosAppSpec{ + Components: []appv1alpha1.Component{ + { + Name: "frontend", + Type: "web-service", + }, + }, + }, + } + + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(appWithoutDB). + Build() + + r := &HeliosAppReconciler{ + Client: fakeClient, + Scheme: fullScheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseSecretInjection(ctx, appWithoutDB) + if err != nil { + t.Fatalf("reconcileDatabaseSecretInjection should not fail for app without database traits: %v", err) + } + }) + + t.Run("DeploymentNotFound_GracefulSkip", func(t *testing.T) { + // When Deployment doesn't exist yet (ArgoCD hasn't synced), + // the reconciler should skip without error. + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(app). + Build() + + r := &HeliosAppReconciler{ + Client: fakeClient, + Scheme: fullScheme, + } + + ctx := context.Background() + err := r.reconcileDatabaseSecretInjection(ctx, app) + if err != nil { + t.Fatalf("reconcileDatabaseSecretInjection should not fail when Deployment is missing: %v", err) + } + }) +} diff --git a/apps/operator/internal/controller/heliosapp_controller.go b/apps/operator/internal/controller/heliosapp_controller.go index f0441f2..01be8f8 100644 --- a/apps/operator/internal/controller/heliosapp_controller.go +++ b/apps/operator/internal/controller/heliosapp_controller.go @@ -127,6 +127,18 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } + // ------------------------------------------------------------------ + // PHASE 0.9: Inject Database Credentials into Backend Deployment + // Patches the live Deployment (deployed by ArgoCD) to add DB_HOST, + // DB_USER, DB_PASS env vars referencing the operator-managed Secret. + // Runs AFTER secrets and instances so the Secret already exists. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseSecretInjection(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to inject database secrets into Deployment") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret injection failed: %v", err)) + return ctrl.Result{}, err + } + // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") // This validation is for application workloads (GitOps pipeline downstream). // We run this AFTER DB provisioning so databases can come up while app is building. diff --git a/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml b/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml new file mode 100644 index 0000000..30b84ae --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml @@ -0,0 +1,35 @@ +apiVersion: app.helios.io/v1alpha1 +kind: HeliosApp +metadata: + name: ${{ values.name }} + namespace: default +spec: + owner: ${{ values.owner }} + description: "NestJS service: ${{ values.name }}" + gitRepo: ${{ values.sourceRepo }} + gitBranch: main + imageRepo: ${{ values.image }} + gitopsRepo: ${{ values.gitopsRepo }} + gitopsPath: ${{ values.name }} + pipelineName: from-code-to-cluster + webhookDomain: ${{ values.webhookUrl }} + webhookSecret: github-webhook-secret + port: ${{ values.port }} + testCommand: "npm ci && npm test" + components: + - name: ${{ values.name }} + type: web-service + properties: + image: ${{ values.image }}:latest + port: ${{ values.port }} + replicas: 1 + traits: + - type: service + properties: + port: ${{ values.port }} + - type: database + properties: + dbType: postgres + dbName: ${{ values.name | replace('-', '_') }}_db + version: "16" + storage: "1Gi" diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/.env.example b/apps/portal/examples/nestjs-prisma-template/content/source/.env.example new file mode 100644 index 0000000..17c800a --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/.env.example @@ -0,0 +1,9 @@ +# Database credentials (injected by Helios Operator) +DB_HOST=localhost +DB_USER=postgres +DB_PASS=postgres +DB_NAME=${{ values.name }}-db +DB_PORT=5432 + +# Application +PORT=${{ values.port }} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/Dockerfile b/apps/portal/examples/nestjs-prisma-template/content/source/Dockerfile new file mode 100644 index 0000000..bbc28d5 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/Dockerfile @@ -0,0 +1,40 @@ +# ---- Build Stage ---- +FROM node:24-alpine AS builder + +WORKDIR /app + +# Install dependencies +COPY package*.json ./ +RUN npm ci + +# Copy Prisma schema and config, then generate client +COPY prisma ./prisma/ +COPY prisma.config.ts ./ +RUN npx prisma generate + +# Build NestJS application +COPY . . +RUN npm run build + +# ---- Production Stage ---- +FROM node:24-alpine AS production + +WORKDIR /app + +# Install production dependencies only +COPY package*.json ./ +RUN npm ci --omit=dev + +# Copy Prisma schema, config, and generated client from builder +COPY --from=builder /app/prisma ./prisma/ +COPY --from=builder /app/prisma.config.ts ./ +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma/ +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma/ + +# Copy built application +COPY --from=builder /app/dist ./dist/ + +EXPOSE ${{ values.port }} + +# Run migrations and start the application +CMD ["npm", "run", "start:migrate:prod"] diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml b/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml new file mode 100644 index 0000000..5185c92 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: ${{ values.name }} + description: ${{ values.description }} + annotations: + github.com/project-slug: ${{ values.destination.owner + "/" + values.destination.repo }} + backstage.io/techdocs-ref: dir:. +spec: + type: service + lifecycle: production + owner: ${{ values.owner }} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/nest-cli.json b/apps/portal/examples/nestjs-prisma-template/content/source/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/package-lock.json b/apps/portal/examples/nestjs-prisma-template/content/source/package-lock.json new file mode 100644 index 0000000..0a04e78 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/package-lock.json @@ -0,0 +1,8987 @@ +{ + "name": "${{ values.name }}", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "${{ values.name }}", + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@nestjs/common": "11.1.16", + "@nestjs/core": "11.1.16", + "@nestjs/platform-express": "11.1.16", + "@prisma/adapter-pg": "7.5.0", + "@prisma/client": "7.5.0", + "pg": "8.20.0", + "reflect-metadata": "0.2.2", + "rxjs": "7.8.2" + }, + "devDependencies": { + "@nestjs/cli": "11.0.16", + "@nestjs/schematics": "11.0.9", + "@nestjs/testing": "11.1.16", + "@types/express": "5.0.6", + "@types/jest": "29.5.14", + "@types/node": "24.12.0", + "@types/pg": "8.18.0", + "jest": "29.7.0", + "prisma": "7.5.0", + "ts-jest": "29.4.6", + "ts-loader": "9.5.4", + "ts-node": "10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "5.9.3" + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", + "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", + "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "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/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@electric-sql/pglite": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", + "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz", + "integrity": "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz", + "integrity": "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==", + "devOptional": true, + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mrleebo/prisma-ast": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", + "integrity": "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@nestjs/cli": { + "version": "11.0.16", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.16.tgz", + "integrity": "sha512-P0H+Vcjki6P5160E5QnMt3Q0X5FTg4PZkP99Ig4lm/4JWqfw32j3EXv3YBTJ2DmxLwOQ/IS9F7dzKpMAgzKTGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", + "@angular-devkit/schematics-cli": "19.2.19", + "@inquirer/prompts": "7.10.1", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.2.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "13.0.0", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.9.3", + "webpack": "5.104.1", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 20.11" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/cli/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/cli/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.104.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", + "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.4", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.16.tgz", + "integrity": "sha512-JSIeW+USuMJkkcNbiOdcPkVCeI3TSnXstIVEPpp3HiaKnPRuSbUUKm9TY9o/XpIcPHWUOQItAtC5BiAwFdVITQ==", + "license": "MIT", + "dependencies": { + "file-type": "21.3.0", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/core": { + "version": "11.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.16.tgz", + "integrity": "sha512-tXWXyCiqWthelJjrE0KLFjf0O98VEt+WPVx5CrqCf+059kIxJ8y1Vw7Cy7N4fwQafWNrmFL2AfN87DDMbVAY0w==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/platform-express": { + "version": "11.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.16.tgz", + "integrity": "sha512-IOegr5+ZfUiMKgk+garsSU4MOkPRhm46e6w8Bp1GcO4vCdl9Piz6FlWAzKVfa/U3Hn/DdzSVJOW3TWcQQFdBDw==", + "license": "MIT", + "dependencies": { + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.1.1", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "11.0.9", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", + "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.17", + "@angular-devkit/schematics": "19.2.17", + "comment-json": "4.4.1", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", + "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", + "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.17", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@nestjs/testing": { + "version": "11.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.16.tgz", + "integrity": "sha512-E7/aUCxzeMSJV80L5GWGIuiMyR/1ncS7uOIetAImfbS4ATE1/h2GBafk0qpk+vjFtPIbtoh9BWDGICzUEU5jDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, + "node_modules/@prisma/adapter-pg": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.5.0.tgz", + "integrity": "sha512-EJx7OLULahcC3IjJgdx2qRDNCT+ToY2v66UkeETMCLhNOTgqVzRzYvOEphY7Zp0eHyzfkC33Edd/qqeadf9R4A==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/driver-adapter-utils": "7.5.0", + "@types/pg": "8.11.11", + "pg": "^8.16.3", + "postgres-array": "3.0.4" + } + }, + "node_modules/@prisma/adapter-pg/node_modules/@types/pg": { + "version": "8.11.11", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz", + "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@prisma/adapter-pg/node_modules/pg-types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz", + "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@prisma/adapter-pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@prisma/adapter-pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@prisma/adapter-pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@prisma/client": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.5.0.tgz", + "integrity": "sha512-h4hF9ctp+kSRs7ENHGsFQmHAgHcfkOCxbYt6Ti9Xi8x7D+kP4tTi9x51UKmiTH/OqdyJAO+8V+r+JA5AWdav7w==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/client-runtime-utils": "7.5.0" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/client-runtime-utils": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.5.0.tgz", + "integrity": "sha512-KnJ2b4Si/pcWEtK68uM+h0h1oh80CZt2suhLTVuLaSKg4n58Q9jBF/A42Kw6Ma+aThy1yAhfDeTC0JvEmeZnFQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/config": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.5.0.tgz", + "integrity": "sha512-1J/9YEX7A889xM46PYg9e8VAuSL1IUmXJW3tEhMv7XQHDWlfC9YSkIw9sTYRaq5GswGlxZ+GnnyiNsUZ9JJhSQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.5.0.tgz", + "integrity": "sha512-163+nffny0JoPEkDhfNco0vcuT3ymIJc9+WX7MHSQhfkeKUmKe9/wqvGk5SjppT93DtBjVwr5HPJYlXbzm6qtg==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/dev": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.20.0.tgz", + "integrity": "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "@electric-sql/pglite": "0.3.15", + "@electric-sql/pglite-socket": "0.0.20", + "@electric-sql/pglite-tools": "0.2.20", + "@hono/node-server": "1.19.9", + "@mrleebo/prisma-ast": "0.13.1", + "@prisma/get-platform": "7.2.0", + "@prisma/query-plan-executor": "7.2.0", + "foreground-child": "3.3.1", + "get-port-please": "3.2.0", + "hono": "4.11.4", + "http-status-codes": "2.3.0", + "pathe": "2.0.3", + "proper-lockfile": "4.1.2", + "remeda": "2.33.4", + "std-env": "3.10.0", + "valibot": "1.2.0", + "zeptomatch": "2.1.0" + } + }, + "node_modules/@prisma/driver-adapter-utils": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.5.0.tgz", + "integrity": "sha512-B79N/amgV677mFesFDBAdrW0OIaqawap9E0sjgLBtzIz2R3hIMS1QB8mLZuUEiS4q5Y8Oh3I25Kw4SLxMypk9Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/engines": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.5.0.tgz", + "integrity": "sha512-ondGRhzoaVpRWvFaQ5wH5zS1BIbhzbKqczKjCn6j3L0Zfe/LInjcEg8+xtB49AuZBX30qyx1ZtGoootUohz2pw==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0", + "@prisma/engines-version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "@prisma/fetch-engine": "7.5.0", + "@prisma/get-platform": "7.5.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e.tgz", + "integrity": "sha512-E+iRV/vbJLl8iGjVr6g/TEWokA+gjkV/doZkaQN1i/ULVdDwGnPJDfLUIFGS3BVwlG/m6L8T4x1x5isl8hGMxA==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.5.0.tgz", + "integrity": "sha512-7I+2y1nu/gkEKSiHHbcZ1HPe/euGdEqJZxEEMT0246q4De1+hla0ZzlTgvaT9dHcVCgLSuCG8v39db5qUUWNgw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/fetch-engine": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.5.0.tgz", + "integrity": "sha512-kZCl2FV54qnyrVdnII8MI6qvt7HfU6Cbiz8dZ8PXz4f4lbSw45jEB9/gEMK2SGdiNhBKyk/Wv95uthoLhGMLYA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0", + "@prisma/engines-version": "7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e", + "@prisma/get-platform": "7.5.0" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.5.0.tgz", + "integrity": "sha512-7I+2y1nu/gkEKSiHHbcZ1HPe/euGdEqJZxEEMT0246q4De1+hla0ZzlTgvaT9dHcVCgLSuCG8v39db5qUUWNgw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.5.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", + "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.2.0" + } + }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", + "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/query-plan-executor": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz", + "integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/studio-core": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.21.1.tgz", + "integrity": "sha512-bOGqG/eMQtKC0XVvcVLRmhWWzm/I+0QUWqAEhEBtetpuS3k3V4IWqKGUONkAIT223DNXJMxMtZp36b1FmcdPeg==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19 || ^22.12 || ^24.0", + "pnpm": "8" + }, + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", + "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.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/pg": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.18.0.tgz", + "integrity": "sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001778", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", + "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, + "node_modules/chevrotain/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT", + "peer": true + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.313", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-type": { + "version": "21.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", + "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/grammex": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", + "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/graphmatch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz", + "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", + "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "devOptional": true, + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "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==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-pool": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-types/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "devOptional": true, + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "license": "MIT" + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prisma": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.5.0.tgz", + "integrity": "sha512-n30qZpWehaYQzigLjmuPisyEsvOzHt7bZeRyg8gZ5DvJo9FGjD+gNaY59Ns3hlLD5/jZH5GBeftIss0jDbUoLg==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "7.5.0", + "@prisma/dev": "0.20.0", + "@prisma/engines": "7.5.0", + "@prisma/studio-core": "0.21.1", + "mysql2": "3.15.3", + "postgres": "3.4.7" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "better-sqlite3": ">=9.0.0", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/remeda": { + "version": "2.33.4", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", + "integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/remeda" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "devOptional": true, + "license": "MIT", + "peer": true + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", + "devOptional": true + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/ts-jest": { + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-loader": { + "version": "9.5.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", + "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webpack": { + "version": "5.105.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", + "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zeptomatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", + "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "grammex": "^3.1.11", + "graphmatch": "^1.1.0" + } + } + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/package.json b/apps/portal/examples/nestjs-prisma-template/content/source/package.json new file mode 100644 index 0000000..ec49539 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/package.json @@ -0,0 +1,58 @@ +{ + "name": "${{ values.name }}", + "version": "0.0.1", + "description": "${{ values.description }}", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "start": "nest start", + "start:dev": "nest start --watch", + "start:prod": "node dist/main", + "start:migrate:prod": "npx prisma migrate deploy && node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "prisma:generate": "prisma generate", + "prisma:migrate:dev": "prisma migrate dev", + "prisma:migrate:deploy": "prisma migrate deploy" + }, + "dependencies": { + "@nestjs/common": "11.1.16", + "@nestjs/core": "11.1.16", + "@nestjs/platform-express": "11.1.16", + "@prisma/adapter-pg": "7.5.0", + "@prisma/client": "7.5.0", + "pg": "8.20.0", + "reflect-metadata": "0.2.2", + "rxjs": "7.8.2" + }, + "devDependencies": { + "@nestjs/cli": "11.0.16", + "@nestjs/schematics": "11.0.9", + "@nestjs/testing": "11.1.16", + "@types/express": "5.0.6", + "@types/jest": "29.5.14", + "@types/node": "24.12.0", + "@types/pg": "8.18.0", + "jest": "29.7.0", + "prisma": "7.5.0", + "ts-jest": "29.4.6", + "ts-loader": "9.5.4", + "ts-node": "10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "5.9.3" + }, + "jest": { + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": ["**/*.(t|j)s"], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/prisma.config.ts b/apps/portal/examples/nestjs-prisma-template/content/source/prisma.config.ts new file mode 100644 index 0000000..506a286 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/prisma.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from 'prisma/config'; + +/** + * Constructs a PostgreSQL connection URL from individual environment variables. + * These env vars are injected by the Helios Operator: + * - DB_HOST, DB_USER, DB_PASS (from K8s Secret) + * - DB_NAME, DB_PORT (from K8s ConfigMap / CUE engine) + */ +function buildDatabaseUrl(): string { + const host = process.env.DB_HOST || 'localhost'; + const user = process.env.DB_USER || 'postgres'; + const pass = encodeURIComponent(process.env.DB_PASS || 'postgres'); + const name = process.env.DB_NAME || 'postgres'; + const port = process.env.DB_PORT || '5432'; + + return `postgresql://${user}:${pass}@${host}:${port}/${name}?schema=public`; +} + +export default defineConfig({ + schema: 'prisma/schema.prisma', + migrations: { + path: 'prisma/migrations', + }, + datasource: { + url: process.env.DATABASE_URL || buildDatabaseUrl(), + }, +}); diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/prisma/migrations/migration_lock.toml b/apps/portal/examples/nestjs-prisma-template/content/source/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..13ac3fa --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Prisma Migrate lockfile v1 + +provider = "postgresql" diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma b/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma new file mode 100644 index 0000000..74bea31 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma @@ -0,0 +1,23 @@ +// Prisma Schema for ${{ values.name }} +// DATABASE_URL is constructed at runtime from injected env vars: +// DB_HOST, DB_USER, DB_PASS (from K8s Secret via Helios Operator) +// DB_NAME, DB_PORT (from K8s ConfigMap via CUE engine) + +generator client { + provider = "prisma-client" +} + +datasource db { + provider = "postgresql" +} + +// Example model — replace with your domain models +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@map("users") +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/app.controller.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/app.controller.ts new file mode 100644 index 0000000..d02038d --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/app.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } + + @Get('health') + healthCheck(): { status: string } { + return this.appService.healthCheck(); + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/app.module.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/app.module.ts new file mode 100644 index 0000000..5921107 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/app.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { PrismaModule } from './prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/app.service.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/app.service.ts new file mode 100644 index 0000000..f90ebe3 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/app.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from './prisma/prisma.service'; + +@Injectable() +export class AppService { + constructor(private readonly prisma: PrismaService) {} + + getHello(): string { + return 'Hello from ${{ values.name }}!'; + } + + healthCheck(): { status: string } { + return { status: 'ok' }; + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts new file mode 100644 index 0000000..37a6af9 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts @@ -0,0 +1,13 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + const port = process.env.PORT || ${{ values.port } +}; +await app.listen(port); + +console.log(`🚀 Application is running on port ${port}`); +} +bootstrap(); diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.module.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.module.ts new file mode 100644 index 0000000..7207426 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { PrismaService } from './prisma.service'; + +@Global() +@Module({ + providers: [PrismaService], + exports: [PrismaService], +}) +export class PrismaModule {} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts new file mode 100644 index 0000000..d932300 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts @@ -0,0 +1,61 @@ +import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; +import { PrismaPg } from '@prisma/adapter-pg'; +import * as pg from 'pg'; + +/** + * PrismaService manages the database connection lifecycle using Prisma v7. + * + * Prisma v7 requires an explicit driver adapter — we use @prisma/adapter-pg + * with the `pg` Node.js driver for PostgreSQL. + * + * Database credentials are injected by the Helios Operator as env vars: + * - DB_HOST: database hostname (from K8s Secret) + * - DB_USER: database username (from K8s Secret) + * - DB_PASS: database password (from K8s Secret) + * - DB_NAME: database name (from K8s ConfigMap, or defaults to component-db) + * - DB_PORT: database port (from K8s ConfigMap, or defaults to 5432) + */ +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + private pool: pg.Pool; + + constructor() { + const pool = new pg.Pool({ + host: process.env.DB_HOST || 'localhost', + user: process.env.DB_USER || 'postgres', + password: process.env.DB_PASS || 'postgres', + database: process.env.DB_NAME || 'postgres', + port: parseInt(process.env.DB_PORT || '5432', 10), + }); + + const adapter = new PrismaPg(pool as any); + + super({ adapter }); + + this.pool = pool; + } + + /** + * Constructs a PostgreSQL connection URL from individual environment variables. + * Used for Prisma CLI operations (migrate, studio) that require DATABASE_URL. + */ + static buildDatabaseUrl(): string { + const host = process.env.DB_HOST || 'localhost'; + const user = process.env.DB_USER || 'postgres'; + const pass = encodeURIComponent(process.env.DB_PASS || 'postgres'); + const name = process.env.DB_NAME || 'postgres'; + const port = process.env.DB_PORT || '5432'; + + return `postgresql://${user}:${pass}@${host}:${port}/${name}?schema=public`; + } + + async onModuleInit(): Promise { + await this.$connect(); + } + + async onModuleDestroy(): Promise { + await this.$disconnect(); + await this.pool.end(); + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.build.json b/apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.build.json new file mode 100644 index 0000000..64f86c6 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.json b/apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.json new file mode 100644 index 0000000..0833d25 --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/content/source/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2022", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "paths": { + "@/*": ["src/*"] + } + } +} diff --git a/apps/portal/examples/nestjs-prisma-template/template.yaml b/apps/portal/examples/nestjs-prisma-template/template.yaml new file mode 100644 index 0000000..713447a --- /dev/null +++ b/apps/portal/examples/nestjs-prisma-template/template.yaml @@ -0,0 +1,190 @@ +apiVersion: scaffolder.backstage.io/v1beta3 +kind: Template +metadata: + name: nestjs-prisma-template + title: NestJS + Prisma Template (Database-backed) + description: >- + Scaffolds a NestJS service with Prisma ORM and a PostgreSQL database. + The Helios Operator automatically provisions the database and injects + credentials (DB_HOST, DB_USER, DB_PASS) into the backend pod. +spec: + owner: user:guest + type: service + + parameters: + - title: Component Information + required: + - name + - owner + - port + properties: + name: + title: Name + type: string + description: Unique name of the component + ui:autofocus: true + owner: + title: Owner + type: string + description: Owner of the component + ui:field: OwnerPicker + ui:options: + catalogFilter: + kind: [Group, User] + port: + title: Port + type: number + description: The port the NestJS app listens on + default: 3000 + dockerOrg: + title: Docker Registry Org/User + type: string + description: Your Docker Hub username or Organization + repoName: + title: Docker Repository Name + type: string + description: The name of the Docker repository (e.g. my-service) + + - title: Database Configuration + properties: + databaseConfig: + title: Database Settings + type: object + ui:field: DatabasePicker + + - title: Repository & Webhook + required: + - repoUrl + - webhookUrl + - webhookSecret + properties: + repoUrl: + title: Source Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com + webhookUrl: + title: Webhook URL + type: string + description: The URL to send the webhook payload to + webhookSecret: + title: Webhook Secret + type: string + description: The secret to use for the webhook + ui:field: Secret + + steps: + # 1. Source Code + - id: fetch-source + name: Fetch Source Code + action: fetch:template + input: + url: ./content/source + targetPath: ./source + values: + name: ${{ parameters.name }} + owner: ${{ parameters.owner }} + port: ${{ parameters.port }} + description: "NestJS service: ${{ parameters.name }}" + image: index.docker.io/${{ parameters.dockerOrg }}/${{ parameters.repoName }} + + - id: publish-source + name: Publish Source Code + action: publish:github + input: + description: Source Code for ${{ parameters.name }} + repoUrl: ${{ parameters.repoUrl }} + sourcePath: ./source + token: ${{ secrets.GITHUB_TOKEN }} + repoVisibility: public + protectDefaultBranch: false + + - id: create-webhook + name: Create Webhook + action: github:webhook + input: + repoUrl: ${{ parameters.repoUrl }} + webhookUrl: ${{ parameters.webhookUrl }} + webhookSecret: ${{ parameters.webhookSecret }} + events: + - push + active: true + contentType: json + token: ${{ secrets.GITHUB_TOKEN }} + + # 2. GitOps + - id: fetch-gitops + name: Fetch GitOps Manifests + action: fetch:template + input: + url: ./content/gitops + targetPath: ./gitops + values: + name: ${{ parameters.name }} + image: index.docker.io/${{ parameters.dockerOrg }}/${{ parameters.repoName }} + dockerOrg: ${{ parameters.dockerOrg }} + repoName: ${{ parameters.repoName }} + port: ${{ parameters.port }} + owner: ${{ parameters.owner }} + sourceRepo: ${{ steps['publish-source'].output.remoteUrl }} + gitopsRepo: ${{ steps['publish-source'].output.remoteUrl | replace(".git", "") }}-gitops + webhookUrl: ${{ parameters.webhookUrl }} + testCommand: "npm ci && npm test" + + - id: publish-gitops + name: Publish GitOps Manifests + action: publish:github + input: + description: GitOps Manifests for ${{ parameters.name }} + repoUrl: ${{ parameters.repoUrl }}-gitops + sourcePath: ./gitops + token: ${{ secrets.GITHUB_TOKEN }} + repoVisibility: public + protectDefaultBranch: false + + # 3. Secret + Deploy + - id: create-secret + name: Create GitHub Credentials Secret + action: kubernetes:create-github-secret + input: + name: ${{ parameters.name }} + namespace: default + username: ${{ (parameters.repoUrl | parseRepoUrl).owner }} + webhookSecret: ${{ parameters.webhookSecret }} + + - id: apply-helios + name: Deploy to Kubernetes + action: kubernetes:apply + input: + manifestPath: ./gitops/helios-app.yaml + namespaced: true + + # 4. Registration + - id: register + name: Register Component + action: catalog:register + input: + repoContentsUrl: ${{ steps['publish-source'].output.repoContentsUrl }} + catalogInfoPath: '/catalog-info.yaml' + + - id: notify + name: Notify User + action: notification:send + input: + recipients: entity + entityRefs: + - user:default/guest + title: 'NestJS + Prisma Template Executed' + info: 'Your NestJS + Prisma service has been scaffolded with a PostgreSQL database!' + + output: + links: + - title: Source Repository + url: ${{ steps['publish-source'].output.remoteUrl }} + - title: GitOps Repository + url: ${{ steps['publish-gitops'].output.remoteUrl }} + - title: Open in Catalog + icon: catalog + entityRef: ${{ steps['register'].output.entityRef }} diff --git a/cue/cue.mod/module.cue b/cue/cue.mod/module.cue index ce623ea..fbe3e0b 100644 --- a/cue/cue.mod/module.cue +++ b/cue/cue.mod/module.cue @@ -1,2 +1,2 @@ module: "helios.io/cue" -language: version: "v0.15.4" +language: version: "v0.16.0" diff --git a/docs/manual-verification-guide.md b/docs/manual-verification-guide.md new file mode 100644 index 0000000..3b92f47 --- /dev/null +++ b/docs/manual-verification-guide.md @@ -0,0 +1,540 @@ +# Manual Verification Guide — Automated Secret Injection (#39) + +This guide walks through manually verifying the acceptance criteria for the +Automated Secret Injection feature on a local k3d cluster. + +## Acceptance Criteria + +1. **Secret Injection**: `kubectl describe pod ` shows database + credentials (DB_HOST, DB_USER, DB_PASS) successfully mounted as environment + variables via `secretKeyRef`. +2. **Database Connectivity**: A NestJS server connects to the provisioned + PostgreSQL database using the injected env vars and responds to health checks. + +## Prerequisites + +- **Docker** running locally +- **k3d** (`v5+`) +- **kubectl** configured +- **kustomize** (or `kubectl kustomize`) + +Install via mise (if used): + +```bash +mise install k3d kubectl +``` + +--- + +## Step 1: Create a k3d Cluster + +```bash +k3d cluster create helios-test --wait +``` + +Fix kubeconfig if needed (k3d may set the server to `host.docker.internal`): + +```bash +# Get the port k3d assigned +PORT=$(kubectl config view -o jsonpath='{.clusters[?(@.name=="k3d-helios-test")].cluster.server}' | grep -oP ':\K\d+$') + +# Point to 127.0.0.1 for local access +kubectl config set-cluster k3d-helios-test --server="https://127.0.0.1:${PORT}" +``` + +Verify: + +```bash +kubectl get nodes +# Should show: k3d-helios-test-server-0 Ready +``` + +--- + +## Step 2: Build and Load the Operator Image + +The operator needs CUE files bundled in the image. Use the test Dockerfile +(`apps/operator/Dockerfile.test`) which copies `cue/` into the image: + +```bash +# From project root (helios-platform/) +docker build -f apps/operator/Dockerfile.test -t helios-operator:local . + +# Load into k3d +k3d image import helios-operator:local -c helios-test +``` + +--- + +## Step 3: Deploy the Operator + +### 3a. Install CRDs + +```bash +kubectl apply -f apps/operator/config/crd/bases/app.helios.io_heliosapps.yaml +``` + +### 3b. Deploy operator via kustomize + +The operator kustomization (`config/default/`) sets namespace `operator-system` +and adds prefix `operator-`. The manager image is already mapped to +`helios-operator:local` in `config/manager/kustomization.yaml`. + +```bash +cd apps/operator + +# Build kustomize output and patch imagePullPolicy for local images +kustomize build config/default \ + | sed 's/imagePullPolicy: Always/imagePullPolicy: Never/' \ + | kubectl apply -f - + +cd ../.. +``` + +Wait for the operator to be ready: + +```bash +kubectl -n operator-system get pods -w +# Wait for: operator-controller-manager-xxx 2/2 Running +``` + +Check the operator logs to confirm CUE engine initialized: + +```bash +kubectl -n operator-system logs deployment/operator-controller-manager -c manager | head -20 +# Look for: "CUE engine initialized" +``` + +--- + +## Step 4: Build and Load the Test NestJS App + +Create a minimal NestJS + Prisma test app. You can use the template source at +`apps/portal/examples/nestjs-prisma-template/content/source/` as a base, with +template variables replaced (`${{ values.* }}` -> concrete values). + +### 4a. Create a test app directory + +```bash +mkdir -p /tmp/nestjs-test-app/src/prisma +mkdir -p /tmp/nestjs-test-app/prisma/migrations/0001_init +``` + +### 4b. Key files + +**package.json** — Note: use `npm install` in Dockerfile if no lockfile exists. +Set `start:migrate:prod` to run prisma migrations then start the app. + +```json +{ + "name": "test-backend", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "nest build", + "start:prod": "node dist/main", + "start:migrate:prod": "npx prisma migrate deploy && node dist/main" + }, + "dependencies": { + "@nestjs/common": "11.1.16", + "@nestjs/core": "11.1.16", + "@nestjs/platform-express": "11.1.16", + "@prisma/adapter-pg": "7.5.0", + "@prisma/client": "7.5.0", + "pg": "8.20.0", + "reflect-metadata": "0.2.2", + "rxjs": "7.8.2" + }, + "devDependencies": { + "@nestjs/cli": "11.0.16", + "@types/pg": "8.18.0", + "prisma": "7.5.0", + "ts-node": "10.9.2", + "typescript": "5.9.3" + } +} +``` + +**prisma.config.ts** — Prisma 7 requires this instead of `url` in schema: + +```typescript +import { defineConfig } from 'prisma/config'; + +function buildDatabaseUrl(): string { + const host = process.env.DB_HOST || 'localhost'; + const user = process.env.DB_USER || 'postgres'; + const pass = encodeURIComponent(process.env.DB_PASS || 'postgres'); + const name = process.env.DB_NAME || 'postgres'; + const port = process.env.DB_PORT || '5432'; + return `postgresql://${user}:${pass}@${host}:${port}/${name}?schema=public`; +} + +export default defineConfig({ + schema: 'prisma/schema.prisma', + migrations: { path: 'prisma/migrations' }, + datasource: { url: process.env.DATABASE_URL || buildDatabaseUrl() }, +}); +``` + +**prisma/schema.prisma** — No `url` in datasource (Prisma 7 breaking change): + +```prisma +generator client { + provider = "prisma-client" +} + +datasource db { + provider = "postgresql" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + @@map("users") +} +``` + +**prisma/migrations/migration_lock.toml**: + +```toml +# Prisma Migrate lockfile v1 + +provider = "postgresql" +``` + +**prisma/migrations/0001_init/migration.sql**: + +```sql +CREATE TABLE "users" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); +``` + +**src/prisma/prisma.service.ts** — Use `import * as pg` (not default import): + +```typescript +import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; +import { PrismaPg } from '@prisma/adapter-pg'; +import * as pg from 'pg'; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + private pool: pg.Pool; + + constructor() { + const pool = new pg.Pool({ + host: process.env.DB_HOST || 'localhost', + user: process.env.DB_USER || 'postgres', + password: process.env.DB_PASS || 'postgres', + database: process.env.DB_NAME || 'postgres', + port: parseInt(process.env.DB_PORT || '5432', 10), + }); + const adapter = new PrismaPg(pool as any); + super({ adapter }); + this.pool = pool; + } + + async onModuleInit(): Promise { await this.$connect(); } + async onModuleDestroy(): Promise { await this.$disconnect(); await this.pool.end(); } +} +``` + +**src/app.controller.ts** — Health endpoint: + +```typescript +import { Controller, Get } from '@nestjs/common'; + +@Controller() +export class AppController { + @Get('health') + health() { return { status: 'ok' }; } +} +``` + +(Also create standard `src/main.ts`, `src/app.module.ts`, `src/prisma/prisma.module.ts`, +`tsconfig.json`, `tsconfig.build.json`, `nest-cli.json` — use the template source +as reference.) + +### 4c. Dockerfile for test app + +```dockerfile +FROM node:24-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY prisma ./prisma/ +COPY prisma.config.ts ./ +RUN npx prisma generate +COPY . . +RUN npm run build + +FROM node:24-alpine AS production +WORKDIR /app +COPY package*.json ./ +RUN npm install --omit=dev +COPY --from=builder /app/prisma ./prisma/ +COPY --from=builder /app/prisma.config.ts ./ +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma/ +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma/ +COPY --from=builder /app/dist ./dist/ +EXPOSE 3000 +CMD ["npm", "run", "start:migrate:prod"] +``` + +### 4d. Build and load + +```bash +cd /tmp/nestjs-test-app +docker build -t test-backend:latest . +k3d image import test-backend:latest -c helios-test +cd - +``` + +--- + +## Step 5: Create Test Resources + +### 5a. Backend Deployment (simulates ArgoCD output) + +The operator injects env vars into an **existing** Deployment. In production, +ArgoCD creates this. For testing, create it manually: + +```bash +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-backend + namespace: default + labels: + app: test-backend +spec: + replicas: 1 + selector: + matchLabels: + app: test-backend + template: + metadata: + labels: + app: test-backend + spec: + containers: + - name: test-backend + image: test-backend:latest + imagePullPolicy: Never + ports: + - containerPort: 3000 + env: + - name: PORT + value: "3000" + - name: DB_NAME + value: "test_backend_db" + - name: DB_PORT + value: "5432" +EOF +``` + +### 5b. HeliosApp Custom Resource + +This triggers the operator's reconcile loop: + +```bash +cat <<'EOF' | kubectl apply -f - +apiVersion: app.helios.io/v1alpha1 +kind: HeliosApp +metadata: + name: test-app + namespace: default +spec: + owner: test-team + description: "Test app for database secret injection verification" + gitRepo: "https://github.com/test/test-repo" + gitopsRepo: "https://github.com/test/test-gitops" + gitopsPath: "test-app" + imageRepo: "test-backend" + gitBranch: main + gitopsBranch: main + gitopsSecretRef: "" + pipelineName: "from-code-to-cluster" + webhookDomain: "test.example.com" + webhookSecret: "github-webhook-secret" + port: 3000 + replicas: 1 + components: + - name: test-backend + type: web-service + properties: + image: "test-backend:latest" + port: 3000 + replicas: 1 + traits: + - type: service + properties: + port: 3000 + - type: database + properties: + dbType: postgres + dbName: test_backend_db + version: "16" + storage: "1Gi" +EOF +``` + +--- + +## Step 6: Verify Acceptance Criteria + +### 6a. Verify Secret Created (Phase 0.5) + +```bash +kubectl get secret test-backend-db-secret -o jsonpath='{.data}' | python3 -m json.tool +``` + +Expected: Secret contains `DB_HOST`, `DB_USER`, `DB_PASS` keys (base64-encoded). + +Decode values: + +```bash +kubectl get secret test-backend-db-secret -o jsonpath='{.data.DB_HOST}' | base64 -d +# Expected: test-backend-db + +kubectl get secret test-backend-db-secret -o jsonpath='{.data.DB_USER}' | base64 -d +# Expected: some generated username + +kubectl get secret test-backend-db-secret -o jsonpath='{.data.DB_PASS}' | base64 -d +# Expected: some generated password +``` + +### 6b. Verify Postgres StatefulSet Running (Phase 0.7) + +```bash +kubectl get statefulset test-backend-db +# Expected: READY 1/1 + +kubectl get svc test-backend-db +# Expected: headless service (ClusterIP: None) +``` + +### 6c. Verify Env Var Injection (Phase 0.9) — Acceptance Criterion #1 + +```bash +kubectl get deployment test-backend -o jsonpath='{.spec.template.spec.containers[0].env}' | python3 -m json.tool +``` + +Expected output includes `secretKeyRef` entries: + +```json +[ + { "name": "PORT", "value": "3000" }, + { "name": "DB_NAME", "value": "test_backend_db" }, + { "name": "DB_PORT", "value": "5432" }, + { + "name": "DB_HOST", + "valueFrom": { "secretKeyRef": { "name": "test-backend-db-secret", "key": "DB_HOST" } } + }, + { + "name": "DB_USER", + "valueFrom": { "secretKeyRef": { "name": "test-backend-db-secret", "key": "DB_USER" } } + }, + { + "name": "DB_PASS", + "valueFrom": { "secretKeyRef": { "name": "test-backend-db-secret", "key": "DB_PASS" } } + } +] +``` + +Also verify via `kubectl describe pod`: + +```bash +kubectl describe pod -l app=test-backend | grep -A2 "DB_HOST\|DB_USER\|DB_PASS" +``` + +### 6d. Verify NestJS Connects to Database — Acceptance Criterion #2 + +Wait for the backend pod to be ready: + +```bash +kubectl get pods -l app=test-backend -w +# Wait for: test-backend-xxx 1/1 Running +``` + +Check application logs: + +```bash +kubectl logs -l app=test-backend +``` + +Expected: Prisma migration runs, NestJS starts without connection errors. + +Port-forward and test health endpoint: + +```bash +kubectl port-forward deployment/test-backend 3000:3000 & +curl http://localhost:3000/health +# Expected: {"status":"ok"} +kill %1 +``` + +--- + +## Step 7: Check Operator Logs + +```bash +kubectl -n operator-system logs deployment/operator-controller-manager -c manager \ + | grep -E "secret|inject|database|StatefulSet" +``` + +Look for: + +- `"Created database secret"` (Phase 0.5) +- `"Created database StatefulSet"` / `"Created database Service"` (Phase 0.7) +- `"Injected database env vars"` (Phase 0.9) + +**Note:** Errors about Tekton CRDs or GitOps tokens are expected in this test +environment — the operator tolerates these and continues to database phases. + +--- + +## Step 8: Cleanup + +```bash +kubectl delete heliosapp test-app +kubectl delete deployment test-backend +kubectl delete statefulset test-backend-db +kubectl delete svc test-backend-db +kubectl delete secret test-backend-db-secret +k3d cluster delete helios-test +``` + +--- + +## Known Gotchas + +| Issue | Cause | Fix | +|---|---|---| +| `import pg from 'pg'` fails at runtime | CommonJS module system — `pg` has no default export | Use `import * as pg from 'pg'` | +| `url = env("DATABASE_URL")` in schema.prisma | Prisma 7 removed `url` from schema datasource | Use `prisma.config.ts` with `defineConfig()` | +| `prisma migrate deploy` fails with "lock file missing" | No `migration_lock.toml` in migrations dir | Create `migration_lock.toml` with `provider = "postgresql"` | +| `PrismaPg(pool)` TypeScript error | `@types/pg` Pool type conflicts with adapter's bundled types | Cast with `pool as any` | +| k3d kubeconfig uses `host.docker.internal` | k3d default for Docker Desktop | Override with `kubectl config set-cluster ... --server=https://127.0.0.1:` | +| CUE engine fails to initialize | CUE files not in operator image | Use `Dockerfile.test` which copies `cue/` into image | + +--- + +## Summary of Operator Phases Tested + +| Phase | Action | Resource Created | +|---|---|---| +| 0.5 | Generate DB secret | `Secret/-db-secret` | +| 0.7 | Provision Postgres | `StatefulSet/-db` + `Service/-db` | +| 0.9 | Inject env vars | Patches `Deployment/` with `secretKeyRef` | diff --git a/implementation_plan.md b/implementation_plan.md new file mode 100644 index 0000000..7408765 --- /dev/null +++ b/implementation_plan.md @@ -0,0 +1,115 @@ +# Automated Secret Injection (#39) + +Inject database credentials (DB_HOST, DB_USER, DB_PASS) from the Operator-generated K8s Secret into the Backend Pod's env block, and create a NestJS + Prisma Node.js template that connects using these injected env vars. + +## Proposed Changes + +### Operator — Secret Injection into Backend Deployment + +The CUE engine renders a Deployment for each component (via GitOps sync). After the GitOps sync pushes the manifest, ArgoCD deploys it. However, the Deployment currently has **no database env vars** — the CUE `#Deployment` base only supports simple `{name, value}` env vars, not `secretKeyRef`. + +**Approach**: Add a new reconciliation phase (**Phase 0.9**) in [heliosapp_controller.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/heliosapp_controller.go) that **patches the live Deployment** (deployed by ArgoCD) to inject `DB_HOST`, `DB_USER`, and `DB_PASS` as `envFrom` / env vars referencing the K8s Secret. This runs AFTER database secrets and instances are created. + +> [!IMPORTANT] +> The CUE engine generates the base Deployment manifest (pushed via GitOps). The Go operator patches the **live Deployment** in-cluster to add secret env vars. This keeps secrets out of the GitOps repo entirely. + +#### [MODIFY] [database_resources.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go) + +Add a new function `InjectDatabaseEnvVars` that: +- Takes a Deployment and a database secret name +- Adds `DB_HOST`, `DB_USER`, `DB_PASS` env vars to the first container using `valueFrom.secretKeyRef` +- Also adds `DATABASE_URL` as a convenience env var for Prisma ORM (constructed from the other vars via an init container or as a direct string referencing the secret values) +- Is idempotent: skips if env vars already exist + +Add a new reconciler method `reconcileDatabaseSecretInjection` that: +- Finds components with database traits +- Gets or waits for the corresponding Deployment (by component name) +- Calls `InjectDatabaseEnvVars` to patch the Deployment's env block +- Uses `r.Update()` to apply changes to the live Deployment + +#### [MODIFY] [heliosapp_controller.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/heliosapp_controller.go) + +- Add a call to `r.reconcileDatabaseSecretInjection(ctx, &heliosApp)` as **Phase 0.9** (after database instance provisioning at Phase 0.7, before image validation) +- Add structured logging for the injection phase + +#### [MODIFY] [database_resources_test.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources_test.go) + +Add new test cases: +- `TestInjectDatabaseEnvVars` — verifies env vars are correctly injected into a Deployment +- `TestInjectDatabaseEnvVars_Idempotent` — verifies running injection twice doesn't duplicate env vars +- `TestReconcileDatabaseSecretInjection` — integration test with fake client verifying the full reconciliation flow +- `TestReconcileDatabaseSecretInjection_NoTraits` — skips when no database traits +- `TestReconcileDatabaseSecretInjection_DeploymentNotFound` — handles missing Deployment gracefully (returns nil, logs warning — Deployment may not be deployed by ArgoCD yet) + +--- + +### Node.js Template — NestJS + Prisma ORM + +Create a new Backstage template for NestJS + Prisma that connects to the database using the injected env vars. + +#### [NEW] `apps/portal/examples/nestjs-prisma-template/` directory + +Structure: +``` +nestjs-prisma-template/ + template.yaml # Backstage scaffolder template + content/ + source/ + package.json # NestJS + Prisma deps (latest) + tsconfig.json # TypeScript config + tsconfig.build.json # Build-specific TS config + nest-cli.json # NestJS CLI config + .env.example # Example env vars + Dockerfile # Multi-stage NestJS build + catalog-info.yaml # Backstage catalog entry + prisma/ + schema.prisma # Prisma schema with env-based DATABASE_URL + src/ + main.ts # NestJS bootstrap + app.module.ts # Root module + app.controller.ts # Health check controller + app.service.ts # App service + prisma/ + prisma.module.ts # Prisma module + prisma.service.ts # Prisma service (extends PrismaClient) + gitops/ + helios-app.yaml # HeliosApp CRD manifest with database trait +``` + +Key design decisions: +- `DATABASE_URL` is built from `DB_HOST`, `DB_USER`, `DB_PASS` env vars (Prisma convention) +- The Prisma schema reads `DATABASE_URL` from `env("DATABASE_URL")` +- The [Dockerfile](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/Dockerfile) runs `prisma generate` at build time and `prisma migrate deploy` at startup +- Uses latest stable NestJS v11+ and Prisma v6+ + +--- + +## Verification Plan + +### Automated Tests + +All existing tests plus new tests, run from `apps/operator/`: + +```sh +# 1. Compilation check +go build ./... + +# 2. Static analysis +go vet ./... + +# 3. Full test suite (includes all database_resources_test.go tests) +make test + +# 4. Build operator binary +make build + +# 5. Run database-specific tests in verbose mode +go test ./internal/controller/... -v -count=1 -run TestInjectDatabaseEnvVars +go test ./internal/controller/... -v -count=1 -run TestReconcileDatabaseSecretInjection +``` + +### Manual Verification + +After deploying a backend with a database trait: +1. Run `kubectl describe pod ` to verify DB_HOST, DB_USER, DB_PASS are present as environment variables referencing the secret +2. Verify the NestJS application boots and connects to the database by checking pod logs diff --git a/task.md b/task.md new file mode 100644 index 0000000..5efd769 --- /dev/null +++ b/task.md @@ -0,0 +1,28 @@ +# [Impl] Automated Secret Injection #39 + +## Operator — Secret Injection into Backend Deployment + +- [x] Research codebase and understand existing patterns +- [x] Implement [reconcileDatabaseSecretInjection](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#497-552) in Go operator + - [x] Add new phase (0.9) to inject DB_HOST, DB_USER, DB_PASS into backend Deployment env block + - [x] Patch the Deployment rendered by CUE/GitOps with env secretKeyRef +- [x] Add comprehensive tests for secret injection logic + - [x] Unit tests for injection function (3 tests) + - [x] Integration test with fake client (3 tests) + +## Node.js Template — NestJS + Prisma ORM + +- [x] Create NestJS + Prisma template directory structure + - [x] [package.json](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/advanced-template/content/source/package.json) with latest NestJS and Prisma deps + - [x] [prisma/schema.prisma](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma) connecting via DB_HOST, DB_USER, DB_PASS envs + - [x] `prisma/migrations/` with initial migration + - [x] NestJS main app module with Prisma service + - [x] [Dockerfile](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/Dockerfile) for containerized deployment +- [x] Update Backstage template for database-backed services + +## Verification + +- [/] `go build ./...` — compiles cleanly +- [x] `go vet ./...` — no issues +- [x] `make test` — all tests pass +- [/] `make build` — operator binary builds diff --git a/walkthrough.md b/walkthrough.md new file mode 100644 index 0000000..450130a --- /dev/null +++ b/walkthrough.md @@ -0,0 +1,2596 @@ +# Automated Secret Injection (#39) — Walkthrough + +## Changes Made + +### 1. Go Operator — Secret Injection (Phase 0.9) + +#### [database_resources.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go) + +- **[InjectDatabaseEnvVars](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#458-496)** — Patches a Deployment's first container with `DB_HOST`, `DB_USER`, `DB_PASS` env vars via `secretKeyRef`. Idempotent: skips if vars already exist. +- **[reconcileDatabaseSecretInjection](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#497-552)** — Finds components with database traits, fetches the live Deployment, calls [InjectDatabaseEnvVars](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#458-496), and updates it. Gracefully skips if Deployment doesn't exist yet (ArgoCD hasn't synced). + +```diff:database_resources.go +// database_resources.go handles database credential generation and Secret creation +// for components with database traits. +// +// The CUE engine generates ConfigMaps with database metadata (host, port, name), +// but credentials (username, password) are generated by this Go code for security +// reasons - secrets should never be stored in CUE definitions or GitOps repos. +package controller + +import ( + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "math/big" + "strings" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" +) + +const ( + // DefaultPasswordLength is the length of generated passwords + DefaultPasswordLength = 32 + + // DefaultUsernameLength is the length of generated usernames + DefaultUsernameLength = 16 + + // PasswordCharset contains valid characters for password generation + // Includes uppercase, lowercase, digits, and special characters + PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" + + // UsernameCharset contains valid characters for username generation + // Lowercase letters and digits only for database compatibility + UsernameCharset = "abcdefghijklmnopqrstuvwxyz0123456789" + + // DatabaseTraitType is the trait type identifier for database traits + DatabaseTraitType = "database" + + // DefaultPostgresVersion is the default Postgres image tag when not specified. + DefaultPostgresVersion = "16" + + // DefaultPostgresPort is the default port for Postgres. + DefaultPostgresPort = 5432 + + // DefaultDatabaseStorage is the default PVC size for database volumes. + DefaultDatabaseStorage = "1Gi" + + // PostgresDataPath is the mount path for Postgres data directory. + PostgresDataPath = "/var/lib/postgresql/data" + + // PostgresDataSubPath is the subPath within the PVC to avoid lost+found issues. + PostgresDataSubPath = "pgdata" +) + +// DatabaseCredentials holds generated database credentials +type DatabaseCredentials struct { + Username string + Password string +} + +// DatabaseTraitProperties represents the properties of a database trait +type DatabaseTraitProperties struct { + DBType string `json:"dbType"` + DBName string `json:"dbName"` + Port int `json:"port"` + Version string `json:"version"` + Storage string `json:"storage"` +} + +// GenerateSecurePassword generates a cryptographically secure random password +func GenerateSecurePassword(length int) (string, error) { + if length <= 0 { + length = DefaultPasswordLength + } + + password := make([]byte, length) + charsetLen := big.NewInt(int64(len(PasswordCharset))) + + for i := 0; i < length; i++ { + idx, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + password[i] = PasswordCharset[idx.Int64()] + } + + return string(password), nil +} + +// GenerateSecureUsername generates a cryptographically secure random username +func GenerateSecureUsername(length int) (string, error) { + if length <= 0 { + length = DefaultUsernameLength + } + + // Ensure username starts with a letter (database requirement) + username := make([]byte, length) + lettersOnly := "abcdefghijklmnopqrstuvwxyz" + lettersLen := big.NewInt(int64(len(lettersOnly))) + + // First character must be a letter + idx, err := rand.Int(rand.Reader, lettersLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + username[0] = lettersOnly[idx.Int64()] + + // Rest can be letters or digits + charsetLen := big.NewInt(int64(len(UsernameCharset))) + for i := 1; i < length; i++ { + idx, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + username[i] = UsernameCharset[idx.Int64()] + } + + return string(username), nil +} + +// GenerateCredentials generates a new set of database credentials +func GenerateCredentials() (*DatabaseCredentials, error) { + username, err := GenerateSecureUsername(DefaultUsernameLength) + if err != nil { + return nil, fmt.Errorf("failed to generate username: %w", err) + } + + password, err := GenerateSecurePassword(DefaultPasswordLength) + if err != nil { + return nil, fmt.Errorf("failed to generate password: %w", err) + } + + return &DatabaseCredentials{ + Username: username, + Password: password, + }, nil +} + +// GenerateDatabaseSecret creates a Kubernetes Secret containing database credentials +func GenerateDatabaseSecret(namespace, secretName, componentName string, creds *DatabaseCredentials, dbHost string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{ + "app": componentName, + "helios.io/managed-by": "operator", + "helios.io/secret-type": "database-credentials", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "DB_USER": []byte(creds.Username), + "DB_PASS": []byte(creds.Password), + "DB_HOST": []byte(dbHost), + }, + } +} + +// GetDatabaseSecretName returns the conventional secret name for a component +func GetDatabaseSecretName(componentName string) string { + return fmt.Sprintf("%s-db-secret", componentName) +} + +// GetDatabaseHost returns the conventional database host for a component +func GetDatabaseHost(componentName string) string { + return fmt.Sprintf("%s-db", componentName) +} + +// ExtractDatabaseTraits extracts all database traits from HeliosApp components +func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { + ComponentName string + Properties DatabaseTraitProperties +} { + var dbTraits []struct { + ComponentName string + Properties DatabaseTraitProperties + } + + for _, component := range app.Spec.Components { + for _, trait := range component.Traits { + if strings.ToLower(trait.Type) == DatabaseTraitType { + var props DatabaseTraitProperties + if trait.Properties != nil && trait.Properties.Raw != nil { + if err := json.Unmarshal(trait.Properties.Raw, &props); err != nil { + // Log error but continue - don't fail the entire reconciliation + continue + } + } + dbTraits = append(dbTraits, struct { + ComponentName string + Properties DatabaseTraitProperties + }{ + ComponentName: component.Name, + Properties: props, + }) + } + } + } + + return dbTraits +} + +// reconcileDatabaseSecrets ensures database credential secrets exist for all +// components with database traits. If a secret already exists, it is not modified +// to preserve existing credentials. +func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping secret creation") + return nil + } + + for _, dbTrait := range dbTraits { + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + dbHost := GetDatabaseHost(dbTrait.ComponentName) + + // Check if secret already exists + existingSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{ + Name: secretName, + Namespace: app.Namespace, + }, existingSecret) + + if err == nil { + // Secret already exists - do not overwrite to preserve credentials + log.Info("Database secret already exists, skipping", + "component", dbTrait.ComponentName, + "secret", secretName) + continue + } + + if !errors.IsNotFound(err) { + // Unexpected error + log.Error(err, "Failed to check for existing database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to check for database secret %s: %w", secretName, err) + } + + // Secret doesn't exist - generate new credentials + log.Info("Generating database credentials", + "component", dbTrait.ComponentName, + "secret", secretName) + + creds, err := GenerateCredentials() + if err != nil { + log.Error(err, "Failed to generate database credentials", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to generate credentials for %s: %w", dbTrait.ComponentName, err) + } + + // Create the secret + secret := GenerateDatabaseSecret(app.Namespace, secretName, dbTrait.ComponentName, creds, dbHost) + + // Set owner reference so secret is garbage collected with the HeliosApp + if err := ctrl.SetControllerReference(app, secret, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to set owner reference for secret %s: %w", secretName, err) + } + + if err := r.Create(ctx, secret); err != nil { + if errors.IsAlreadyExists(err) { + // Race condition - secret was created between our check and create + log.Info("Database secret was created concurrently, skipping", + "component", dbTrait.ComponentName, + "secret", secretName) + continue + } + log.Error(err, "Failed to create database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to create database secret %s: %w", secretName, err) + } + + log.Info("Successfully created database secret", + "component", dbTrait.ComponentName, + "secret", secretName, + "dbHost", dbHost) + } + + return nil +} + +// reconcileDatabaseInstance provisions database StatefulSets and headless +// Services for components with database traits. This runs AFTER +// reconcileDatabaseSecrets so that the credential Secret already exists +// when the StatefulSet is created. +func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping instance provisioning") + return nil + } + + for _, dbTrait := range dbTraits { + // Only provision postgres instances for now + if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { + log.V(1).Info("Skipping non-postgres database type", + "component", dbTrait.ComponentName, + "dbType", dbTrait.Properties.DBType) + continue + } + + dbHost := GetDatabaseHost(dbTrait.ComponentName) + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + + // Determine effective database name + effectiveDBName := dbTrait.Properties.DBName + if effectiveDBName == "" { + effectiveDBName = fmt.Sprintf("%s-db", dbTrait.ComponentName) + } + + // Determine version — CUE schema requires version!, but we + // guard here defensively in case of direct API usage. + version := dbTrait.Properties.Version + if version == "" { + version = DefaultPostgresVersion + } + + // Determine port + port := dbTrait.Properties.Port + if port <= 0 { + port = DefaultPostgresPort + } + + // Determine storage + storage := dbTrait.Properties.Storage + if storage == "" { + storage = DefaultDatabaseStorage + } + + // --- StatefulSet --- + sts, err := GenerateDatabaseStatefulSet( + app.Namespace, dbHost, secretName, effectiveDBName, version, storage, int32(port), + ) + if err != nil { + log.Error(err, "Failed to generate database StatefulSet", + "component", dbTrait.ComponentName, "storage", storage) + return fmt.Errorf("failed to generate StatefulSet for %s: %w", dbHost, err) + } + + if err := ctrl.SetControllerReference(app, sts, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database StatefulSet", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to set owner reference for StatefulSet %s: %w", dbHost, err) + } + + existingSts := &appsv1.StatefulSet{} + err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) + if err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to check for StatefulSet %s: %w", dbHost, err) + } + + log.Info("Creating database StatefulSet", + "component", dbTrait.ComponentName, + "statefulset", dbHost, + "image", fmt.Sprintf("postgres:%s", version)) + + if err := r.Create(ctx, sts); err != nil { + if errors.IsAlreadyExists(err) { + log.Info("Database StatefulSet was created concurrently, skipping", + "component", dbTrait.ComponentName) + } else { + return fmt.Errorf("failed to create StatefulSet %s: %w", dbHost, err) + } + } + } else { + // Handle StatefulSet drift: update spec to match the new template + log.Info("Database StatefulSet already exists, updating if necessary", + "component", dbTrait.ComponentName, + "statefulset", dbHost) + + // We only update the mutable fields (Replicas, Template) + updatedSts := existingSts.DeepCopy() + updatedSts.Spec.Replicas = sts.Spec.Replicas + updatedSts.Spec.Template = sts.Spec.Template + + // We need to preserve the existing VolumeClaimTemplates when updating + updatedSts.Spec.VolumeClaimTemplates = existingSts.Spec.VolumeClaimTemplates + + if err := r.Update(ctx, updatedSts); err != nil { + return fmt.Errorf("failed to update StatefulSet %s: %w", dbHost, err) + } + } + + // --- Headless Service --- + svc := GenerateDatabaseService(app.Namespace, dbHost, int32(port)) + + if err := ctrl.SetControllerReference(app, svc, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database Service", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to set owner reference for Service %s: %w", dbHost, err) + } + + existingSvc := &corev1.Service{} + err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSvc) + if err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to check for Service %s: %w", dbHost, err) + } + + log.Info("Creating database headless Service", + "component", dbTrait.ComponentName, + "service", dbHost) + + if err := r.Create(ctx, svc); err != nil { + if errors.IsAlreadyExists(err) { + log.Info("Database Service was created concurrently, skipping", + "component", dbTrait.ComponentName) + } else { + return fmt.Errorf("failed to create Service %s: %w", dbHost, err) + } + } + } else { + log.Info("Database Service already exists, updating if necessary", + "component", dbTrait.ComponentName, + "service", dbHost) + + updatedSvc := existingSvc.DeepCopy() + updatedSvc.Spec.Ports = svc.Spec.Ports + + if err := r.Update(ctx, updatedSvc); err != nil { + return fmt.Errorf("failed to update Service %s: %w", dbHost, err) + } + } + + log.Info("Successfully reconciled database instance", + "component", dbTrait.ComponentName, + "statefulset", dbHost, + "dbName", effectiveDBName) + } + + return nil +} + +// GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. +// The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and +// uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. +func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) (*appsv1.StatefulSet, error) { + storageQty, err := resource.ParseQuantity(storage) + if err != nil { + return nil, fmt.Errorf("invalid storage size format %q: %w", storage, err) + } + + replicas := int32(1) + labels := map[string]string{ + "app": name, + "helios.io/managed-by": "operator", + "helios.io/trait": "database", + "helios.io/db-type": "postgres", + } + + return &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: appsv1.StatefulSetSpec{ + ServiceName: name, + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": name}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "postgres", + Image: fmt.Sprintf("postgres:%s", version), + Ports: []corev1.ContainerPort{ + { + ContainerPort: port, + Name: "postgres", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "POSTGRES_DB", + Value: dbName, + }, + { + Name: "POSTGRES_USER", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "DB_USER", + }, + }, + }, + { + Name: "POSTGRES_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "DB_PASS", + }, + }, + }, + { + // PGDATA tells Postgres where to store cluster data. + // Must match volumeMount + subPath to avoid lost+found conflicts. + Name: "PGDATA", + Value: PostgresDataPath + "/" + PostgresDataSubPath, + }, + { + // Ensure consistent UTF-8 encoding for all databases. + Name: "POSTGRES_INITDB_ARGS", + Value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C", + }, + { + // Explicitly set the custom port so that postgres knows to listen on it. + Name: "PGPORT", + Value: fmt.Sprintf("%d", port), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + MountPath: PostgresDataPath, + SubPath: PostgresDataSubPath, + }, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resourceMustParse("100m"), + corev1.ResourceMemory: resourceMustParse("256Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resourceMustParse("500m"), + corev1.ResourceMemory: resourceMustParse("512Mi"), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 10, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, + }, + }, + InitialDelaySeconds: 30, + PeriodSeconds: 10, + }, + }, + }, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "data", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: storageQty, + }, + }, + }, + }, + }, + }, + }, nil +} + +// GenerateDatabaseService creates a headless Service for a database StatefulSet. +// The headless Service (clusterIP: None) provides stable DNS resolution +// so resolves to the database pod. +func GenerateDatabaseService(namespace, name string, port int32) *corev1.Service { + labels := map[string]string{ + "app": name, + "helios.io/managed-by": "operator", + "helios.io/trait": "database", + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "None", + Selector: map[string]string{"app": name}, + Ports: []corev1.ServicePort{ + { + Port: port, + TargetPort: intstr.FromInt32(port), + Name: "db", + }, + }, + }, + } +} + +// resourceMustParse is a helper to parse resource quantities. Panics on invalid input. +func resourceMustParse(s string) resource.Quantity { + return resource.MustParse(s) +} + +// GenerateBase64Token generates a random base64-encoded token +// Useful for generating secure webhook secrets or API tokens +func GenerateBase64Token(byteLength int) (string, error) { + if byteLength <= 0 { + byteLength = 32 + } + + bytes := make([]byte, byteLength) + if _, err := rand.Read(bytes); err != nil { + return "", fmt.Errorf("failed to generate random bytes: %w", err) + } + + return base64.StdEncoding.EncodeToString(bytes), nil +} +=== +// database_resources.go handles database credential generation and Secret creation +// for components with database traits. +// +// The CUE engine generates ConfigMaps with database metadata (host, port, name), +// but credentials (username, password) are generated by this Go code for security +// reasons - secrets should never be stored in CUE definitions or GitOps repos. +package controller + +import ( + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "math/big" + "strings" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" +) + +const ( + // DefaultPasswordLength is the length of generated passwords + DefaultPasswordLength = 32 + + // DefaultUsernameLength is the length of generated usernames + DefaultUsernameLength = 16 + + // PasswordCharset contains valid characters for password generation + // Includes uppercase, lowercase, digits, and special characters + PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" + + // UsernameCharset contains valid characters for username generation + // Lowercase letters and digits only for database compatibility + UsernameCharset = "abcdefghijklmnopqrstuvwxyz0123456789" + + // DatabaseTraitType is the trait type identifier for database traits + DatabaseTraitType = "database" + + // DefaultPostgresVersion is the default Postgres image tag when not specified. + DefaultPostgresVersion = "16" + + // DefaultPostgresPort is the default port for Postgres. + DefaultPostgresPort = 5432 + + // DefaultDatabaseStorage is the default PVC size for database volumes. + DefaultDatabaseStorage = "1Gi" + + // PostgresDataPath is the mount path for Postgres data directory. + PostgresDataPath = "/var/lib/postgresql/data" + + // PostgresDataSubPath is the subPath within the PVC to avoid lost+found issues. + PostgresDataSubPath = "pgdata" +) + +// DatabaseCredentials holds generated database credentials +type DatabaseCredentials struct { + Username string + Password string +} + +// DatabaseTraitProperties represents the properties of a database trait +type DatabaseTraitProperties struct { + DBType string `json:"dbType"` + DBName string `json:"dbName"` + Port int `json:"port"` + Version string `json:"version"` + Storage string `json:"storage"` +} + +// GenerateSecurePassword generates a cryptographically secure random password +func GenerateSecurePassword(length int) (string, error) { + if length <= 0 { + length = DefaultPasswordLength + } + + password := make([]byte, length) + charsetLen := big.NewInt(int64(len(PasswordCharset))) + + for i := 0; i < length; i++ { + idx, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + password[i] = PasswordCharset[idx.Int64()] + } + + return string(password), nil +} + +// GenerateSecureUsername generates a cryptographically secure random username +func GenerateSecureUsername(length int) (string, error) { + if length <= 0 { + length = DefaultUsernameLength + } + + // Ensure username starts with a letter (database requirement) + username := make([]byte, length) + lettersOnly := "abcdefghijklmnopqrstuvwxyz" + lettersLen := big.NewInt(int64(len(lettersOnly))) + + // First character must be a letter + idx, err := rand.Int(rand.Reader, lettersLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + username[0] = lettersOnly[idx.Int64()] + + // Rest can be letters or digits + charsetLen := big.NewInt(int64(len(UsernameCharset))) + for i := 1; i < length; i++ { + idx, err := rand.Int(rand.Reader, charsetLen) + if err != nil { + return "", fmt.Errorf("failed to generate random index: %w", err) + } + username[i] = UsernameCharset[idx.Int64()] + } + + return string(username), nil +} + +// GenerateCredentials generates a new set of database credentials +func GenerateCredentials() (*DatabaseCredentials, error) { + username, err := GenerateSecureUsername(DefaultUsernameLength) + if err != nil { + return nil, fmt.Errorf("failed to generate username: %w", err) + } + + password, err := GenerateSecurePassword(DefaultPasswordLength) + if err != nil { + return nil, fmt.Errorf("failed to generate password: %w", err) + } + + return &DatabaseCredentials{ + Username: username, + Password: password, + }, nil +} + +// GenerateDatabaseSecret creates a Kubernetes Secret containing database credentials +func GenerateDatabaseSecret(namespace, secretName, componentName string, creds *DatabaseCredentials, dbHost string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{ + "app": componentName, + "helios.io/managed-by": "operator", + "helios.io/secret-type": "database-credentials", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "DB_USER": []byte(creds.Username), + "DB_PASS": []byte(creds.Password), + "DB_HOST": []byte(dbHost), + }, + } +} + +// GetDatabaseSecretName returns the conventional secret name for a component +func GetDatabaseSecretName(componentName string) string { + return fmt.Sprintf("%s-db-secret", componentName) +} + +// GetDatabaseHost returns the conventional database host for a component +func GetDatabaseHost(componentName string) string { + return fmt.Sprintf("%s-db", componentName) +} + +// ExtractDatabaseTraits extracts all database traits from HeliosApp components +func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { + ComponentName string + Properties DatabaseTraitProperties +} { + var dbTraits []struct { + ComponentName string + Properties DatabaseTraitProperties + } + + for _, component := range app.Spec.Components { + for _, trait := range component.Traits { + if strings.ToLower(trait.Type) == DatabaseTraitType { + var props DatabaseTraitProperties + if trait.Properties != nil && trait.Properties.Raw != nil { + if err := json.Unmarshal(trait.Properties.Raw, &props); err != nil { + // Log error but continue - don't fail the entire reconciliation + continue + } + } + dbTraits = append(dbTraits, struct { + ComponentName string + Properties DatabaseTraitProperties + }{ + ComponentName: component.Name, + Properties: props, + }) + } + } + } + + return dbTraits +} + +// reconcileDatabaseSecrets ensures database credential secrets exist for all +// components with database traits. If a secret already exists, it is not modified +// to preserve existing credentials. +func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping secret creation") + return nil + } + + for _, dbTrait := range dbTraits { + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + dbHost := GetDatabaseHost(dbTrait.ComponentName) + + // Check if secret already exists + existingSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{ + Name: secretName, + Namespace: app.Namespace, + }, existingSecret) + + if err == nil { + // Secret already exists - do not overwrite to preserve credentials + log.Info("Database secret already exists, skipping", + "component", dbTrait.ComponentName, + "secret", secretName) + continue + } + + if !errors.IsNotFound(err) { + // Unexpected error + log.Error(err, "Failed to check for existing database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to check for database secret %s: %w", secretName, err) + } + + // Secret doesn't exist - generate new credentials + log.Info("Generating database credentials", + "component", dbTrait.ComponentName, + "secret", secretName) + + creds, err := GenerateCredentials() + if err != nil { + log.Error(err, "Failed to generate database credentials", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to generate credentials for %s: %w", dbTrait.ComponentName, err) + } + + // Create the secret + secret := GenerateDatabaseSecret(app.Namespace, secretName, dbTrait.ComponentName, creds, dbHost) + + // Set owner reference so secret is garbage collected with the HeliosApp + if err := ctrl.SetControllerReference(app, secret, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to set owner reference for secret %s: %w", secretName, err) + } + + if err := r.Create(ctx, secret); err != nil { + if errors.IsAlreadyExists(err) { + // Race condition - secret was created between our check and create + log.Info("Database secret was created concurrently, skipping", + "component", dbTrait.ComponentName, + "secret", secretName) + continue + } + log.Error(err, "Failed to create database secret", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("failed to create database secret %s: %w", secretName, err) + } + + log.Info("Successfully created database secret", + "component", dbTrait.ComponentName, + "secret", secretName, + "dbHost", dbHost) + } + + return nil +} + +// reconcileDatabaseInstance provisions database StatefulSets and headless +// Services for components with database traits. This runs AFTER +// reconcileDatabaseSecrets so that the credential Secret already exists +// when the StatefulSet is created. +func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping instance provisioning") + return nil + } + + for _, dbTrait := range dbTraits { + // Only provision postgres instances for now + if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { + log.V(1).Info("Skipping non-postgres database type", + "component", dbTrait.ComponentName, + "dbType", dbTrait.Properties.DBType) + continue + } + + dbHost := GetDatabaseHost(dbTrait.ComponentName) + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + + // Determine effective database name + effectiveDBName := dbTrait.Properties.DBName + if effectiveDBName == "" { + effectiveDBName = fmt.Sprintf("%s-db", dbTrait.ComponentName) + } + + // Determine version — CUE schema requires version!, but we + // guard here defensively in case of direct API usage. + version := dbTrait.Properties.Version + if version == "" { + version = DefaultPostgresVersion + } + + // Determine port + port := dbTrait.Properties.Port + if port <= 0 { + port = DefaultPostgresPort + } + + // Determine storage + storage := dbTrait.Properties.Storage + if storage == "" { + storage = DefaultDatabaseStorage + } + + // --- StatefulSet --- + sts, err := GenerateDatabaseStatefulSet( + app.Namespace, dbHost, secretName, effectiveDBName, version, storage, int32(port), + ) + if err != nil { + log.Error(err, "Failed to generate database StatefulSet", + "component", dbTrait.ComponentName, "storage", storage) + return fmt.Errorf("failed to generate StatefulSet for %s: %w", dbHost, err) + } + + if err := ctrl.SetControllerReference(app, sts, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database StatefulSet", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to set owner reference for StatefulSet %s: %w", dbHost, err) + } + + existingSts := &appsv1.StatefulSet{} + err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) + if err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to check for StatefulSet %s: %w", dbHost, err) + } + + log.Info("Creating database StatefulSet", + "component", dbTrait.ComponentName, + "statefulset", dbHost, + "image", fmt.Sprintf("postgres:%s", version)) + + if err := r.Create(ctx, sts); err != nil { + if errors.IsAlreadyExists(err) { + log.Info("Database StatefulSet was created concurrently, skipping", + "component", dbTrait.ComponentName) + } else { + return fmt.Errorf("failed to create StatefulSet %s: %w", dbHost, err) + } + } + } else { + // Handle StatefulSet drift: update spec to match the new template + log.Info("Database StatefulSet already exists, updating if necessary", + "component", dbTrait.ComponentName, + "statefulset", dbHost) + + // We only update the mutable fields (Replicas, Template) + updatedSts := existingSts.DeepCopy() + updatedSts.Spec.Replicas = sts.Spec.Replicas + updatedSts.Spec.Template = sts.Spec.Template + + // We need to preserve the existing VolumeClaimTemplates when updating + updatedSts.Spec.VolumeClaimTemplates = existingSts.Spec.VolumeClaimTemplates + + if err := r.Update(ctx, updatedSts); err != nil { + return fmt.Errorf("failed to update StatefulSet %s: %w", dbHost, err) + } + } + + // --- Headless Service --- + svc := GenerateDatabaseService(app.Namespace, dbHost, int32(port)) + + if err := ctrl.SetControllerReference(app, svc, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for database Service", + "component", dbTrait.ComponentName) + return fmt.Errorf("failed to set owner reference for Service %s: %w", dbHost, err) + } + + existingSvc := &corev1.Service{} + err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSvc) + if err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to check for Service %s: %w", dbHost, err) + } + + log.Info("Creating database headless Service", + "component", dbTrait.ComponentName, + "service", dbHost) + + if err := r.Create(ctx, svc); err != nil { + if errors.IsAlreadyExists(err) { + log.Info("Database Service was created concurrently, skipping", + "component", dbTrait.ComponentName) + } else { + return fmt.Errorf("failed to create Service %s: %w", dbHost, err) + } + } + } else { + log.Info("Database Service already exists, updating if necessary", + "component", dbTrait.ComponentName, + "service", dbHost) + + updatedSvc := existingSvc.DeepCopy() + updatedSvc.Spec.Ports = svc.Spec.Ports + + if err := r.Update(ctx, updatedSvc); err != nil { + return fmt.Errorf("failed to update Service %s: %w", dbHost, err) + } + } + + log.Info("Successfully reconciled database instance", + "component", dbTrait.ComponentName, + "statefulset", dbHost, + "dbName", effectiveDBName) + } + + return nil +} + +// databaseEnvVarNames lists the env var names injected by the operator +// for database credential secret injection. +var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} + +// InjectDatabaseEnvVars patches a Deployment's first container to include +// DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. +// The function is idempotent — if the env vars already exist with the correct +// secretKeyRef, no changes are made and it returns false. +func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { + if len(deploy.Spec.Template.Spec.Containers) == 0 { + return false + } + + container := &deploy.Spec.Template.Spec.Containers[0] + + // Build a set of existing env var names for fast lookup. + existingEnvs := make(map[string]bool, len(container.Env)) + for _, ev := range container.Env { + existingEnvs[ev.Name] = true + } + + changed := false + for _, envName := range databaseEnvVarNames { + if existingEnvs[envName] { + continue + } + container.Env = append(container.Env, corev1.EnvVar{ + Name: envName, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: envName, + }, + }, + }) + changed = true + } + + return changed +} + +// reconcileDatabaseSecretInjection patches live Deployments (deployed by ArgoCD) +// to inject DB_HOST, DB_USER, DB_PASS env vars from the operator-managed Secret. +// This runs AFTER database secrets and instances are provisioned so that the +// Secret already exists when the Deployment references it. +func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + dbTraits := ExtractDatabaseTraits(app) + if len(dbTraits) == 0 { + log.V(1).Info("No database traits found, skipping secret injection") + return nil + } + + for _, dbTrait := range dbTraits { + secretName := GetDatabaseSecretName(dbTrait.ComponentName) + deployName := dbTrait.ComponentName + + // Fetch the live Deployment (created by ArgoCD via GitOps). + deploy := &appsv1.Deployment{} + err := r.Get(ctx, types.NamespacedName{ + Name: deployName, + Namespace: app.Namespace, + }, deploy) + if err != nil { + if errors.IsNotFound(err) { + // Deployment may not exist yet (ArgoCD hasn't synced). + // This is expected — we'll inject on the next reconcile. + log.Info("Deployment not found yet, will inject on next reconcile", + "component", dbTrait.ComponentName, + "deployment", deployName) + continue + } + return fmt.Errorf("failed to get Deployment %s: %w", deployName, err) + } + + // Inject env vars if not already present. + if !InjectDatabaseEnvVars(deploy, secretName) { + log.V(1).Info("Database env vars already injected, skipping", + "component", dbTrait.ComponentName, + "deployment", deployName) + continue + } + + if err := r.Update(ctx, deploy); err != nil { + return fmt.Errorf("failed to update Deployment %s with database env vars: %w", deployName, err) + } + + log.Info("Successfully injected database env vars into Deployment", + "component", dbTrait.ComponentName, + "deployment", deployName, + "secret", secretName) + } + + return nil +} + +// GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. +// The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and +// uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. +func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) (*appsv1.StatefulSet, error) { + storageQty, err := resource.ParseQuantity(storage) + if err != nil { + return nil, fmt.Errorf("invalid storage size format %q: %w", storage, err) + } + + replicas := int32(1) + labels := map[string]string{ + "app": name, + "helios.io/managed-by": "operator", + "helios.io/trait": "database", + "helios.io/db-type": "postgres", + } + + return &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: appsv1.StatefulSetSpec{ + ServiceName: name, + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": name}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "postgres", + Image: fmt.Sprintf("postgres:%s", version), + Ports: []corev1.ContainerPort{ + { + ContainerPort: port, + Name: "postgres", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "POSTGRES_DB", + Value: dbName, + }, + { + Name: "POSTGRES_USER", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "DB_USER", + }, + }, + }, + { + Name: "POSTGRES_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: "DB_PASS", + }, + }, + }, + { + // PGDATA tells Postgres where to store cluster data. + // Must match volumeMount + subPath to avoid lost+found conflicts. + Name: "PGDATA", + Value: PostgresDataPath + "/" + PostgresDataSubPath, + }, + { + // Ensure consistent UTF-8 encoding for all databases. + Name: "POSTGRES_INITDB_ARGS", + Value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C", + }, + { + // Explicitly set the custom port so that postgres knows to listen on it. + Name: "PGPORT", + Value: fmt.Sprintf("%d", port), + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + MountPath: PostgresDataPath, + SubPath: PostgresDataSubPath, + }, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resourceMustParse("100m"), + corev1.ResourceMemory: resourceMustParse("256Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resourceMustParse("500m"), + corev1.ResourceMemory: resourceMustParse("512Mi"), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 10, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, + }, + }, + InitialDelaySeconds: 30, + PeriodSeconds: 10, + }, + }, + }, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "data", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: storageQty, + }, + }, + }, + }, + }, + }, + }, nil +} + +// GenerateDatabaseService creates a headless Service for a database StatefulSet. +// The headless Service (clusterIP: None) provides stable DNS resolution +// so resolves to the database pod. +func GenerateDatabaseService(namespace, name string, port int32) *corev1.Service { + labels := map[string]string{ + "app": name, + "helios.io/managed-by": "operator", + "helios.io/trait": "database", + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "None", + Selector: map[string]string{"app": name}, + Ports: []corev1.ServicePort{ + { + Port: port, + TargetPort: intstr.FromInt32(port), + Name: "db", + }, + }, + }, + } +} + +// resourceMustParse is a helper to parse resource quantities. Panics on invalid input. +func resourceMustParse(s string) resource.Quantity { + return resource.MustParse(s) +} + +// GenerateBase64Token generates a random base64-encoded token +// Useful for generating secure webhook secrets or API tokens +func GenerateBase64Token(byteLength int) (string, error) { + if byteLength <= 0 { + byteLength = 32 + } + + bytes := make([]byte, byteLength) + if _, err := rand.Read(bytes); err != nil { + return "", fmt.Errorf("failed to generate random bytes: %w", err) + } + + return base64.StdEncoding.EncodeToString(bytes), nil +} +``` + +#### [heliosapp_controller.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/heliosapp_controller.go) + +Added **Phase 0.9** call after database instance provisioning: + +```diff:heliosapp_controller.go +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "cmp" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "os" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" + heliosCue "github.com/helios-platform-team/helios-platform/apps/operator/internal/cue" + "github.com/helios-platform-team/helios-platform/apps/operator/internal/gitops" +) + +// HeliosAppReconciler reconciles a HeliosApp object +type HeliosAppReconciler struct { + client.Client + Scheme *runtime.Scheme + CueEngine heliosCue.CueEngineInterface + // TektonRenderer renders Tekton CI/CD resources via CUE engine. + TektonRenderer heliosCue.TektonRendererInterface + // GitFactory allows injecting a custom GitOps client (e.g. for testing) + GitFactory func(string, string, string) gitops.GitOpsClientInterface +} + +// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/finalizers,verbs=update +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete + +// Reconcile handles the reconciliation loop for HeliosApp +// Controller does NOT iterate components/traits - all orchestration is in CUE +func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := logf.FromContext(ctx) + + // 1. Fetch HeliosApp CRD + var heliosApp appv1alpha1.HeliosApp + if err := r.Get(ctx, req.NamespacedName, &heliosApp); err != nil { + if errors.IsNotFound(err) { + log.Info("HeliosApp resource not found, ignoring") + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + log.Info("Reconciling HeliosApp", "name", heliosApp.Name, "namespace", heliosApp.Namespace) + + // 2. Map CRD to Application Model + appModel, err := r.mapCRDToModel(&heliosApp) + if err != nil { + log.Error(err, "Failed to map CRD to application model") + return ctrl.Result{}, err + } + + // 3. Render via CUE Engine + manifestBytes, err := r.CueEngine.Render(appModel) + if err != nil { + log.Error(err, "Failed to render application via CUE") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE rendering failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE -1 & 0: Tekton CI/CD Resources (Tasks, Pipeline, Triggers) + // All Tekton resources are rendered via CUE engine. + // ------------------------------------------------------------------ + if err := r.reconcileTektonResourcesCue(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile Tekton resources via CUE") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE Tekton rendering failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE 0.5: Database Credential Secrets + // Generate and store secure credentials for components with database traits. + // Secrets are created BEFORE GitOps sync to ensure credentials exist + // when the application is deployed. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseSecrets(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile database secrets") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret creation failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE 0.7: Database Instance Provisioning + // Provision StatefulSets and headless Services for database traits. + // Runs AFTER secrets so that the credential Secret already exists + // when the database pod starts. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseInstance(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile database instance") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database instance provisioning failed: %v", err)) + return ctrl.Result{}, err + } + + // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") + // This validation is for application workloads (GitOps pipeline downstream). + // We run this AFTER DB provisioning so databases can come up while app is building. + for _, comp := range appModel.App.Components { + // We can add more specific checks here based on component type + // For now, checks if 'image' property exists and is not empty for all components + // assuming all workloads need an image. + if img, ok := comp.Properties["image"].(string); !ok || img == "" { + msg := fmt.Sprintf("Component '%s' is waiting for image (likely building). Status: Pending.", comp.Name) + log.Info(msg) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhasePending, msg) + return ctrl.Result{}, nil // Wait for next update (CI/CD will update CR with image) + } + } + + // ------------------------------------------------------------------ + // PHASE 0.6: Trigger Initial PipelineRun (if not already done) + // ------------------------------------------------------------------ + if !heliosApp.Status.InitialBuildTriggered { + log.Info("Triggering initial PipelineRun for new HeliosApp") + + pipelineName := heliosApp.Spec.PipelineName + if pipelineName == "" { + pipelineName = "from-code-to-cluster" + } + pr, err := GeneratePipelineRunForManifestGeneration(&heliosApp, pipelineName) + if err != nil { + log.Error(err, "Failed to generate initial PipelineRun") + } else { + if err := ctrl.SetControllerReference(&heliosApp, pr, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for PipelineRun") + } + + if err := r.Client.Create(ctx, pr); err != nil { + if !errors.IsAlreadyExists(err) { + log.Error(err, "Failed to create initial PipelineRun") + } + } else { + log.Info("Created initial PipelineRun", "name", pr.GetName()) + } + + // Mark as triggered to avoid creating multiple PipelineRuns + heliosApp.Status.InitialBuildTriggered = true + if err := r.Status().Update(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to update InitialBuildTriggered status") + } + } + } + + // ------------------------------------------------------------------ + // PHASE 1: Render & GitOps (Moved below) + // ------------------------------------------------------------------ + + // 4. GitOps Helper: Get Token & Username + token := os.Getenv("GITHUB_TOKEN") + username := os.Getenv("GITHUB_USER") + if username == "" { + username = "git" // Default fallback + } + + if heliosApp.Spec.GitOpsSecretRef != "" { + var secret corev1.Secret + // Explicitly log the secret lookup attempt + if err := r.Get(ctx, types.NamespacedName{Name: heliosApp.Spec.GitOpsSecretRef, Namespace: heliosApp.Namespace}, &secret); err == nil { + if t, ok := secret.Data["token"]; ok { + token = string(t) + } else if p, ok := secret.Data["password"]; ok { + // Fallback to 'password' key (standard basic-auth secret from Tekton setup) + token = string(p) + } else { + log.Info("Secret found but 'token' or 'password' key is missing", "Secret", heliosApp.Spec.GitOpsSecretRef) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s missing 'token' key", heliosApp.Spec.GitOpsSecretRef)) + return ctrl.Result{}, nil + } + if u, ok := secret.Data["username"]; ok { + username = string(u) + } + } else { + log.Error(err, "Failed to get GitOps Secret", "Secret", heliosApp.Spec.GitOpsSecretRef) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s not found", heliosApp.Spec.GitOpsSecretRef)) + return ctrl.Result{}, nil + } + } + + // 5. GitOps Sync + + if token == "" { + err := fmt.Errorf("GitOps token is empty. Check Secret or GITHUB_TOKEN env var") + log.Error(err, "Authentication failed") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, "GitOps token missing") + return ctrl.Result{}, nil // Don't retry immediately if config is missing + } + + // OPTIMIZATION: Check Hash + currentHash := r.computeHash(manifestBytes) + if heliosApp.Status.LastAppliedHash == currentHash { + log.Info("Manifest hash unchanged, skipping GitOps sync", "hash", currentHash) + + // Still ensure status is Ready if it was previously set + if heliosApp.Status.Phase != appv1alpha1.PhaseReady { + heliosApp.Status.Phase = appv1alpha1.PhaseReady + if err := r.Status().Update(ctx, &heliosApp); err != nil { + return ctrl.Result{}, err + } + } + } else { + // Use GitFactory if available, otherwise default to NewGitOpsClient + getGitClient := r.GitFactory + if getGitClient == nil { + getGitClient = func(repo, user, token string) gitops.GitOpsClientInterface { + return gitops.NewGitOpsClient(repo, user, token) + } + } + + gitClient := getGitClient(heliosApp.Spec.GitOpsRepo, username, token) + targetPath := fmt.Sprintf("%s/manifest.yaml", heliosApp.Spec.GitOpsPath) + + if err := gitClient.SyncManifest(ctx, targetPath, string(manifestBytes)); err != nil { + log.Error(err, "GitOps sync failed") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("GitOps failed: %v", err)) + return ctrl.Result{}, err + } + + // 6. Update Status + heliosApp.Status.Phase = appv1alpha1.PhaseReady + heliosApp.Status.Message = fmt.Sprintf("Manifest pushed to %s/%s", heliosApp.Spec.GitOpsRepo, targetPath) + heliosApp.Status.LastAppliedHash = currentHash + // We clear ResourcesCreated as we are not managing them directly anymore + heliosApp.Status.ResourcesCreated = nil + + if err := r.Status().Update(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to update status") + return ctrl.Result{}, err + } + log.Info("Successfully reconciled HeliosApp via GitOps", "newHash", currentHash) + } + + // 7. Ensure ArgoCD Application exists + log.Info("Ensuring ArgoCD Application exists") + argoApp, err := GenerateArgoApplication(&heliosApp) + if err != nil { + log.Error(err, "Failed to generate ArgoCD Application manifest") + // We don't return error here to avoid loop if GitOps was successful, just log it. + // Or maybe we should retry? Let's log and continue for now. + } else { + // Define ArgoCD Application identity + argoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) + // We use Sever-Side Apply or Create/Update logic + // Since ArgoCD app is in "argocd" namespace usually, we need permissions there. + // For simplicity/demo: Try to get, if not found create. + + foundArgoApp := &unstructured.Unstructured{} + foundArgoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) + + key := client.ObjectKey{ + Name: argoApp.GetName(), + Namespace: argoApp.GetNamespace(), + } + + if err := r.Client.Get(ctx, key, foundArgoApp); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating ArgoCD Application", "name", argoApp.GetName()) + if err := r.Client.Create(ctx, argoApp); err != nil { + log.Error(err, "Failed to create ArgoCD Application") + } + } else { + log.Error(err, "Failed to get ArgoCD Application") + } + } else { + // Optional: Update if needed (checkout 'spec' diff) + log.Info("ArgoCD Application already exists", "name", argoApp.GetName()) + } + } + + // NOTE: Ingress removed - use port-forwarding for EventListener: + // kubectl port-forward svc/el--listener 8080:8080 + + return ctrl.Result{}, nil +} + +// mapCRDToModel converts HeliosApp CRD to CUE Application Model +func (r *HeliosAppReconciler) mapCRDToModel(app *appv1alpha1.HeliosApp) (heliosCue.Application, error) { + components := make([]heliosCue.Component, len(app.Spec.Components)) + + for i, c := range app.Spec.Components { + // Parse properties from RawExtension + var props map[string]any + if c.Properties != nil && c.Properties.Raw != nil { + if err := json.Unmarshal(c.Properties.Raw, &props); err != nil { + return heliosCue.Application{}, fmt.Errorf("failed to parse component properties: %w", err) + } + } + + // Parse traits + traits := make([]heliosCue.Trait, len(c.Traits)) + for j, t := range c.Traits { + var traitProps map[string]any + if t.Properties != nil && t.Properties.Raw != nil { + if err := json.Unmarshal(t.Properties.Raw, &traitProps); err != nil { + return heliosCue.Application{}, fmt.Errorf("failed to parse trait properties: %w", err) + } + } + traits[j] = heliosCue.Trait{ + Type: t.Type, + Properties: traitProps, + } + } + + components[i] = heliosCue.Component{ + Name: c.Name, + Type: c.Type, + Properties: props, + Traits: traits, + } + } + + return heliosCue.Application{ + App: heliosCue.AppSpec{ + Name: app.Name, + Namespace: app.Namespace, + Owner: app.Spec.Owner, + Description: app.Spec.Description, + Components: components, + }, + }, nil +} + +// mapCRDToTektonInput converts HeliosApp CRD to TektonInput for CUE rendering. +// This is the bridge between HeliosApp spec fields and the CUE #TektonInput schema. +func (r *HeliosAppReconciler) mapCRDToTektonInput(app *appv1alpha1.HeliosApp) heliosCue.TektonInput { + input := heliosCue.TektonInput{ + AppName: app.Name, + Namespace: app.Namespace, + GitRepo: app.Spec.GitRepo, + GitBranch: app.Spec.GitBranch, + ImageRepo: app.Spec.ImageRepo, + GitOpsRepo: app.Spec.GitOpsRepo, + GitOpsPath: app.Spec.GitOpsPath, + GitOpsBranch: app.Spec.GitOpsBranch, + GitOpsSecretRef: app.Spec.GitOpsSecretRef, + WebhookDomain: app.Spec.WebhookDomain, + WebhookSecret: app.Spec.WebhookSecret, + PipelineName: app.Spec.PipelineName, + PipelineType: app.Spec.PipelineName, // pipelineType uses same value as pipelineName + TriggerType: "github-push", // Default; extend HeliosAppSpec if needed + ServiceAccount: app.Spec.ServiceAccount, + PVCName: app.Spec.PVCName, + ContextSubpath: app.Spec.ContextSubpath, + Replicas: int(app.Spec.Replicas), + Port: int(app.Spec.Port), + TestCommand: app.Spec.TestCommand, + DockerSecret: "docker-credentials", + ArgoCDNamespace: app.Spec.ArgoCDNamespace, + ArgoCDProject: app.Spec.ArgoCDProject, + } + + // Apply defaults for fields that may be empty + input.GitBranch = cmp.Or(input.GitBranch, "main") + input.GitOpsBranch = cmp.Or(input.GitOpsBranch, "main") + input.GitOpsSecretRef = cmp.Or(input.GitOpsSecretRef, "github-credentials") + input.WebhookSecret = cmp.Or(input.WebhookSecret, "github-webhook-secret") + if input.PipelineName == "" { + input.PipelineName = "from-code-to-cluster" + input.PipelineType = "from-code-to-cluster" + } + input.ServiceAccount = cmp.Or(input.ServiceAccount, "default") + if input.Replicas <= 0 { + input.Replicas = 1 + } + if input.Port <= 0 { + input.Port = 8080 + } + + return input +} + +// reconcileTektonResourcesCue renders Tekton resources via CUE and applies them. +// This is the NEW path that replaces all hardcoded Generate* functions. +func (r *HeliosAppReconciler) reconcileTektonResourcesCue(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + // 1. Map CRD → TektonInput + tektonInput := r.mapCRDToTektonInput(app) + + // 2. Render via CUE + objects, err := r.TektonRenderer.RenderTektonResources(tektonInput) + if err != nil { + return fmt.Errorf("CUE TektonRenderer failed: %w", err) + } + + log.Info("CUE rendered Tekton resources", "count", len(objects)) + + // 3. Apply each rendered resource + for _, obj := range objects { + // Set owner reference (skip cluster-scoped resources) + if obj.GetNamespace() != "" { + if err := ctrl.SetControllerReference(app, obj, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference", "kind", obj.GetKind(), "name", obj.GetName()) + continue + } + } + + // Create or update + found := &unstructured.Unstructured{} + found.SetGroupVersionKind(obj.GroupVersionKind()) + err := r.Client.Get(ctx, client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()}, found) + if err != nil { + if errors.IsNotFound(err) { + log.Info("Creating resource", "kind", obj.GetKind(), "name", obj.GetName()) + if err := r.Client.Create(ctx, obj); err != nil { + log.Error(err, "Failed to create resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } else { + log.Error(err, "Failed to get resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } else { + // Update existing resource's spec + found.Object["spec"] = obj.Object["spec"] + if err := r.Client.Update(ctx, found); err != nil { + log.Error(err, "Failed to update resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } + } + + // 4. Also ensure RBAC (SA, RoleBinding, ClusterRoleBinding) — these are not in CUE yet + r.ensureTektonRBAC(ctx, app) + + return nil +} + +// ensureTektonRBAC creates ServiceAccount, RoleBinding, ClusterRoleBinding. +// These are infrastructure resources not managed by CUE (they are cluster lifecycle, not app lifecycle). +func (r *HeliosAppReconciler) ensureTektonRBAC(ctx context.Context, app *appv1alpha1.HeliosApp) { + log := logf.FromContext(ctx) + + sa := GenerateServiceAccount(app.Namespace) + if err := ctrl.SetControllerReference(app, sa, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for ServiceAccount") + } else { + foundSA := &unstructured.Unstructured{} + foundSA.SetGroupVersionKind(sa.GroupVersionKind()) + if err := r.Client.Get(ctx, client.ObjectKey{Name: sa.GetName(), Namespace: sa.GetNamespace()}, foundSA); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating ServiceAccount", "name", sa.GetName()) + r.Client.Create(ctx, sa) + } + } + } + + rb := GenerateRoleBinding(app.Namespace) + if err := ctrl.SetControllerReference(app, rb, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for RoleBinding") + } else { + foundRB := &unstructured.Unstructured{} + foundRB.SetGroupVersionKind(rb.GroupVersionKind()) + if err := r.Client.Get(ctx, client.ObjectKey{Name: rb.GetName(), Namespace: rb.GetNamespace()}, foundRB); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating RoleBinding", "name", rb.GetName()) + r.Client.Create(ctx, rb) + } + } + } + + crb := GenerateClusterRoleBinding(app.Namespace) + foundCrb := &unstructured.Unstructured{} + foundCrb.SetGroupVersionKind(crb.GroupVersionKind()) + if err := r.Client.Get(ctx, client.ObjectKey{Name: crb.GetName()}, foundCrb); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating ClusterRoleBinding", "name", crb.GetName()) + r.Client.Create(ctx, crb) + } + } +} + +// updateStatus updates the HeliosApp status +func (r *HeliosAppReconciler) updateStatus(ctx context.Context, app *appv1alpha1.HeliosApp, phase appv1alpha1.HeliosAppPhase, message string) { + app.Status.Phase = phase + app.Status.Message = message + if err := r.Status().Update(ctx, app); err != nil { + logf.FromContext(ctx).Error(err, "Failed to update status") + } +} + +// computeHash returns SHA256 of data +func (r *HeliosAppReconciler) computeHash(data []byte) string { + hash := sha256.Sum256(data) + return hex.EncodeToString(hash[:]) +} + +// SetupWithManager sets up the controller with the Manager +func (r *HeliosAppReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&appv1alpha1.HeliosApp{}). + Owns(&appsv1.Deployment{}). + Owns(&appsv1.StatefulSet{}). + Owns(&corev1.Service{}). + Owns(&networkingv1.Ingress{}). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret), + ). + Named("heliosapp"). + Complete(r) +} + +// findObjectsForSecret maps Secret changes to HeliosApp reconcile requests. +// This ensures the controller re-reconciles when a referenced secret changes. +func (r *HeliosAppReconciler) findObjectsForSecret(ctx context.Context, obj client.Object) []reconcile.Request { + log := logf.FromContext(ctx) + + // List all HeliosApps in the same namespace + var heliosAppList appv1alpha1.HeliosAppList + if err := r.List(ctx, &heliosAppList, client.InNamespace(obj.GetNamespace())); err != nil { + log.Error(err, "Failed to list HeliosApps for secret watch") + return nil + } + + var requests []reconcile.Request + for _, app := range heliosAppList.Items { + // Check if this app references the changed secret + if app.Spec.GitOpsSecretRef == obj.GetName() || + app.Spec.WebhookSecret == obj.GetName() { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: app.Name, + Namespace: app.Namespace, + }, + }) + } + } + + return requests +} +=== +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "cmp" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "os" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" + heliosCue "github.com/helios-platform-team/helios-platform/apps/operator/internal/cue" + "github.com/helios-platform-team/helios-platform/apps/operator/internal/gitops" +) + +// HeliosAppReconciler reconciles a HeliosApp object +type HeliosAppReconciler struct { + client.Client + Scheme *runtime.Scheme + CueEngine heliosCue.CueEngineInterface + // TektonRenderer renders Tekton CI/CD resources via CUE engine. + TektonRenderer heliosCue.TektonRendererInterface + // GitFactory allows injecting a custom GitOps client (e.g. for testing) + GitFactory func(string, string, string) gitops.GitOpsClientInterface +} + +// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/finalizers,verbs=update +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete + +// Reconcile handles the reconciliation loop for HeliosApp +// Controller does NOT iterate components/traits - all orchestration is in CUE +func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := logf.FromContext(ctx) + + // 1. Fetch HeliosApp CRD + var heliosApp appv1alpha1.HeliosApp + if err := r.Get(ctx, req.NamespacedName, &heliosApp); err != nil { + if errors.IsNotFound(err) { + log.Info("HeliosApp resource not found, ignoring") + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + log.Info("Reconciling HeliosApp", "name", heliosApp.Name, "namespace", heliosApp.Namespace) + + // 2. Map CRD to Application Model + appModel, err := r.mapCRDToModel(&heliosApp) + if err != nil { + log.Error(err, "Failed to map CRD to application model") + return ctrl.Result{}, err + } + + // 3. Render via CUE Engine + manifestBytes, err := r.CueEngine.Render(appModel) + if err != nil { + log.Error(err, "Failed to render application via CUE") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE rendering failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE -1 & 0: Tekton CI/CD Resources (Tasks, Pipeline, Triggers) + // All Tekton resources are rendered via CUE engine. + // ------------------------------------------------------------------ + if err := r.reconcileTektonResourcesCue(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile Tekton resources via CUE") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE Tekton rendering failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE 0.5: Database Credential Secrets + // Generate and store secure credentials for components with database traits. + // Secrets are created BEFORE GitOps sync to ensure credentials exist + // when the application is deployed. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseSecrets(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile database secrets") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret creation failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE 0.7: Database Instance Provisioning + // Provision StatefulSets and headless Services for database traits. + // Runs AFTER secrets so that the credential Secret already exists + // when the database pod starts. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseInstance(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to reconcile database instance") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database instance provisioning failed: %v", err)) + return ctrl.Result{}, err + } + + // ------------------------------------------------------------------ + // PHASE 0.9: Inject Database Credentials into Backend Deployment + // Patches the live Deployment (deployed by ArgoCD) to add DB_HOST, + // DB_USER, DB_PASS env vars referencing the operator-managed Secret. + // Runs AFTER secrets and instances so the Secret already exists. + // ------------------------------------------------------------------ + if err := r.reconcileDatabaseSecretInjection(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to inject database secrets into Deployment") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret injection failed: %v", err)) + return ctrl.Result{}, err + } + + // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") + // This validation is for application workloads (GitOps pipeline downstream). + // We run this AFTER DB provisioning so databases can come up while app is building. + for _, comp := range appModel.App.Components { + // We can add more specific checks here based on component type + // For now, checks if 'image' property exists and is not empty for all components + // assuming all workloads need an image. + if img, ok := comp.Properties["image"].(string); !ok || img == "" { + msg := fmt.Sprintf("Component '%s' is waiting for image (likely building). Status: Pending.", comp.Name) + log.Info(msg) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhasePending, msg) + return ctrl.Result{}, nil // Wait for next update (CI/CD will update CR with image) + } + } + + // ------------------------------------------------------------------ + // PHASE 0.6: Trigger Initial PipelineRun (if not already done) + // ------------------------------------------------------------------ + if !heliosApp.Status.InitialBuildTriggered { + log.Info("Triggering initial PipelineRun for new HeliosApp") + + pipelineName := heliosApp.Spec.PipelineName + if pipelineName == "" { + pipelineName = "from-code-to-cluster" + } + pr, err := GeneratePipelineRunForManifestGeneration(&heliosApp, pipelineName) + if err != nil { + log.Error(err, "Failed to generate initial PipelineRun") + } else { + if err := ctrl.SetControllerReference(&heliosApp, pr, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for PipelineRun") + } + + if err := r.Client.Create(ctx, pr); err != nil { + if !errors.IsAlreadyExists(err) { + log.Error(err, "Failed to create initial PipelineRun") + } + } else { + log.Info("Created initial PipelineRun", "name", pr.GetName()) + } + + // Mark as triggered to avoid creating multiple PipelineRuns + heliosApp.Status.InitialBuildTriggered = true + if err := r.Status().Update(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to update InitialBuildTriggered status") + } + } + } + + // ------------------------------------------------------------------ + // PHASE 1: Render & GitOps (Moved below) + // ------------------------------------------------------------------ + + // 4. GitOps Helper: Get Token & Username + token := os.Getenv("GITHUB_TOKEN") + username := os.Getenv("GITHUB_USER") + if username == "" { + username = "git" // Default fallback + } + + if heliosApp.Spec.GitOpsSecretRef != "" { + var secret corev1.Secret + // Explicitly log the secret lookup attempt + if err := r.Get(ctx, types.NamespacedName{Name: heliosApp.Spec.GitOpsSecretRef, Namespace: heliosApp.Namespace}, &secret); err == nil { + if t, ok := secret.Data["token"]; ok { + token = string(t) + } else if p, ok := secret.Data["password"]; ok { + // Fallback to 'password' key (standard basic-auth secret from Tekton setup) + token = string(p) + } else { + log.Info("Secret found but 'token' or 'password' key is missing", "Secret", heliosApp.Spec.GitOpsSecretRef) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s missing 'token' key", heliosApp.Spec.GitOpsSecretRef)) + return ctrl.Result{}, nil + } + if u, ok := secret.Data["username"]; ok { + username = string(u) + } + } else { + log.Error(err, "Failed to get GitOps Secret", "Secret", heliosApp.Spec.GitOpsSecretRef) + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s not found", heliosApp.Spec.GitOpsSecretRef)) + return ctrl.Result{}, nil + } + } + + // 5. GitOps Sync + + if token == "" { + err := fmt.Errorf("GitOps token is empty. Check Secret or GITHUB_TOKEN env var") + log.Error(err, "Authentication failed") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, "GitOps token missing") + return ctrl.Result{}, nil // Don't retry immediately if config is missing + } + + // OPTIMIZATION: Check Hash + currentHash := r.computeHash(manifestBytes) + if heliosApp.Status.LastAppliedHash == currentHash { + log.Info("Manifest hash unchanged, skipping GitOps sync", "hash", currentHash) + + // Still ensure status is Ready if it was previously set + if heliosApp.Status.Phase != appv1alpha1.PhaseReady { + heliosApp.Status.Phase = appv1alpha1.PhaseReady + if err := r.Status().Update(ctx, &heliosApp); err != nil { + return ctrl.Result{}, err + } + } + } else { + // Use GitFactory if available, otherwise default to NewGitOpsClient + getGitClient := r.GitFactory + if getGitClient == nil { + getGitClient = func(repo, user, token string) gitops.GitOpsClientInterface { + return gitops.NewGitOpsClient(repo, user, token) + } + } + + gitClient := getGitClient(heliosApp.Spec.GitOpsRepo, username, token) + targetPath := fmt.Sprintf("%s/manifest.yaml", heliosApp.Spec.GitOpsPath) + + if err := gitClient.SyncManifest(ctx, targetPath, string(manifestBytes)); err != nil { + log.Error(err, "GitOps sync failed") + r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("GitOps failed: %v", err)) + return ctrl.Result{}, err + } + + // 6. Update Status + heliosApp.Status.Phase = appv1alpha1.PhaseReady + heliosApp.Status.Message = fmt.Sprintf("Manifest pushed to %s/%s", heliosApp.Spec.GitOpsRepo, targetPath) + heliosApp.Status.LastAppliedHash = currentHash + // We clear ResourcesCreated as we are not managing them directly anymore + heliosApp.Status.ResourcesCreated = nil + + if err := r.Status().Update(ctx, &heliosApp); err != nil { + log.Error(err, "Failed to update status") + return ctrl.Result{}, err + } + log.Info("Successfully reconciled HeliosApp via GitOps", "newHash", currentHash) + } + + // 7. Ensure ArgoCD Application exists + log.Info("Ensuring ArgoCD Application exists") + argoApp, err := GenerateArgoApplication(&heliosApp) + if err != nil { + log.Error(err, "Failed to generate ArgoCD Application manifest") + // We don't return error here to avoid loop if GitOps was successful, just log it. + // Or maybe we should retry? Let's log and continue for now. + } else { + // Define ArgoCD Application identity + argoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) + // We use Sever-Side Apply or Create/Update logic + // Since ArgoCD app is in "argocd" namespace usually, we need permissions there. + // For simplicity/demo: Try to get, if not found create. + + foundArgoApp := &unstructured.Unstructured{} + foundArgoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) + + key := client.ObjectKey{ + Name: argoApp.GetName(), + Namespace: argoApp.GetNamespace(), + } + + if err := r.Client.Get(ctx, key, foundArgoApp); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating ArgoCD Application", "name", argoApp.GetName()) + if err := r.Client.Create(ctx, argoApp); err != nil { + log.Error(err, "Failed to create ArgoCD Application") + } + } else { + log.Error(err, "Failed to get ArgoCD Application") + } + } else { + // Optional: Update if needed (checkout 'spec' diff) + log.Info("ArgoCD Application already exists", "name", argoApp.GetName()) + } + } + + // NOTE: Ingress removed - use port-forwarding for EventListener: + // kubectl port-forward svc/el--listener 8080:8080 + + return ctrl.Result{}, nil +} + +// mapCRDToModel converts HeliosApp CRD to CUE Application Model +func (r *HeliosAppReconciler) mapCRDToModel(app *appv1alpha1.HeliosApp) (heliosCue.Application, error) { + components := make([]heliosCue.Component, len(app.Spec.Components)) + + for i, c := range app.Spec.Components { + // Parse properties from RawExtension + var props map[string]any + if c.Properties != nil && c.Properties.Raw != nil { + if err := json.Unmarshal(c.Properties.Raw, &props); err != nil { + return heliosCue.Application{}, fmt.Errorf("failed to parse component properties: %w", err) + } + } + + // Parse traits + traits := make([]heliosCue.Trait, len(c.Traits)) + for j, t := range c.Traits { + var traitProps map[string]any + if t.Properties != nil && t.Properties.Raw != nil { + if err := json.Unmarshal(t.Properties.Raw, &traitProps); err != nil { + return heliosCue.Application{}, fmt.Errorf("failed to parse trait properties: %w", err) + } + } + traits[j] = heliosCue.Trait{ + Type: t.Type, + Properties: traitProps, + } + } + + components[i] = heliosCue.Component{ + Name: c.Name, + Type: c.Type, + Properties: props, + Traits: traits, + } + } + + return heliosCue.Application{ + App: heliosCue.AppSpec{ + Name: app.Name, + Namespace: app.Namespace, + Owner: app.Spec.Owner, + Description: app.Spec.Description, + Components: components, + }, + }, nil +} + +// mapCRDToTektonInput converts HeliosApp CRD to TektonInput for CUE rendering. +// This is the bridge between HeliosApp spec fields and the CUE #TektonInput schema. +func (r *HeliosAppReconciler) mapCRDToTektonInput(app *appv1alpha1.HeliosApp) heliosCue.TektonInput { + input := heliosCue.TektonInput{ + AppName: app.Name, + Namespace: app.Namespace, + GitRepo: app.Spec.GitRepo, + GitBranch: app.Spec.GitBranch, + ImageRepo: app.Spec.ImageRepo, + GitOpsRepo: app.Spec.GitOpsRepo, + GitOpsPath: app.Spec.GitOpsPath, + GitOpsBranch: app.Spec.GitOpsBranch, + GitOpsSecretRef: app.Spec.GitOpsSecretRef, + WebhookDomain: app.Spec.WebhookDomain, + WebhookSecret: app.Spec.WebhookSecret, + PipelineName: app.Spec.PipelineName, + PipelineType: app.Spec.PipelineName, // pipelineType uses same value as pipelineName + TriggerType: "github-push", // Default; extend HeliosAppSpec if needed + ServiceAccount: app.Spec.ServiceAccount, + PVCName: app.Spec.PVCName, + ContextSubpath: app.Spec.ContextSubpath, + Replicas: int(app.Spec.Replicas), + Port: int(app.Spec.Port), + TestCommand: app.Spec.TestCommand, + DockerSecret: "docker-credentials", + ArgoCDNamespace: app.Spec.ArgoCDNamespace, + ArgoCDProject: app.Spec.ArgoCDProject, + } + + // Apply defaults for fields that may be empty + input.GitBranch = cmp.Or(input.GitBranch, "main") + input.GitOpsBranch = cmp.Or(input.GitOpsBranch, "main") + input.GitOpsSecretRef = cmp.Or(input.GitOpsSecretRef, "github-credentials") + input.WebhookSecret = cmp.Or(input.WebhookSecret, "github-webhook-secret") + if input.PipelineName == "" { + input.PipelineName = "from-code-to-cluster" + input.PipelineType = "from-code-to-cluster" + } + input.ServiceAccount = cmp.Or(input.ServiceAccount, "default") + if input.Replicas <= 0 { + input.Replicas = 1 + } + if input.Port <= 0 { + input.Port = 8080 + } + + return input +} + +// reconcileTektonResourcesCue renders Tekton resources via CUE and applies them. +// This is the NEW path that replaces all hardcoded Generate* functions. +func (r *HeliosAppReconciler) reconcileTektonResourcesCue(ctx context.Context, app *appv1alpha1.HeliosApp) error { + log := logf.FromContext(ctx) + + // 1. Map CRD → TektonInput + tektonInput := r.mapCRDToTektonInput(app) + + // 2. Render via CUE + objects, err := r.TektonRenderer.RenderTektonResources(tektonInput) + if err != nil { + return fmt.Errorf("CUE TektonRenderer failed: %w", err) + } + + log.Info("CUE rendered Tekton resources", "count", len(objects)) + + // 3. Apply each rendered resource + for _, obj := range objects { + // Set owner reference (skip cluster-scoped resources) + if obj.GetNamespace() != "" { + if err := ctrl.SetControllerReference(app, obj, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference", "kind", obj.GetKind(), "name", obj.GetName()) + continue + } + } + + // Create or update + found := &unstructured.Unstructured{} + found.SetGroupVersionKind(obj.GroupVersionKind()) + err := r.Client.Get(ctx, client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()}, found) + if err != nil { + if errors.IsNotFound(err) { + log.Info("Creating resource", "kind", obj.GetKind(), "name", obj.GetName()) + if err := r.Client.Create(ctx, obj); err != nil { + log.Error(err, "Failed to create resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } else { + log.Error(err, "Failed to get resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } else { + // Update existing resource's spec + found.Object["spec"] = obj.Object["spec"] + if err := r.Client.Update(ctx, found); err != nil { + log.Error(err, "Failed to update resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } + } + + // 4. Also ensure RBAC (SA, RoleBinding, ClusterRoleBinding) — these are not in CUE yet + r.ensureTektonRBAC(ctx, app) + + return nil +} + +// ensureTektonRBAC creates ServiceAccount, RoleBinding, ClusterRoleBinding. +// These are infrastructure resources not managed by CUE (they are cluster lifecycle, not app lifecycle). +func (r *HeliosAppReconciler) ensureTektonRBAC(ctx context.Context, app *appv1alpha1.HeliosApp) { + log := logf.FromContext(ctx) + + sa := GenerateServiceAccount(app.Namespace) + if err := ctrl.SetControllerReference(app, sa, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for ServiceAccount") + } else { + foundSA := &unstructured.Unstructured{} + foundSA.SetGroupVersionKind(sa.GroupVersionKind()) + if err := r.Client.Get(ctx, client.ObjectKey{Name: sa.GetName(), Namespace: sa.GetNamespace()}, foundSA); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating ServiceAccount", "name", sa.GetName()) + r.Client.Create(ctx, sa) + } + } + } + + rb := GenerateRoleBinding(app.Namespace) + if err := ctrl.SetControllerReference(app, rb, r.Scheme); err != nil { + log.Error(err, "Failed to set owner reference for RoleBinding") + } else { + foundRB := &unstructured.Unstructured{} + foundRB.SetGroupVersionKind(rb.GroupVersionKind()) + if err := r.Client.Get(ctx, client.ObjectKey{Name: rb.GetName(), Namespace: rb.GetNamespace()}, foundRB); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating RoleBinding", "name", rb.GetName()) + r.Client.Create(ctx, rb) + } + } + } + + crb := GenerateClusterRoleBinding(app.Namespace) + foundCrb := &unstructured.Unstructured{} + foundCrb.SetGroupVersionKind(crb.GroupVersionKind()) + if err := r.Client.Get(ctx, client.ObjectKey{Name: crb.GetName()}, foundCrb); err != nil { + if errors.IsNotFound(err) { + log.Info("Creating ClusterRoleBinding", "name", crb.GetName()) + r.Client.Create(ctx, crb) + } + } +} + +// updateStatus updates the HeliosApp status +func (r *HeliosAppReconciler) updateStatus(ctx context.Context, app *appv1alpha1.HeliosApp, phase appv1alpha1.HeliosAppPhase, message string) { + app.Status.Phase = phase + app.Status.Message = message + if err := r.Status().Update(ctx, app); err != nil { + logf.FromContext(ctx).Error(err, "Failed to update status") + } +} + +// computeHash returns SHA256 of data +func (r *HeliosAppReconciler) computeHash(data []byte) string { + hash := sha256.Sum256(data) + return hex.EncodeToString(hash[:]) +} + +// SetupWithManager sets up the controller with the Manager +func (r *HeliosAppReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&appv1alpha1.HeliosApp{}). + Owns(&appsv1.Deployment{}). + Owns(&appsv1.StatefulSet{}). + Owns(&corev1.Service{}). + Owns(&networkingv1.Ingress{}). + Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret), + ). + Named("heliosapp"). + Complete(r) +} + +// findObjectsForSecret maps Secret changes to HeliosApp reconcile requests. +// This ensures the controller re-reconciles when a referenced secret changes. +func (r *HeliosAppReconciler) findObjectsForSecret(ctx context.Context, obj client.Object) []reconcile.Request { + log := logf.FromContext(ctx) + + // List all HeliosApps in the same namespace + var heliosAppList appv1alpha1.HeliosAppList + if err := r.List(ctx, &heliosAppList, client.InNamespace(obj.GetNamespace())); err != nil { + log.Error(err, "Failed to list HeliosApps for secret watch") + return nil + } + + var requests []reconcile.Request + for _, app := range heliosAppList.Items { + // Check if this app references the changed secret + if app.Spec.GitOpsSecretRef == obj.GetName() || + app.Spec.WebhookSecret == obj.GetName() { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: app.Name, + Namespace: app.Namespace, + }, + }) + } + } + + return requests +} +``` + +#### [database_resources_test.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources_test.go) + +6 new tests: +| Test | Description | +|------|-------------| +| `TestInjectDatabaseEnvVars/InjectsAllEnvVars` | Verifies DB_HOST, DB_USER, DB_PASS injected with correct secretKeyRef | +| `TestInjectDatabaseEnvVars/Idempotent` | Running twice doesn't duplicate env vars | +| `TestInjectDatabaseEnvVars/NoContainers` | Handles edge case of empty container list | +| `TestReconcileDatabaseSecretInjection/InjectsIntoExistingDeployment` | Full reconciliation with fake K8s client | +| `TestReconcileDatabaseSecretInjection/SkipsWhenNoTraits` | No-op when no database traits | +| `TestReconcileDatabaseSecretInjection/DeploymentNotFound_GracefulSkip` | Graceful skip when ArgoCD hasn't deployed yet | + +--- + +### 2. NestJS + Prisma Template + +Created [nestjs-prisma-template/](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template) with: + +| File | Purpose | +|------|---------| +| [template.yaml](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/template/template.yaml) | Backstage scaffolder template with DatabasePicker | +| [content/source/package.json](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/advanced-template/content/source/package.json) | NestJS v11+ & Prisma v6+ dependencies | +| [content/source/prisma/schema.prisma](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma) | Prisma schema using `env("DATABASE_URL")` | +| [content/source/src/prisma/prisma.service.ts](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts) | **Key file** — constructs `DATABASE_URL` from `DB_HOST`, `DB_USER`, `DB_PASS` with `encodeURIComponent` | +| [content/source/src/prisma/prisma.module.ts](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.module.ts) | Global module exporting PrismaService | +| [content/source/Dockerfile](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/advanced-template/content/source/Dockerfile) | Multi-stage build, runs `prisma migrate deploy` at startup | +| [content/gitops/helios-app.yaml](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml) | HeliosApp CRD with database trait | + +--- + +## Verification Results + +| Check | Result | +|-------|--------| +| `go build ./...` | ✅ Passes | +| `go vet ./...` | ✅ No issues | +| `make test` | ✅ All tests pass (68% controller coverage) | +| `make build` | ✅ Operator binary builds | From fac30e548ac644be78f4110bc50ae4e23267bba3 Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 15 Mar 2026 17:31:10 +0700 Subject: [PATCH 13/19] fix: address PR review feedback and modernize Go to 1.26.1 PR review fixes: - Validate existing env var sources in InjectDatabaseEnvVars, not just names - Revert accidental local-dev image tag in kustomization.yaml - Requeue when Deployment is missing for database secret injection - Add ArgoCD ignoreDifferences to prevent self-heal reverting injected env vars - Fix broken NestJS template main.ts syntax - Fix catalog-info.yaml referencing undefined template variables - Remove committed planning docs (task.md, implementation_plan.md, walkthrough.md) Go 1.26.1 modernization: - Update Dockerfile and devcontainer from golang:1.24 to golang:1.26 - Use t.Context() instead of context.Background() in tests - Use for-range over integers instead of C-style loops - Use strings.SplitSeq instead of strings.Split in for-range - Update Go version references in documentation Co-Authored-By: Claude Opus 4.6 --- apps/operator/.devcontainer/devcontainer.json | 2 +- apps/operator/Dockerfile | 2 +- apps/operator/README.md | 2 +- .../config/manager/kustomization.yaml | 2 - .../internal/controller/argocd_resources.go | 11 + .../internal/controller/database_resources.go | 80 +- .../controller/database_resources_test.go | 97 +- .../controller/heliosapp_controller.go | 11 +- .../heliosapp_controller_unit_test.go | 6 +- apps/operator/test/utils/utils.go | 3 +- .../content/source/catalog-info.yaml | 2 +- .../content/source/src/main.ts | 7 +- docs/OPERATOR.md | 2 +- docs/SETUP.md | 2 +- implementation_plan.md | 115 - task.md | 28 - walkthrough.md | 2596 ----------------- 17 files changed, 170 insertions(+), 2798 deletions(-) delete mode 100644 implementation_plan.md delete mode 100644 task.md delete mode 100644 walkthrough.md diff --git a/apps/operator/.devcontainer/devcontainer.json b/apps/operator/.devcontainer/devcontainer.json index a3ab754..33ca8bc 100644 --- a/apps/operator/.devcontainer/devcontainer.json +++ b/apps/operator/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Kubebuilder DevContainer", - "image": "golang:1.24", + "image": "golang:1.26", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} diff --git a/apps/operator/Dockerfile b/apps/operator/Dockerfile index cb1b130..86e798a 100644 --- a/apps/operator/Dockerfile +++ b/apps/operator/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.24 AS builder +FROM golang:1.26 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/apps/operator/README.md b/apps/operator/README.md index a31c09a..cf6c79f 100644 --- a/apps/operator/README.md +++ b/apps/operator/README.md @@ -8,7 +8,7 @@ See [GIT_OPS_GUIDE.md](GIT_OPS_GUIDE.md) for detailed GitOps setup instructions. ### Prerequisites -- go version v1.24.0+ +- go version v1.26.0+ - docker version 17.03+. - kubectl version v1.11.3+. - Access to a Kubernetes v1.11.3+ cluster. diff --git a/apps/operator/config/manager/kustomization.yaml b/apps/operator/config/manager/kustomization.yaml index f919b93..9215473 100644 --- a/apps/operator/config/manager/kustomization.yaml +++ b/apps/operator/config/manager/kustomization.yaml @@ -4,5 +4,3 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: helios-operator - newTag: local diff --git a/apps/operator/internal/controller/argocd_resources.go b/apps/operator/internal/controller/argocd_resources.go index 5aca144..9e67943 100644 --- a/apps/operator/internal/controller/argocd_resources.go +++ b/apps/operator/internal/controller/argocd_resources.go @@ -45,6 +45,17 @@ func GenerateArgoApplication(heliosApp *appv1alpha1.HeliosApp) (*unstructured.Un "CreateNamespace=true", }, }, + // Ignore env var diffs on Deployments so that ArgoCD self-heal + // does not revert the DB_* env vars injected by the operator. + "ignoreDifferences": []any{ + map[string]any{ + "group": "apps", + "kind": "Deployment", + "jsonPointers": []any{ + "/spec/template/spec/containers/0/env", + }, + }, + }, }, } diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index e9f81c8..7712846 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -86,7 +86,7 @@ func GenerateSecurePassword(length int) (string, error) { password := make([]byte, length) charsetLen := big.NewInt(int64(len(PasswordCharset))) - for i := 0; i < length; i++ { + for i := range length { idx, err := rand.Int(rand.Reader, charsetLen) if err != nil { return "", fmt.Errorf("failed to generate random index: %w", err) @@ -459,6 +459,8 @@ var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} // DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. // The function is idempotent — if the env vars already exist with the correct // secretKeyRef, no changes are made and it returns false. +// If an env var exists but points to a different source (wrong secret, plain +// value, etc.), it is updated to the expected secretKeyRef. func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { if len(deploy.Spec.Template.Spec.Containers) == 0 { return false @@ -466,29 +468,46 @@ func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { container := &deploy.Spec.Template.Spec.Containers[0] - // Build a set of existing env var names for fast lookup. - existingEnvs := make(map[string]bool, len(container.Env)) - for _, ev := range container.Env { - existingEnvs[ev.Name] = true - } - changed := false for _, envName := range databaseEnvVarNames { - if existingEnvs[envName] { - continue - } - container.Env = append(container.Env, corev1.EnvVar{ - Name: envName, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: envName, + expectedRef := &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, }, + Key: envName, }, - }) - changed = true + } + + // Look for an existing env var with this name. + found := false + for i := range container.Env { + if container.Env[i].Name != envName { + continue + } + found = true + // If it already references the correct Secret/key, leave it as-is. + if container.Env[i].ValueFrom != nil && + container.Env[i].ValueFrom.SecretKeyRef != nil && + container.Env[i].ValueFrom.SecretKeyRef.Name == secretName && + container.Env[i].ValueFrom.SecretKeyRef.Key == envName { + break + } + // Otherwise, update it to use the expected secretKeyRef. + container.Env[i].Value = "" + container.Env[i].ValueFrom = expectedRef + changed = true + break + } + + // If no env var with this name exists, append a new one. + if !found { + container.Env = append(container.Env, corev1.EnvVar{ + Name: envName, + ValueFrom: expectedRef, + }) + changed = true + } } return changed @@ -498,15 +517,21 @@ func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { // to inject DB_HOST, DB_USER, DB_PASS env vars from the operator-managed Secret. // This runs AFTER database secrets and instances are provisioned so that the // Secret already exists when the Deployment references it. -func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Context, app *appv1alpha1.HeliosApp) error { +// +// Returns (pendingInjection, error). pendingInjection is true when one or more +// target Deployments do not exist yet (e.g. ArgoCD hasn't synced), signalling +// the caller to requeue so injection is retried. +func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Context, app *appv1alpha1.HeliosApp) (bool, error) { log := logf.FromContext(ctx) dbTraits := ExtractDatabaseTraits(app) if len(dbTraits) == 0 { log.V(1).Info("No database traits found, skipping secret injection") - return nil + return false, nil } + pendingInjection := false + for _, dbTrait := range dbTraits { secretName := GetDatabaseSecretName(dbTrait.ComponentName) deployName := dbTrait.ComponentName @@ -520,13 +545,14 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte if err != nil { if errors.IsNotFound(err) { // Deployment may not exist yet (ArgoCD hasn't synced). - // This is expected — we'll inject on the next reconcile. - log.Info("Deployment not found yet, will inject on next reconcile", + // Signal the caller to requeue so we retry injection later. + log.Info("Deployment not found yet, will requeue for injection", "component", dbTrait.ComponentName, "deployment", deployName) + pendingInjection = true continue } - return fmt.Errorf("failed to get Deployment %s: %w", deployName, err) + return false, fmt.Errorf("failed to get Deployment %s: %w", deployName, err) } // Inject env vars if not already present. @@ -538,7 +564,7 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte } if err := r.Update(ctx, deploy); err != nil { - return fmt.Errorf("failed to update Deployment %s with database env vars: %w", deployName, err) + return false, fmt.Errorf("failed to update Deployment %s with database env vars: %w", deployName, err) } log.Info("Successfully injected database env vars into Deployment", @@ -547,7 +573,7 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte "secret", secretName) } - return nil + return pendingInjection, nil } // GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index cb6fde1..9d2a29d 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -1,7 +1,6 @@ package controller import ( - "context" "encoding/json" "strings" "testing" @@ -138,7 +137,7 @@ func TestGenerateCredentialsUniqueness(t *testing.T) { credentials := make(map[string]bool) iterations := 100 - for i := 0; i < iterations; i++ { + for i := range iterations { creds, err := GenerateCredentials() if err != nil { t.Fatalf("GenerateCredentials failed on iteration %d: %v", i, err) @@ -363,7 +362,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { Scheme: scheme, } - ctx := context.Background() + ctx := t.Context() err := r.reconcileDatabaseSecrets(ctx, app) if err != nil { t.Fatalf("reconcileDatabaseSecrets failed: %v", err) @@ -414,7 +413,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { Scheme: scheme, } - ctx := context.Background() + ctx := t.Context() err := r.reconcileDatabaseSecrets(ctx, app) if err != nil { t.Fatalf("reconcileDatabaseSecrets failed: %v", err) @@ -471,7 +470,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { Scheme: scheme, } - ctx := context.Background() + ctx := t.Context() err := r.reconcileDatabaseSecrets(ctx, appWithoutDB) if err != nil { t.Fatalf("reconcileDatabaseSecrets should not fail for app without database traits: %v", err) @@ -786,7 +785,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { Scheme: fullScheme, } - ctx := context.Background() + ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, app) if err != nil { t.Fatalf("reconcileDatabaseInstance failed: %v", err) @@ -868,7 +867,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { Scheme: fullScheme, } - ctx := context.Background() + ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, appWithoutDB) if err != nil { t.Fatalf("reconcileDatabaseInstance should not fail for app without database traits: %v", err) @@ -934,7 +933,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { Scheme: fullScheme, } - ctx := context.Background() + ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, appWithRedis) if err != nil { t.Fatalf("reconcileDatabaseInstance should not fail for redis type: %v", err) @@ -1038,6 +1037,67 @@ func TestInjectDatabaseEnvVars(t *testing.T) { } }) + t.Run("UpdatesExistingWrongSource", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "api-server", + Image: "myregistry/api:v1", + Env: []corev1.EnvVar{ + {Name: "PORT", Value: "3000"}, + {Name: "DB_HOST", Value: "hardcoded-host"}, + {Name: "DB_USER", ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "wrong-secret"}, + Key: "DB_USER", + }, + }}, + }, + }, + }, + }, + }, + }, + } + + changed := InjectDatabaseEnvVars(deploy, "api-server-db-secret") + if !changed { + t.Fatal("Expected InjectDatabaseEnvVars to return true when existing env vars have wrong source") + } + + container := deploy.Spec.Template.Spec.Containers[0] + // Should have PORT + DB_HOST + DB_USER + DB_PASS = 4 + if len(container.Env) != 4 { + t.Fatalf("Expected 4 env vars, got %d", len(container.Env)) + } + + // DB_HOST should now reference the secret, not a plain value + for _, env := range container.Env { + if env.Name == "DB_HOST" { + if env.Value != "" { + t.Error("DB_HOST should have Value cleared") + } + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Fatal("DB_HOST should reference a secret") + } + if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + t.Errorf("DB_HOST: expected secret name %q, got %q", "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + } + } + if env.Name == "DB_USER" { + if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { + t.Fatal("DB_USER should reference a secret") + } + if env.ValueFrom.SecretKeyRef.Name != "api-server-db-secret" { + t.Errorf("DB_USER: expected secret name %q, got %q", "api-server-db-secret", env.ValueFrom.SecretKeyRef.Name) + } + } + } + }) + t.Run("NoContainers", func(t *testing.T) { deploy := &appsv1.Deployment{ Spec: appsv1.DeploymentSpec{ @@ -1132,11 +1192,14 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { Scheme: fullScheme, } - ctx := context.Background() - err := r.reconcileDatabaseSecretInjection(ctx, app) + ctx := t.Context() + pending, err := r.reconcileDatabaseSecretInjection(ctx, app) if err != nil { t.Fatalf("reconcileDatabaseSecretInjection failed: %v", err) } + if pending { + t.Error("Expected no pending injection when Deployment exists") + } // Verify the Deployment was updated with DB env vars updatedDeploy := &appsv1.Deployment{} @@ -1204,11 +1267,14 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { Scheme: fullScheme, } - ctx := context.Background() - err := r.reconcileDatabaseSecretInjection(ctx, appWithoutDB) + ctx := t.Context() + pending, err := r.reconcileDatabaseSecretInjection(ctx, appWithoutDB) if err != nil { t.Fatalf("reconcileDatabaseSecretInjection should not fail for app without database traits: %v", err) } + if pending { + t.Error("Expected no pending injection for app without database traits") + } }) t.Run("DeploymentNotFound_GracefulSkip", func(t *testing.T) { @@ -1229,10 +1295,13 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { Scheme: fullScheme, } - ctx := context.Background() - err := r.reconcileDatabaseSecretInjection(ctx, app) + ctx := t.Context() + pending, err := r.reconcileDatabaseSecretInjection(ctx, app) if err != nil { t.Fatalf("reconcileDatabaseSecretInjection should not fail when Deployment is missing: %v", err) } + if !pending { + t.Error("Expected pending=true when Deployment is missing") + } }) } diff --git a/apps/operator/internal/controller/heliosapp_controller.go b/apps/operator/internal/controller/heliosapp_controller.go index 01be8f8..a7fd738 100644 --- a/apps/operator/internal/controller/heliosapp_controller.go +++ b/apps/operator/internal/controller/heliosapp_controller.go @@ -24,6 +24,7 @@ import ( "encoding/json" "fmt" "os" + "time" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -133,7 +134,8 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // DB_USER, DB_PASS env vars referencing the operator-managed Secret. // Runs AFTER secrets and instances so the Secret already exists. // ------------------------------------------------------------------ - if err := r.reconcileDatabaseSecretInjection(ctx, &heliosApp); err != nil { + dbInjectionPending, err := r.reconcileDatabaseSecretInjection(ctx, &heliosApp) + if err != nil { log.Error(err, "Failed to inject database secrets into Deployment") r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret injection failed: %v", err)) return ctrl.Result{}, err @@ -316,6 +318,13 @@ func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // NOTE: Ingress removed - use port-forwarding for EventListener: // kubectl port-forward svc/el--listener 8080:8080 + // If database secret injection is pending (Deployment not yet created by + // ArgoCD), requeue so the operator retries once the Deployment appears. + if dbInjectionPending { + log.Info("Database secret injection pending, requeuing") + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + } + return ctrl.Result{}, nil } diff --git a/apps/operator/internal/controller/heliosapp_controller_unit_test.go b/apps/operator/internal/controller/heliosapp_controller_unit_test.go index 5a52716..133e6d9 100644 --- a/apps/operator/internal/controller/heliosapp_controller_unit_test.go +++ b/apps/operator/internal/controller/heliosapp_controller_unit_test.go @@ -119,7 +119,7 @@ func TestHeliosAppReconciler_Reconcile_Success(t *testing.T) { }, } - ctx := context.Background() + ctx := t.Context() res, err := r.Reconcile(ctx, req) // 7. Assertions @@ -180,7 +180,7 @@ func TestHeliosAppReconciler_Reconcile_PendingImage(t *testing.T) { TektonRenderer: &FakeTektonRenderer{}, } - res, err := r.Reconcile(context.Background(), ctrl.Request{NamespacedName: types.NamespacedName{Name: "pending-app", Namespace: "default"}}) + res, err := r.Reconcile(t.Context(), ctrl.Request{NamespacedName: types.NamespacedName{Name: "pending-app", Namespace: "default"}}) if err != nil { t.Errorf("Reconcile() error = %v, wantErr %v", err, nil) @@ -190,7 +190,7 @@ func TestHeliosAppReconciler_Reconcile_PendingImage(t *testing.T) { } updatedApp := &appv1alpha1.HeliosApp{} - _ = client.Get(context.Background(), types.NamespacedName{Name: "pending-app", Namespace: "default"}, updatedApp) + _ = client.Get(t.Context(), types.NamespacedName{Name: "pending-app", Namespace: "default"}, updatedApp) if updatedApp.Status.Phase != appv1alpha1.PhasePending { t.Errorf("Expected Phase to be %s, got %s", appv1alpha1.PhasePending, updatedApp.Status.Phase) } diff --git a/apps/operator/test/utils/utils.go b/apps/operator/test/utils/utils.go index 448055b..45b7f65 100644 --- a/apps/operator/test/utils/utils.go +++ b/apps/operator/test/utils/utils.go @@ -181,8 +181,7 @@ func LoadImageToKindClusterWithName(name string) error { // according to line breakers, and ignores the empty elements in it. func GetNonEmptyLines(output string) []string { var res []string - elements := strings.Split(output, "\n") - for _, element := range elements { + for element := range strings.SplitSeq(output, "\n") { if element != "" { res = append(res, element) } diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml b/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml index 5185c92..7212d13 100644 --- a/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml +++ b/apps/portal/examples/nestjs-prisma-template/content/source/catalog-info.yaml @@ -4,7 +4,7 @@ metadata: name: ${{ values.name }} description: ${{ values.description }} annotations: - github.com/project-slug: ${{ values.destination.owner + "/" + values.destination.repo }} + github.com/project-slug: ${{ values.owner }}/${{ values.name }} backstage.io/techdocs-ref: dir:. spec: type: service diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts index 37a6af9..2477e89 100644 --- a/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/main.ts @@ -4,10 +4,9 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); - const port = process.env.PORT || ${{ values.port } -}; -await app.listen(port); + const port = process.env.PORT || ${{ values.port }}; + await app.listen(port); -console.log(`🚀 Application is running on port ${port}`); + console.log(`Application is running on port ${port}`); } bootstrap(); diff --git a/docs/OPERATOR.md b/docs/OPERATOR.md index fc7fb04..7063ea1 100644 --- a/docs/OPERATOR.md +++ b/docs/OPERATOR.md @@ -12,7 +12,7 @@ The **Helios Operator** is the core control plane component of the Helios Platfo To develop or run the operator, you need: -- **Go**: v1.24.0+ +- **Go**: v1.26.0+ - **Docker**: 17.03+ - **kubectl**: v1.11.3+ - **Kubernetes Cluster**: Access to a `v1.11.3+` cluster (e.g., Kind, Minikube, or EKS). diff --git a/docs/SETUP.md b/docs/SETUP.md index 2af996e..53d98a5 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -6,7 +6,7 @@ This guide explains how to install necessary development tools for the Helios Pl Ensure you have the following installed: -- **Go**: v1.24.0+ +- **Go**: v1.26.0+ - **Docker**: 17.03+ - **kubectl**: v1.11.3+ diff --git a/implementation_plan.md b/implementation_plan.md deleted file mode 100644 index 7408765..0000000 --- a/implementation_plan.md +++ /dev/null @@ -1,115 +0,0 @@ -# Automated Secret Injection (#39) - -Inject database credentials (DB_HOST, DB_USER, DB_PASS) from the Operator-generated K8s Secret into the Backend Pod's env block, and create a NestJS + Prisma Node.js template that connects using these injected env vars. - -## Proposed Changes - -### Operator — Secret Injection into Backend Deployment - -The CUE engine renders a Deployment for each component (via GitOps sync). After the GitOps sync pushes the manifest, ArgoCD deploys it. However, the Deployment currently has **no database env vars** — the CUE `#Deployment` base only supports simple `{name, value}` env vars, not `secretKeyRef`. - -**Approach**: Add a new reconciliation phase (**Phase 0.9**) in [heliosapp_controller.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/heliosapp_controller.go) that **patches the live Deployment** (deployed by ArgoCD) to inject `DB_HOST`, `DB_USER`, and `DB_PASS` as `envFrom` / env vars referencing the K8s Secret. This runs AFTER database secrets and instances are created. - -> [!IMPORTANT] -> The CUE engine generates the base Deployment manifest (pushed via GitOps). The Go operator patches the **live Deployment** in-cluster to add secret env vars. This keeps secrets out of the GitOps repo entirely. - -#### [MODIFY] [database_resources.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go) - -Add a new function `InjectDatabaseEnvVars` that: -- Takes a Deployment and a database secret name -- Adds `DB_HOST`, `DB_USER`, `DB_PASS` env vars to the first container using `valueFrom.secretKeyRef` -- Also adds `DATABASE_URL` as a convenience env var for Prisma ORM (constructed from the other vars via an init container or as a direct string referencing the secret values) -- Is idempotent: skips if env vars already exist - -Add a new reconciler method `reconcileDatabaseSecretInjection` that: -- Finds components with database traits -- Gets or waits for the corresponding Deployment (by component name) -- Calls `InjectDatabaseEnvVars` to patch the Deployment's env block -- Uses `r.Update()` to apply changes to the live Deployment - -#### [MODIFY] [heliosapp_controller.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/heliosapp_controller.go) - -- Add a call to `r.reconcileDatabaseSecretInjection(ctx, &heliosApp)` as **Phase 0.9** (after database instance provisioning at Phase 0.7, before image validation) -- Add structured logging for the injection phase - -#### [MODIFY] [database_resources_test.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources_test.go) - -Add new test cases: -- `TestInjectDatabaseEnvVars` — verifies env vars are correctly injected into a Deployment -- `TestInjectDatabaseEnvVars_Idempotent` — verifies running injection twice doesn't duplicate env vars -- `TestReconcileDatabaseSecretInjection` — integration test with fake client verifying the full reconciliation flow -- `TestReconcileDatabaseSecretInjection_NoTraits` — skips when no database traits -- `TestReconcileDatabaseSecretInjection_DeploymentNotFound` — handles missing Deployment gracefully (returns nil, logs warning — Deployment may not be deployed by ArgoCD yet) - ---- - -### Node.js Template — NestJS + Prisma ORM - -Create a new Backstage template for NestJS + Prisma that connects to the database using the injected env vars. - -#### [NEW] `apps/portal/examples/nestjs-prisma-template/` directory - -Structure: -``` -nestjs-prisma-template/ - template.yaml # Backstage scaffolder template - content/ - source/ - package.json # NestJS + Prisma deps (latest) - tsconfig.json # TypeScript config - tsconfig.build.json # Build-specific TS config - nest-cli.json # NestJS CLI config - .env.example # Example env vars - Dockerfile # Multi-stage NestJS build - catalog-info.yaml # Backstage catalog entry - prisma/ - schema.prisma # Prisma schema with env-based DATABASE_URL - src/ - main.ts # NestJS bootstrap - app.module.ts # Root module - app.controller.ts # Health check controller - app.service.ts # App service - prisma/ - prisma.module.ts # Prisma module - prisma.service.ts # Prisma service (extends PrismaClient) - gitops/ - helios-app.yaml # HeliosApp CRD manifest with database trait -``` - -Key design decisions: -- `DATABASE_URL` is built from `DB_HOST`, `DB_USER`, `DB_PASS` env vars (Prisma convention) -- The Prisma schema reads `DATABASE_URL` from `env("DATABASE_URL")` -- The [Dockerfile](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/Dockerfile) runs `prisma generate` at build time and `prisma migrate deploy` at startup -- Uses latest stable NestJS v11+ and Prisma v6+ - ---- - -## Verification Plan - -### Automated Tests - -All existing tests plus new tests, run from `apps/operator/`: - -```sh -# 1. Compilation check -go build ./... - -# 2. Static analysis -go vet ./... - -# 3. Full test suite (includes all database_resources_test.go tests) -make test - -# 4. Build operator binary -make build - -# 5. Run database-specific tests in verbose mode -go test ./internal/controller/... -v -count=1 -run TestInjectDatabaseEnvVars -go test ./internal/controller/... -v -count=1 -run TestReconcileDatabaseSecretInjection -``` - -### Manual Verification - -After deploying a backend with a database trait: -1. Run `kubectl describe pod ` to verify DB_HOST, DB_USER, DB_PASS are present as environment variables referencing the secret -2. Verify the NestJS application boots and connects to the database by checking pod logs diff --git a/task.md b/task.md deleted file mode 100644 index 5efd769..0000000 --- a/task.md +++ /dev/null @@ -1,28 +0,0 @@ -# [Impl] Automated Secret Injection #39 - -## Operator — Secret Injection into Backend Deployment - -- [x] Research codebase and understand existing patterns -- [x] Implement [reconcileDatabaseSecretInjection](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#497-552) in Go operator - - [x] Add new phase (0.9) to inject DB_HOST, DB_USER, DB_PASS into backend Deployment env block - - [x] Patch the Deployment rendered by CUE/GitOps with env secretKeyRef -- [x] Add comprehensive tests for secret injection logic - - [x] Unit tests for injection function (3 tests) - - [x] Integration test with fake client (3 tests) - -## Node.js Template — NestJS + Prisma ORM - -- [x] Create NestJS + Prisma template directory structure - - [x] [package.json](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/advanced-template/content/source/package.json) with latest NestJS and Prisma deps - - [x] [prisma/schema.prisma](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma) connecting via DB_HOST, DB_USER, DB_PASS envs - - [x] `prisma/migrations/` with initial migration - - [x] NestJS main app module with Prisma service - - [x] [Dockerfile](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/Dockerfile) for containerized deployment -- [x] Update Backstage template for database-backed services - -## Verification - -- [/] `go build ./...` — compiles cleanly -- [x] `go vet ./...` — no issues -- [x] `make test` — all tests pass -- [/] `make build` — operator binary builds diff --git a/walkthrough.md b/walkthrough.md deleted file mode 100644 index 450130a..0000000 --- a/walkthrough.md +++ /dev/null @@ -1,2596 +0,0 @@ -# Automated Secret Injection (#39) — Walkthrough - -## Changes Made - -### 1. Go Operator — Secret Injection (Phase 0.9) - -#### [database_resources.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go) - -- **[InjectDatabaseEnvVars](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#458-496)** — Patches a Deployment's first container with `DB_HOST`, `DB_USER`, `DB_PASS` env vars via `secretKeyRef`. Idempotent: skips if vars already exist. -- **[reconcileDatabaseSecretInjection](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#497-552)** — Finds components with database traits, fetches the live Deployment, calls [InjectDatabaseEnvVars](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources.go#458-496), and updates it. Gracefully skips if Deployment doesn't exist yet (ArgoCD hasn't synced). - -```diff:database_resources.go -// database_resources.go handles database credential generation and Secret creation -// for components with database traits. -// -// The CUE engine generates ConfigMaps with database metadata (host, port, name), -// but credentials (username, password) are generated by this Go code for security -// reasons - secrets should never be stored in CUE definitions or GitOps repos. -package controller - -import ( - "context" - "crypto/rand" - "encoding/base64" - "encoding/json" - "fmt" - "math/big" - "strings" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" -) - -const ( - // DefaultPasswordLength is the length of generated passwords - DefaultPasswordLength = 32 - - // DefaultUsernameLength is the length of generated usernames - DefaultUsernameLength = 16 - - // PasswordCharset contains valid characters for password generation - // Includes uppercase, lowercase, digits, and special characters - PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" - - // UsernameCharset contains valid characters for username generation - // Lowercase letters and digits only for database compatibility - UsernameCharset = "abcdefghijklmnopqrstuvwxyz0123456789" - - // DatabaseTraitType is the trait type identifier for database traits - DatabaseTraitType = "database" - - // DefaultPostgresVersion is the default Postgres image tag when not specified. - DefaultPostgresVersion = "16" - - // DefaultPostgresPort is the default port for Postgres. - DefaultPostgresPort = 5432 - - // DefaultDatabaseStorage is the default PVC size for database volumes. - DefaultDatabaseStorage = "1Gi" - - // PostgresDataPath is the mount path for Postgres data directory. - PostgresDataPath = "/var/lib/postgresql/data" - - // PostgresDataSubPath is the subPath within the PVC to avoid lost+found issues. - PostgresDataSubPath = "pgdata" -) - -// DatabaseCredentials holds generated database credentials -type DatabaseCredentials struct { - Username string - Password string -} - -// DatabaseTraitProperties represents the properties of a database trait -type DatabaseTraitProperties struct { - DBType string `json:"dbType"` - DBName string `json:"dbName"` - Port int `json:"port"` - Version string `json:"version"` - Storage string `json:"storage"` -} - -// GenerateSecurePassword generates a cryptographically secure random password -func GenerateSecurePassword(length int) (string, error) { - if length <= 0 { - length = DefaultPasswordLength - } - - password := make([]byte, length) - charsetLen := big.NewInt(int64(len(PasswordCharset))) - - for i := 0; i < length; i++ { - idx, err := rand.Int(rand.Reader, charsetLen) - if err != nil { - return "", fmt.Errorf("failed to generate random index: %w", err) - } - password[i] = PasswordCharset[idx.Int64()] - } - - return string(password), nil -} - -// GenerateSecureUsername generates a cryptographically secure random username -func GenerateSecureUsername(length int) (string, error) { - if length <= 0 { - length = DefaultUsernameLength - } - - // Ensure username starts with a letter (database requirement) - username := make([]byte, length) - lettersOnly := "abcdefghijklmnopqrstuvwxyz" - lettersLen := big.NewInt(int64(len(lettersOnly))) - - // First character must be a letter - idx, err := rand.Int(rand.Reader, lettersLen) - if err != nil { - return "", fmt.Errorf("failed to generate random index: %w", err) - } - username[0] = lettersOnly[idx.Int64()] - - // Rest can be letters or digits - charsetLen := big.NewInt(int64(len(UsernameCharset))) - for i := 1; i < length; i++ { - idx, err := rand.Int(rand.Reader, charsetLen) - if err != nil { - return "", fmt.Errorf("failed to generate random index: %w", err) - } - username[i] = UsernameCharset[idx.Int64()] - } - - return string(username), nil -} - -// GenerateCredentials generates a new set of database credentials -func GenerateCredentials() (*DatabaseCredentials, error) { - username, err := GenerateSecureUsername(DefaultUsernameLength) - if err != nil { - return nil, fmt.Errorf("failed to generate username: %w", err) - } - - password, err := GenerateSecurePassword(DefaultPasswordLength) - if err != nil { - return nil, fmt.Errorf("failed to generate password: %w", err) - } - - return &DatabaseCredentials{ - Username: username, - Password: password, - }, nil -} - -// GenerateDatabaseSecret creates a Kubernetes Secret containing database credentials -func GenerateDatabaseSecret(namespace, secretName, componentName string, creds *DatabaseCredentials, dbHost string) *corev1.Secret { - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{ - "app": componentName, - "helios.io/managed-by": "operator", - "helios.io/secret-type": "database-credentials", - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "DB_USER": []byte(creds.Username), - "DB_PASS": []byte(creds.Password), - "DB_HOST": []byte(dbHost), - }, - } -} - -// GetDatabaseSecretName returns the conventional secret name for a component -func GetDatabaseSecretName(componentName string) string { - return fmt.Sprintf("%s-db-secret", componentName) -} - -// GetDatabaseHost returns the conventional database host for a component -func GetDatabaseHost(componentName string) string { - return fmt.Sprintf("%s-db", componentName) -} - -// ExtractDatabaseTraits extracts all database traits from HeliosApp components -func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { - ComponentName string - Properties DatabaseTraitProperties -} { - var dbTraits []struct { - ComponentName string - Properties DatabaseTraitProperties - } - - for _, component := range app.Spec.Components { - for _, trait := range component.Traits { - if strings.ToLower(trait.Type) == DatabaseTraitType { - var props DatabaseTraitProperties - if trait.Properties != nil && trait.Properties.Raw != nil { - if err := json.Unmarshal(trait.Properties.Raw, &props); err != nil { - // Log error but continue - don't fail the entire reconciliation - continue - } - } - dbTraits = append(dbTraits, struct { - ComponentName string - Properties DatabaseTraitProperties - }{ - ComponentName: component.Name, - Properties: props, - }) - } - } - } - - return dbTraits -} - -// reconcileDatabaseSecrets ensures database credential secrets exist for all -// components with database traits. If a secret already exists, it is not modified -// to preserve existing credentials. -func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - dbTraits := ExtractDatabaseTraits(app) - if len(dbTraits) == 0 { - log.V(1).Info("No database traits found, skipping secret creation") - return nil - } - - for _, dbTrait := range dbTraits { - secretName := GetDatabaseSecretName(dbTrait.ComponentName) - dbHost := GetDatabaseHost(dbTrait.ComponentName) - - // Check if secret already exists - existingSecret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{ - Name: secretName, - Namespace: app.Namespace, - }, existingSecret) - - if err == nil { - // Secret already exists - do not overwrite to preserve credentials - log.Info("Database secret already exists, skipping", - "component", dbTrait.ComponentName, - "secret", secretName) - continue - } - - if !errors.IsNotFound(err) { - // Unexpected error - log.Error(err, "Failed to check for existing database secret", - "component", dbTrait.ComponentName, - "secret", secretName) - return fmt.Errorf("failed to check for database secret %s: %w", secretName, err) - } - - // Secret doesn't exist - generate new credentials - log.Info("Generating database credentials", - "component", dbTrait.ComponentName, - "secret", secretName) - - creds, err := GenerateCredentials() - if err != nil { - log.Error(err, "Failed to generate database credentials", - "component", dbTrait.ComponentName) - return fmt.Errorf("failed to generate credentials for %s: %w", dbTrait.ComponentName, err) - } - - // Create the secret - secret := GenerateDatabaseSecret(app.Namespace, secretName, dbTrait.ComponentName, creds, dbHost) - - // Set owner reference so secret is garbage collected with the HeliosApp - if err := ctrl.SetControllerReference(app, secret, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for database secret", - "component", dbTrait.ComponentName, - "secret", secretName) - return fmt.Errorf("failed to set owner reference for secret %s: %w", secretName, err) - } - - if err := r.Create(ctx, secret); err != nil { - if errors.IsAlreadyExists(err) { - // Race condition - secret was created between our check and create - log.Info("Database secret was created concurrently, skipping", - "component", dbTrait.ComponentName, - "secret", secretName) - continue - } - log.Error(err, "Failed to create database secret", - "component", dbTrait.ComponentName, - "secret", secretName) - return fmt.Errorf("failed to create database secret %s: %w", secretName, err) - } - - log.Info("Successfully created database secret", - "component", dbTrait.ComponentName, - "secret", secretName, - "dbHost", dbHost) - } - - return nil -} - -// reconcileDatabaseInstance provisions database StatefulSets and headless -// Services for components with database traits. This runs AFTER -// reconcileDatabaseSecrets so that the credential Secret already exists -// when the StatefulSet is created. -func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - dbTraits := ExtractDatabaseTraits(app) - if len(dbTraits) == 0 { - log.V(1).Info("No database traits found, skipping instance provisioning") - return nil - } - - for _, dbTrait := range dbTraits { - // Only provision postgres instances for now - if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { - log.V(1).Info("Skipping non-postgres database type", - "component", dbTrait.ComponentName, - "dbType", dbTrait.Properties.DBType) - continue - } - - dbHost := GetDatabaseHost(dbTrait.ComponentName) - secretName := GetDatabaseSecretName(dbTrait.ComponentName) - - // Determine effective database name - effectiveDBName := dbTrait.Properties.DBName - if effectiveDBName == "" { - effectiveDBName = fmt.Sprintf("%s-db", dbTrait.ComponentName) - } - - // Determine version — CUE schema requires version!, but we - // guard here defensively in case of direct API usage. - version := dbTrait.Properties.Version - if version == "" { - version = DefaultPostgresVersion - } - - // Determine port - port := dbTrait.Properties.Port - if port <= 0 { - port = DefaultPostgresPort - } - - // Determine storage - storage := dbTrait.Properties.Storage - if storage == "" { - storage = DefaultDatabaseStorage - } - - // --- StatefulSet --- - sts, err := GenerateDatabaseStatefulSet( - app.Namespace, dbHost, secretName, effectiveDBName, version, storage, int32(port), - ) - if err != nil { - log.Error(err, "Failed to generate database StatefulSet", - "component", dbTrait.ComponentName, "storage", storage) - return fmt.Errorf("failed to generate StatefulSet for %s: %w", dbHost, err) - } - - if err := ctrl.SetControllerReference(app, sts, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for database StatefulSet", - "component", dbTrait.ComponentName) - return fmt.Errorf("failed to set owner reference for StatefulSet %s: %w", dbHost, err) - } - - existingSts := &appsv1.StatefulSet{} - err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) - if err != nil { - if !errors.IsNotFound(err) { - return fmt.Errorf("failed to check for StatefulSet %s: %w", dbHost, err) - } - - log.Info("Creating database StatefulSet", - "component", dbTrait.ComponentName, - "statefulset", dbHost, - "image", fmt.Sprintf("postgres:%s", version)) - - if err := r.Create(ctx, sts); err != nil { - if errors.IsAlreadyExists(err) { - log.Info("Database StatefulSet was created concurrently, skipping", - "component", dbTrait.ComponentName) - } else { - return fmt.Errorf("failed to create StatefulSet %s: %w", dbHost, err) - } - } - } else { - // Handle StatefulSet drift: update spec to match the new template - log.Info("Database StatefulSet already exists, updating if necessary", - "component", dbTrait.ComponentName, - "statefulset", dbHost) - - // We only update the mutable fields (Replicas, Template) - updatedSts := existingSts.DeepCopy() - updatedSts.Spec.Replicas = sts.Spec.Replicas - updatedSts.Spec.Template = sts.Spec.Template - - // We need to preserve the existing VolumeClaimTemplates when updating - updatedSts.Spec.VolumeClaimTemplates = existingSts.Spec.VolumeClaimTemplates - - if err := r.Update(ctx, updatedSts); err != nil { - return fmt.Errorf("failed to update StatefulSet %s: %w", dbHost, err) - } - } - - // --- Headless Service --- - svc := GenerateDatabaseService(app.Namespace, dbHost, int32(port)) - - if err := ctrl.SetControllerReference(app, svc, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for database Service", - "component", dbTrait.ComponentName) - return fmt.Errorf("failed to set owner reference for Service %s: %w", dbHost, err) - } - - existingSvc := &corev1.Service{} - err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSvc) - if err != nil { - if !errors.IsNotFound(err) { - return fmt.Errorf("failed to check for Service %s: %w", dbHost, err) - } - - log.Info("Creating database headless Service", - "component", dbTrait.ComponentName, - "service", dbHost) - - if err := r.Create(ctx, svc); err != nil { - if errors.IsAlreadyExists(err) { - log.Info("Database Service was created concurrently, skipping", - "component", dbTrait.ComponentName) - } else { - return fmt.Errorf("failed to create Service %s: %w", dbHost, err) - } - } - } else { - log.Info("Database Service already exists, updating if necessary", - "component", dbTrait.ComponentName, - "service", dbHost) - - updatedSvc := existingSvc.DeepCopy() - updatedSvc.Spec.Ports = svc.Spec.Ports - - if err := r.Update(ctx, updatedSvc); err != nil { - return fmt.Errorf("failed to update Service %s: %w", dbHost, err) - } - } - - log.Info("Successfully reconciled database instance", - "component", dbTrait.ComponentName, - "statefulset", dbHost, - "dbName", effectiveDBName) - } - - return nil -} - -// GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. -// The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and -// uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. -func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) (*appsv1.StatefulSet, error) { - storageQty, err := resource.ParseQuantity(storage) - if err != nil { - return nil, fmt.Errorf("invalid storage size format %q: %w", storage, err) - } - - replicas := int32(1) - labels := map[string]string{ - "app": name, - "helios.io/managed-by": "operator", - "helios.io/trait": "database", - "helios.io/db-type": "postgres", - } - - return &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Spec: appsv1.StatefulSetSpec{ - ServiceName: name, - Replicas: &replicas, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": name}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"app": name}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "postgres", - Image: fmt.Sprintf("postgres:%s", version), - Ports: []corev1.ContainerPort{ - { - ContainerPort: port, - Name: "postgres", - }, - }, - Env: []corev1.EnvVar{ - { - Name: "POSTGRES_DB", - Value: dbName, - }, - { - Name: "POSTGRES_USER", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: "DB_USER", - }, - }, - }, - { - Name: "POSTGRES_PASSWORD", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: "DB_PASS", - }, - }, - }, - { - // PGDATA tells Postgres where to store cluster data. - // Must match volumeMount + subPath to avoid lost+found conflicts. - Name: "PGDATA", - Value: PostgresDataPath + "/" + PostgresDataSubPath, - }, - { - // Ensure consistent UTF-8 encoding for all databases. - Name: "POSTGRES_INITDB_ARGS", - Value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C", - }, - { - // Explicitly set the custom port so that postgres knows to listen on it. - Name: "PGPORT", - Value: fmt.Sprintf("%d", port), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "data", - MountPath: PostgresDataPath, - SubPath: PostgresDataSubPath, - }, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resourceMustParse("100m"), - corev1.ResourceMemory: resourceMustParse("256Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resourceMustParse("500m"), - corev1.ResourceMemory: resourceMustParse("512Mi"), - }, - }, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, - }, - }, - InitialDelaySeconds: 5, - PeriodSeconds: 10, - }, - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, - }, - }, - InitialDelaySeconds: 30, - PeriodSeconds: 10, - }, - }, - }, - }, - }, - VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "data", - }, - Spec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, - }, - Resources: corev1.VolumeResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceStorage: storageQty, - }, - }, - }, - }, - }, - }, - }, nil -} - -// GenerateDatabaseService creates a headless Service for a database StatefulSet. -// The headless Service (clusterIP: None) provides stable DNS resolution -// so resolves to the database pod. -func GenerateDatabaseService(namespace, name string, port int32) *corev1.Service { - labels := map[string]string{ - "app": name, - "helios.io/managed-by": "operator", - "helios.io/trait": "database", - } - - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Selector: map[string]string{"app": name}, - Ports: []corev1.ServicePort{ - { - Port: port, - TargetPort: intstr.FromInt32(port), - Name: "db", - }, - }, - }, - } -} - -// resourceMustParse is a helper to parse resource quantities. Panics on invalid input. -func resourceMustParse(s string) resource.Quantity { - return resource.MustParse(s) -} - -// GenerateBase64Token generates a random base64-encoded token -// Useful for generating secure webhook secrets or API tokens -func GenerateBase64Token(byteLength int) (string, error) { - if byteLength <= 0 { - byteLength = 32 - } - - bytes := make([]byte, byteLength) - if _, err := rand.Read(bytes); err != nil { - return "", fmt.Errorf("failed to generate random bytes: %w", err) - } - - return base64.StdEncoding.EncodeToString(bytes), nil -} -=== -// database_resources.go handles database credential generation and Secret creation -// for components with database traits. -// -// The CUE engine generates ConfigMaps with database metadata (host, port, name), -// but credentials (username, password) are generated by this Go code for security -// reasons - secrets should never be stored in CUE definitions or GitOps repos. -package controller - -import ( - "context" - "crypto/rand" - "encoding/base64" - "encoding/json" - "fmt" - "math/big" - "strings" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" -) - -const ( - // DefaultPasswordLength is the length of generated passwords - DefaultPasswordLength = 32 - - // DefaultUsernameLength is the length of generated usernames - DefaultUsernameLength = 16 - - // PasswordCharset contains valid characters for password generation - // Includes uppercase, lowercase, digits, and special characters - PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" - - // UsernameCharset contains valid characters for username generation - // Lowercase letters and digits only for database compatibility - UsernameCharset = "abcdefghijklmnopqrstuvwxyz0123456789" - - // DatabaseTraitType is the trait type identifier for database traits - DatabaseTraitType = "database" - - // DefaultPostgresVersion is the default Postgres image tag when not specified. - DefaultPostgresVersion = "16" - - // DefaultPostgresPort is the default port for Postgres. - DefaultPostgresPort = 5432 - - // DefaultDatabaseStorage is the default PVC size for database volumes. - DefaultDatabaseStorage = "1Gi" - - // PostgresDataPath is the mount path for Postgres data directory. - PostgresDataPath = "/var/lib/postgresql/data" - - // PostgresDataSubPath is the subPath within the PVC to avoid lost+found issues. - PostgresDataSubPath = "pgdata" -) - -// DatabaseCredentials holds generated database credentials -type DatabaseCredentials struct { - Username string - Password string -} - -// DatabaseTraitProperties represents the properties of a database trait -type DatabaseTraitProperties struct { - DBType string `json:"dbType"` - DBName string `json:"dbName"` - Port int `json:"port"` - Version string `json:"version"` - Storage string `json:"storage"` -} - -// GenerateSecurePassword generates a cryptographically secure random password -func GenerateSecurePassword(length int) (string, error) { - if length <= 0 { - length = DefaultPasswordLength - } - - password := make([]byte, length) - charsetLen := big.NewInt(int64(len(PasswordCharset))) - - for i := 0; i < length; i++ { - idx, err := rand.Int(rand.Reader, charsetLen) - if err != nil { - return "", fmt.Errorf("failed to generate random index: %w", err) - } - password[i] = PasswordCharset[idx.Int64()] - } - - return string(password), nil -} - -// GenerateSecureUsername generates a cryptographically secure random username -func GenerateSecureUsername(length int) (string, error) { - if length <= 0 { - length = DefaultUsernameLength - } - - // Ensure username starts with a letter (database requirement) - username := make([]byte, length) - lettersOnly := "abcdefghijklmnopqrstuvwxyz" - lettersLen := big.NewInt(int64(len(lettersOnly))) - - // First character must be a letter - idx, err := rand.Int(rand.Reader, lettersLen) - if err != nil { - return "", fmt.Errorf("failed to generate random index: %w", err) - } - username[0] = lettersOnly[idx.Int64()] - - // Rest can be letters or digits - charsetLen := big.NewInt(int64(len(UsernameCharset))) - for i := 1; i < length; i++ { - idx, err := rand.Int(rand.Reader, charsetLen) - if err != nil { - return "", fmt.Errorf("failed to generate random index: %w", err) - } - username[i] = UsernameCharset[idx.Int64()] - } - - return string(username), nil -} - -// GenerateCredentials generates a new set of database credentials -func GenerateCredentials() (*DatabaseCredentials, error) { - username, err := GenerateSecureUsername(DefaultUsernameLength) - if err != nil { - return nil, fmt.Errorf("failed to generate username: %w", err) - } - - password, err := GenerateSecurePassword(DefaultPasswordLength) - if err != nil { - return nil, fmt.Errorf("failed to generate password: %w", err) - } - - return &DatabaseCredentials{ - Username: username, - Password: password, - }, nil -} - -// GenerateDatabaseSecret creates a Kubernetes Secret containing database credentials -func GenerateDatabaseSecret(namespace, secretName, componentName string, creds *DatabaseCredentials, dbHost string) *corev1.Secret { - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{ - "app": componentName, - "helios.io/managed-by": "operator", - "helios.io/secret-type": "database-credentials", - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "DB_USER": []byte(creds.Username), - "DB_PASS": []byte(creds.Password), - "DB_HOST": []byte(dbHost), - }, - } -} - -// GetDatabaseSecretName returns the conventional secret name for a component -func GetDatabaseSecretName(componentName string) string { - return fmt.Sprintf("%s-db-secret", componentName) -} - -// GetDatabaseHost returns the conventional database host for a component -func GetDatabaseHost(componentName string) string { - return fmt.Sprintf("%s-db", componentName) -} - -// ExtractDatabaseTraits extracts all database traits from HeliosApp components -func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { - ComponentName string - Properties DatabaseTraitProperties -} { - var dbTraits []struct { - ComponentName string - Properties DatabaseTraitProperties - } - - for _, component := range app.Spec.Components { - for _, trait := range component.Traits { - if strings.ToLower(trait.Type) == DatabaseTraitType { - var props DatabaseTraitProperties - if trait.Properties != nil && trait.Properties.Raw != nil { - if err := json.Unmarshal(trait.Properties.Raw, &props); err != nil { - // Log error but continue - don't fail the entire reconciliation - continue - } - } - dbTraits = append(dbTraits, struct { - ComponentName string - Properties DatabaseTraitProperties - }{ - ComponentName: component.Name, - Properties: props, - }) - } - } - } - - return dbTraits -} - -// reconcileDatabaseSecrets ensures database credential secrets exist for all -// components with database traits. If a secret already exists, it is not modified -// to preserve existing credentials. -func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - dbTraits := ExtractDatabaseTraits(app) - if len(dbTraits) == 0 { - log.V(1).Info("No database traits found, skipping secret creation") - return nil - } - - for _, dbTrait := range dbTraits { - secretName := GetDatabaseSecretName(dbTrait.ComponentName) - dbHost := GetDatabaseHost(dbTrait.ComponentName) - - // Check if secret already exists - existingSecret := &corev1.Secret{} - err := r.Get(ctx, types.NamespacedName{ - Name: secretName, - Namespace: app.Namespace, - }, existingSecret) - - if err == nil { - // Secret already exists - do not overwrite to preserve credentials - log.Info("Database secret already exists, skipping", - "component", dbTrait.ComponentName, - "secret", secretName) - continue - } - - if !errors.IsNotFound(err) { - // Unexpected error - log.Error(err, "Failed to check for existing database secret", - "component", dbTrait.ComponentName, - "secret", secretName) - return fmt.Errorf("failed to check for database secret %s: %w", secretName, err) - } - - // Secret doesn't exist - generate new credentials - log.Info("Generating database credentials", - "component", dbTrait.ComponentName, - "secret", secretName) - - creds, err := GenerateCredentials() - if err != nil { - log.Error(err, "Failed to generate database credentials", - "component", dbTrait.ComponentName) - return fmt.Errorf("failed to generate credentials for %s: %w", dbTrait.ComponentName, err) - } - - // Create the secret - secret := GenerateDatabaseSecret(app.Namespace, secretName, dbTrait.ComponentName, creds, dbHost) - - // Set owner reference so secret is garbage collected with the HeliosApp - if err := ctrl.SetControllerReference(app, secret, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for database secret", - "component", dbTrait.ComponentName, - "secret", secretName) - return fmt.Errorf("failed to set owner reference for secret %s: %w", secretName, err) - } - - if err := r.Create(ctx, secret); err != nil { - if errors.IsAlreadyExists(err) { - // Race condition - secret was created between our check and create - log.Info("Database secret was created concurrently, skipping", - "component", dbTrait.ComponentName, - "secret", secretName) - continue - } - log.Error(err, "Failed to create database secret", - "component", dbTrait.ComponentName, - "secret", secretName) - return fmt.Errorf("failed to create database secret %s: %w", secretName, err) - } - - log.Info("Successfully created database secret", - "component", dbTrait.ComponentName, - "secret", secretName, - "dbHost", dbHost) - } - - return nil -} - -// reconcileDatabaseInstance provisions database StatefulSets and headless -// Services for components with database traits. This runs AFTER -// reconcileDatabaseSecrets so that the credential Secret already exists -// when the StatefulSet is created. -func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - dbTraits := ExtractDatabaseTraits(app) - if len(dbTraits) == 0 { - log.V(1).Info("No database traits found, skipping instance provisioning") - return nil - } - - for _, dbTrait := range dbTraits { - // Only provision postgres instances for now - if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { - log.V(1).Info("Skipping non-postgres database type", - "component", dbTrait.ComponentName, - "dbType", dbTrait.Properties.DBType) - continue - } - - dbHost := GetDatabaseHost(dbTrait.ComponentName) - secretName := GetDatabaseSecretName(dbTrait.ComponentName) - - // Determine effective database name - effectiveDBName := dbTrait.Properties.DBName - if effectiveDBName == "" { - effectiveDBName = fmt.Sprintf("%s-db", dbTrait.ComponentName) - } - - // Determine version — CUE schema requires version!, but we - // guard here defensively in case of direct API usage. - version := dbTrait.Properties.Version - if version == "" { - version = DefaultPostgresVersion - } - - // Determine port - port := dbTrait.Properties.Port - if port <= 0 { - port = DefaultPostgresPort - } - - // Determine storage - storage := dbTrait.Properties.Storage - if storage == "" { - storage = DefaultDatabaseStorage - } - - // --- StatefulSet --- - sts, err := GenerateDatabaseStatefulSet( - app.Namespace, dbHost, secretName, effectiveDBName, version, storage, int32(port), - ) - if err != nil { - log.Error(err, "Failed to generate database StatefulSet", - "component", dbTrait.ComponentName, "storage", storage) - return fmt.Errorf("failed to generate StatefulSet for %s: %w", dbHost, err) - } - - if err := ctrl.SetControllerReference(app, sts, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for database StatefulSet", - "component", dbTrait.ComponentName) - return fmt.Errorf("failed to set owner reference for StatefulSet %s: %w", dbHost, err) - } - - existingSts := &appsv1.StatefulSet{} - err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSts) - if err != nil { - if !errors.IsNotFound(err) { - return fmt.Errorf("failed to check for StatefulSet %s: %w", dbHost, err) - } - - log.Info("Creating database StatefulSet", - "component", dbTrait.ComponentName, - "statefulset", dbHost, - "image", fmt.Sprintf("postgres:%s", version)) - - if err := r.Create(ctx, sts); err != nil { - if errors.IsAlreadyExists(err) { - log.Info("Database StatefulSet was created concurrently, skipping", - "component", dbTrait.ComponentName) - } else { - return fmt.Errorf("failed to create StatefulSet %s: %w", dbHost, err) - } - } - } else { - // Handle StatefulSet drift: update spec to match the new template - log.Info("Database StatefulSet already exists, updating if necessary", - "component", dbTrait.ComponentName, - "statefulset", dbHost) - - // We only update the mutable fields (Replicas, Template) - updatedSts := existingSts.DeepCopy() - updatedSts.Spec.Replicas = sts.Spec.Replicas - updatedSts.Spec.Template = sts.Spec.Template - - // We need to preserve the existing VolumeClaimTemplates when updating - updatedSts.Spec.VolumeClaimTemplates = existingSts.Spec.VolumeClaimTemplates - - if err := r.Update(ctx, updatedSts); err != nil { - return fmt.Errorf("failed to update StatefulSet %s: %w", dbHost, err) - } - } - - // --- Headless Service --- - svc := GenerateDatabaseService(app.Namespace, dbHost, int32(port)) - - if err := ctrl.SetControllerReference(app, svc, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for database Service", - "component", dbTrait.ComponentName) - return fmt.Errorf("failed to set owner reference for Service %s: %w", dbHost, err) - } - - existingSvc := &corev1.Service{} - err = r.Get(ctx, types.NamespacedName{Name: dbHost, Namespace: app.Namespace}, existingSvc) - if err != nil { - if !errors.IsNotFound(err) { - return fmt.Errorf("failed to check for Service %s: %w", dbHost, err) - } - - log.Info("Creating database headless Service", - "component", dbTrait.ComponentName, - "service", dbHost) - - if err := r.Create(ctx, svc); err != nil { - if errors.IsAlreadyExists(err) { - log.Info("Database Service was created concurrently, skipping", - "component", dbTrait.ComponentName) - } else { - return fmt.Errorf("failed to create Service %s: %w", dbHost, err) - } - } - } else { - log.Info("Database Service already exists, updating if necessary", - "component", dbTrait.ComponentName, - "service", dbHost) - - updatedSvc := existingSvc.DeepCopy() - updatedSvc.Spec.Ports = svc.Spec.Ports - - if err := r.Update(ctx, updatedSvc); err != nil { - return fmt.Errorf("failed to update Service %s: %w", dbHost, err) - } - } - - log.Info("Successfully reconciled database instance", - "component", dbTrait.ComponentName, - "statefulset", dbHost, - "dbName", effectiveDBName) - } - - return nil -} - -// databaseEnvVarNames lists the env var names injected by the operator -// for database credential secret injection. -var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} - -// InjectDatabaseEnvVars patches a Deployment's first container to include -// DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. -// The function is idempotent — if the env vars already exist with the correct -// secretKeyRef, no changes are made and it returns false. -func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { - if len(deploy.Spec.Template.Spec.Containers) == 0 { - return false - } - - container := &deploy.Spec.Template.Spec.Containers[0] - - // Build a set of existing env var names for fast lookup. - existingEnvs := make(map[string]bool, len(container.Env)) - for _, ev := range container.Env { - existingEnvs[ev.Name] = true - } - - changed := false - for _, envName := range databaseEnvVarNames { - if existingEnvs[envName] { - continue - } - container.Env = append(container.Env, corev1.EnvVar{ - Name: envName, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: envName, - }, - }, - }) - changed = true - } - - return changed -} - -// reconcileDatabaseSecretInjection patches live Deployments (deployed by ArgoCD) -// to inject DB_HOST, DB_USER, DB_PASS env vars from the operator-managed Secret. -// This runs AFTER database secrets and instances are provisioned so that the -// Secret already exists when the Deployment references it. -func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - dbTraits := ExtractDatabaseTraits(app) - if len(dbTraits) == 0 { - log.V(1).Info("No database traits found, skipping secret injection") - return nil - } - - for _, dbTrait := range dbTraits { - secretName := GetDatabaseSecretName(dbTrait.ComponentName) - deployName := dbTrait.ComponentName - - // Fetch the live Deployment (created by ArgoCD via GitOps). - deploy := &appsv1.Deployment{} - err := r.Get(ctx, types.NamespacedName{ - Name: deployName, - Namespace: app.Namespace, - }, deploy) - if err != nil { - if errors.IsNotFound(err) { - // Deployment may not exist yet (ArgoCD hasn't synced). - // This is expected — we'll inject on the next reconcile. - log.Info("Deployment not found yet, will inject on next reconcile", - "component", dbTrait.ComponentName, - "deployment", deployName) - continue - } - return fmt.Errorf("failed to get Deployment %s: %w", deployName, err) - } - - // Inject env vars if not already present. - if !InjectDatabaseEnvVars(deploy, secretName) { - log.V(1).Info("Database env vars already injected, skipping", - "component", dbTrait.ComponentName, - "deployment", deployName) - continue - } - - if err := r.Update(ctx, deploy); err != nil { - return fmt.Errorf("failed to update Deployment %s with database env vars: %w", deployName, err) - } - - log.Info("Successfully injected database env vars into Deployment", - "component", dbTrait.ComponentName, - "deployment", deployName, - "secret", secretName) - } - - return nil -} - -// GenerateDatabaseStatefulSet creates a StatefulSet for a Postgres database instance. -// The StatefulSet injects POSTGRES_DB from the CRD's database.name value, and -// uses the Secret from Issue #33 for POSTGRES_USER and POSTGRES_PASSWORD. -func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, storage string, port int32) (*appsv1.StatefulSet, error) { - storageQty, err := resource.ParseQuantity(storage) - if err != nil { - return nil, fmt.Errorf("invalid storage size format %q: %w", storage, err) - } - - replicas := int32(1) - labels := map[string]string{ - "app": name, - "helios.io/managed-by": "operator", - "helios.io/trait": "database", - "helios.io/db-type": "postgres", - } - - return &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Spec: appsv1.StatefulSetSpec{ - ServiceName: name, - Replicas: &replicas, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"app": name}, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"app": name}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "postgres", - Image: fmt.Sprintf("postgres:%s", version), - Ports: []corev1.ContainerPort{ - { - ContainerPort: port, - Name: "postgres", - }, - }, - Env: []corev1.EnvVar{ - { - Name: "POSTGRES_DB", - Value: dbName, - }, - { - Name: "POSTGRES_USER", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: "DB_USER", - }, - }, - }, - { - Name: "POSTGRES_PASSWORD", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: "DB_PASS", - }, - }, - }, - { - // PGDATA tells Postgres where to store cluster data. - // Must match volumeMount + subPath to avoid lost+found conflicts. - Name: "PGDATA", - Value: PostgresDataPath + "/" + PostgresDataSubPath, - }, - { - // Ensure consistent UTF-8 encoding for all databases. - Name: "POSTGRES_INITDB_ARGS", - Value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C", - }, - { - // Explicitly set the custom port so that postgres knows to listen on it. - Name: "PGPORT", - Value: fmt.Sprintf("%d", port), - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "data", - MountPath: PostgresDataPath, - SubPath: PostgresDataSubPath, - }, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resourceMustParse("100m"), - corev1.ResourceMemory: resourceMustParse("256Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resourceMustParse("500m"), - corev1.ResourceMemory: resourceMustParse("512Mi"), - }, - }, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, - }, - }, - InitialDelaySeconds: 5, - PeriodSeconds: 10, - }, - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, - }, - }, - InitialDelaySeconds: 30, - PeriodSeconds: 10, - }, - }, - }, - }, - }, - VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "data", - }, - Spec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, - }, - Resources: corev1.VolumeResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceStorage: storageQty, - }, - }, - }, - }, - }, - }, - }, nil -} - -// GenerateDatabaseService creates a headless Service for a database StatefulSet. -// The headless Service (clusterIP: None) provides stable DNS resolution -// so resolves to the database pod. -func GenerateDatabaseService(namespace, name string, port int32) *corev1.Service { - labels := map[string]string{ - "app": name, - "helios.io/managed-by": "operator", - "helios.io/trait": "database", - } - - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Selector: map[string]string{"app": name}, - Ports: []corev1.ServicePort{ - { - Port: port, - TargetPort: intstr.FromInt32(port), - Name: "db", - }, - }, - }, - } -} - -// resourceMustParse is a helper to parse resource quantities. Panics on invalid input. -func resourceMustParse(s string) resource.Quantity { - return resource.MustParse(s) -} - -// GenerateBase64Token generates a random base64-encoded token -// Useful for generating secure webhook secrets or API tokens -func GenerateBase64Token(byteLength int) (string, error) { - if byteLength <= 0 { - byteLength = 32 - } - - bytes := make([]byte, byteLength) - if _, err := rand.Read(bytes); err != nil { - return "", fmt.Errorf("failed to generate random bytes: %w", err) - } - - return base64.StdEncoding.EncodeToString(bytes), nil -} -``` - -#### [heliosapp_controller.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/heliosapp_controller.go) - -Added **Phase 0.9** call after database instance provisioning: - -```diff:heliosapp_controller.go -/* -Copyright 2026. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "cmp" - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "os" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" - heliosCue "github.com/helios-platform-team/helios-platform/apps/operator/internal/cue" - "github.com/helios-platform-team/helios-platform/apps/operator/internal/gitops" -) - -// HeliosAppReconciler reconciles a HeliosApp object -type HeliosAppReconciler struct { - client.Client - Scheme *runtime.Scheme - CueEngine heliosCue.CueEngineInterface - // TektonRenderer renders Tekton CI/CD resources via CUE engine. - TektonRenderer heliosCue.TektonRendererInterface - // GitFactory allows injecting a custom GitOps client (e.g. for testing) - GitFactory func(string, string, string) gitops.GitOpsClientInterface -} - -// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/finalizers,verbs=update -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete - -// Reconcile handles the reconciliation loop for HeliosApp -// Controller does NOT iterate components/traits - all orchestration is in CUE -func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := logf.FromContext(ctx) - - // 1. Fetch HeliosApp CRD - var heliosApp appv1alpha1.HeliosApp - if err := r.Get(ctx, req.NamespacedName, &heliosApp); err != nil { - if errors.IsNotFound(err) { - log.Info("HeliosApp resource not found, ignoring") - return ctrl.Result{}, nil - } - return ctrl.Result{}, err - } - - log.Info("Reconciling HeliosApp", "name", heliosApp.Name, "namespace", heliosApp.Namespace) - - // 2. Map CRD to Application Model - appModel, err := r.mapCRDToModel(&heliosApp) - if err != nil { - log.Error(err, "Failed to map CRD to application model") - return ctrl.Result{}, err - } - - // 3. Render via CUE Engine - manifestBytes, err := r.CueEngine.Render(appModel) - if err != nil { - log.Error(err, "Failed to render application via CUE") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE rendering failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE -1 & 0: Tekton CI/CD Resources (Tasks, Pipeline, Triggers) - // All Tekton resources are rendered via CUE engine. - // ------------------------------------------------------------------ - if err := r.reconcileTektonResourcesCue(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to reconcile Tekton resources via CUE") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE Tekton rendering failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE 0.5: Database Credential Secrets - // Generate and store secure credentials for components with database traits. - // Secrets are created BEFORE GitOps sync to ensure credentials exist - // when the application is deployed. - // ------------------------------------------------------------------ - if err := r.reconcileDatabaseSecrets(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to reconcile database secrets") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret creation failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE 0.7: Database Instance Provisioning - // Provision StatefulSets and headless Services for database traits. - // Runs AFTER secrets so that the credential Secret already exists - // when the database pod starts. - // ------------------------------------------------------------------ - if err := r.reconcileDatabaseInstance(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to reconcile database instance") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database instance provisioning failed: %v", err)) - return ctrl.Result{}, err - } - - // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") - // This validation is for application workloads (GitOps pipeline downstream). - // We run this AFTER DB provisioning so databases can come up while app is building. - for _, comp := range appModel.App.Components { - // We can add more specific checks here based on component type - // For now, checks if 'image' property exists and is not empty for all components - // assuming all workloads need an image. - if img, ok := comp.Properties["image"].(string); !ok || img == "" { - msg := fmt.Sprintf("Component '%s' is waiting for image (likely building). Status: Pending.", comp.Name) - log.Info(msg) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhasePending, msg) - return ctrl.Result{}, nil // Wait for next update (CI/CD will update CR with image) - } - } - - // ------------------------------------------------------------------ - // PHASE 0.6: Trigger Initial PipelineRun (if not already done) - // ------------------------------------------------------------------ - if !heliosApp.Status.InitialBuildTriggered { - log.Info("Triggering initial PipelineRun for new HeliosApp") - - pipelineName := heliosApp.Spec.PipelineName - if pipelineName == "" { - pipelineName = "from-code-to-cluster" - } - pr, err := GeneratePipelineRunForManifestGeneration(&heliosApp, pipelineName) - if err != nil { - log.Error(err, "Failed to generate initial PipelineRun") - } else { - if err := ctrl.SetControllerReference(&heliosApp, pr, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for PipelineRun") - } - - if err := r.Client.Create(ctx, pr); err != nil { - if !errors.IsAlreadyExists(err) { - log.Error(err, "Failed to create initial PipelineRun") - } - } else { - log.Info("Created initial PipelineRun", "name", pr.GetName()) - } - - // Mark as triggered to avoid creating multiple PipelineRuns - heliosApp.Status.InitialBuildTriggered = true - if err := r.Status().Update(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to update InitialBuildTriggered status") - } - } - } - - // ------------------------------------------------------------------ - // PHASE 1: Render & GitOps (Moved below) - // ------------------------------------------------------------------ - - // 4. GitOps Helper: Get Token & Username - token := os.Getenv("GITHUB_TOKEN") - username := os.Getenv("GITHUB_USER") - if username == "" { - username = "git" // Default fallback - } - - if heliosApp.Spec.GitOpsSecretRef != "" { - var secret corev1.Secret - // Explicitly log the secret lookup attempt - if err := r.Get(ctx, types.NamespacedName{Name: heliosApp.Spec.GitOpsSecretRef, Namespace: heliosApp.Namespace}, &secret); err == nil { - if t, ok := secret.Data["token"]; ok { - token = string(t) - } else if p, ok := secret.Data["password"]; ok { - // Fallback to 'password' key (standard basic-auth secret from Tekton setup) - token = string(p) - } else { - log.Info("Secret found but 'token' or 'password' key is missing", "Secret", heliosApp.Spec.GitOpsSecretRef) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s missing 'token' key", heliosApp.Spec.GitOpsSecretRef)) - return ctrl.Result{}, nil - } - if u, ok := secret.Data["username"]; ok { - username = string(u) - } - } else { - log.Error(err, "Failed to get GitOps Secret", "Secret", heliosApp.Spec.GitOpsSecretRef) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s not found", heliosApp.Spec.GitOpsSecretRef)) - return ctrl.Result{}, nil - } - } - - // 5. GitOps Sync - - if token == "" { - err := fmt.Errorf("GitOps token is empty. Check Secret or GITHUB_TOKEN env var") - log.Error(err, "Authentication failed") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, "GitOps token missing") - return ctrl.Result{}, nil // Don't retry immediately if config is missing - } - - // OPTIMIZATION: Check Hash - currentHash := r.computeHash(manifestBytes) - if heliosApp.Status.LastAppliedHash == currentHash { - log.Info("Manifest hash unchanged, skipping GitOps sync", "hash", currentHash) - - // Still ensure status is Ready if it was previously set - if heliosApp.Status.Phase != appv1alpha1.PhaseReady { - heliosApp.Status.Phase = appv1alpha1.PhaseReady - if err := r.Status().Update(ctx, &heliosApp); err != nil { - return ctrl.Result{}, err - } - } - } else { - // Use GitFactory if available, otherwise default to NewGitOpsClient - getGitClient := r.GitFactory - if getGitClient == nil { - getGitClient = func(repo, user, token string) gitops.GitOpsClientInterface { - return gitops.NewGitOpsClient(repo, user, token) - } - } - - gitClient := getGitClient(heliosApp.Spec.GitOpsRepo, username, token) - targetPath := fmt.Sprintf("%s/manifest.yaml", heliosApp.Spec.GitOpsPath) - - if err := gitClient.SyncManifest(ctx, targetPath, string(manifestBytes)); err != nil { - log.Error(err, "GitOps sync failed") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("GitOps failed: %v", err)) - return ctrl.Result{}, err - } - - // 6. Update Status - heliosApp.Status.Phase = appv1alpha1.PhaseReady - heliosApp.Status.Message = fmt.Sprintf("Manifest pushed to %s/%s", heliosApp.Spec.GitOpsRepo, targetPath) - heliosApp.Status.LastAppliedHash = currentHash - // We clear ResourcesCreated as we are not managing them directly anymore - heliosApp.Status.ResourcesCreated = nil - - if err := r.Status().Update(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to update status") - return ctrl.Result{}, err - } - log.Info("Successfully reconciled HeliosApp via GitOps", "newHash", currentHash) - } - - // 7. Ensure ArgoCD Application exists - log.Info("Ensuring ArgoCD Application exists") - argoApp, err := GenerateArgoApplication(&heliosApp) - if err != nil { - log.Error(err, "Failed to generate ArgoCD Application manifest") - // We don't return error here to avoid loop if GitOps was successful, just log it. - // Or maybe we should retry? Let's log and continue for now. - } else { - // Define ArgoCD Application identity - argoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) - // We use Sever-Side Apply or Create/Update logic - // Since ArgoCD app is in "argocd" namespace usually, we need permissions there. - // For simplicity/demo: Try to get, if not found create. - - foundArgoApp := &unstructured.Unstructured{} - foundArgoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) - - key := client.ObjectKey{ - Name: argoApp.GetName(), - Namespace: argoApp.GetNamespace(), - } - - if err := r.Client.Get(ctx, key, foundArgoApp); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating ArgoCD Application", "name", argoApp.GetName()) - if err := r.Client.Create(ctx, argoApp); err != nil { - log.Error(err, "Failed to create ArgoCD Application") - } - } else { - log.Error(err, "Failed to get ArgoCD Application") - } - } else { - // Optional: Update if needed (checkout 'spec' diff) - log.Info("ArgoCD Application already exists", "name", argoApp.GetName()) - } - } - - // NOTE: Ingress removed - use port-forwarding for EventListener: - // kubectl port-forward svc/el--listener 8080:8080 - - return ctrl.Result{}, nil -} - -// mapCRDToModel converts HeliosApp CRD to CUE Application Model -func (r *HeliosAppReconciler) mapCRDToModel(app *appv1alpha1.HeliosApp) (heliosCue.Application, error) { - components := make([]heliosCue.Component, len(app.Spec.Components)) - - for i, c := range app.Spec.Components { - // Parse properties from RawExtension - var props map[string]any - if c.Properties != nil && c.Properties.Raw != nil { - if err := json.Unmarshal(c.Properties.Raw, &props); err != nil { - return heliosCue.Application{}, fmt.Errorf("failed to parse component properties: %w", err) - } - } - - // Parse traits - traits := make([]heliosCue.Trait, len(c.Traits)) - for j, t := range c.Traits { - var traitProps map[string]any - if t.Properties != nil && t.Properties.Raw != nil { - if err := json.Unmarshal(t.Properties.Raw, &traitProps); err != nil { - return heliosCue.Application{}, fmt.Errorf("failed to parse trait properties: %w", err) - } - } - traits[j] = heliosCue.Trait{ - Type: t.Type, - Properties: traitProps, - } - } - - components[i] = heliosCue.Component{ - Name: c.Name, - Type: c.Type, - Properties: props, - Traits: traits, - } - } - - return heliosCue.Application{ - App: heliosCue.AppSpec{ - Name: app.Name, - Namespace: app.Namespace, - Owner: app.Spec.Owner, - Description: app.Spec.Description, - Components: components, - }, - }, nil -} - -// mapCRDToTektonInput converts HeliosApp CRD to TektonInput for CUE rendering. -// This is the bridge between HeliosApp spec fields and the CUE #TektonInput schema. -func (r *HeliosAppReconciler) mapCRDToTektonInput(app *appv1alpha1.HeliosApp) heliosCue.TektonInput { - input := heliosCue.TektonInput{ - AppName: app.Name, - Namespace: app.Namespace, - GitRepo: app.Spec.GitRepo, - GitBranch: app.Spec.GitBranch, - ImageRepo: app.Spec.ImageRepo, - GitOpsRepo: app.Spec.GitOpsRepo, - GitOpsPath: app.Spec.GitOpsPath, - GitOpsBranch: app.Spec.GitOpsBranch, - GitOpsSecretRef: app.Spec.GitOpsSecretRef, - WebhookDomain: app.Spec.WebhookDomain, - WebhookSecret: app.Spec.WebhookSecret, - PipelineName: app.Spec.PipelineName, - PipelineType: app.Spec.PipelineName, // pipelineType uses same value as pipelineName - TriggerType: "github-push", // Default; extend HeliosAppSpec if needed - ServiceAccount: app.Spec.ServiceAccount, - PVCName: app.Spec.PVCName, - ContextSubpath: app.Spec.ContextSubpath, - Replicas: int(app.Spec.Replicas), - Port: int(app.Spec.Port), - TestCommand: app.Spec.TestCommand, - DockerSecret: "docker-credentials", - ArgoCDNamespace: app.Spec.ArgoCDNamespace, - ArgoCDProject: app.Spec.ArgoCDProject, - } - - // Apply defaults for fields that may be empty - input.GitBranch = cmp.Or(input.GitBranch, "main") - input.GitOpsBranch = cmp.Or(input.GitOpsBranch, "main") - input.GitOpsSecretRef = cmp.Or(input.GitOpsSecretRef, "github-credentials") - input.WebhookSecret = cmp.Or(input.WebhookSecret, "github-webhook-secret") - if input.PipelineName == "" { - input.PipelineName = "from-code-to-cluster" - input.PipelineType = "from-code-to-cluster" - } - input.ServiceAccount = cmp.Or(input.ServiceAccount, "default") - if input.Replicas <= 0 { - input.Replicas = 1 - } - if input.Port <= 0 { - input.Port = 8080 - } - - return input -} - -// reconcileTektonResourcesCue renders Tekton resources via CUE and applies them. -// This is the NEW path that replaces all hardcoded Generate* functions. -func (r *HeliosAppReconciler) reconcileTektonResourcesCue(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - // 1. Map CRD → TektonInput - tektonInput := r.mapCRDToTektonInput(app) - - // 2. Render via CUE - objects, err := r.TektonRenderer.RenderTektonResources(tektonInput) - if err != nil { - return fmt.Errorf("CUE TektonRenderer failed: %w", err) - } - - log.Info("CUE rendered Tekton resources", "count", len(objects)) - - // 3. Apply each rendered resource - for _, obj := range objects { - // Set owner reference (skip cluster-scoped resources) - if obj.GetNamespace() != "" { - if err := ctrl.SetControllerReference(app, obj, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference", "kind", obj.GetKind(), "name", obj.GetName()) - continue - } - } - - // Create or update - found := &unstructured.Unstructured{} - found.SetGroupVersionKind(obj.GroupVersionKind()) - err := r.Client.Get(ctx, client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()}, found) - if err != nil { - if errors.IsNotFound(err) { - log.Info("Creating resource", "kind", obj.GetKind(), "name", obj.GetName()) - if err := r.Client.Create(ctx, obj); err != nil { - log.Error(err, "Failed to create resource", "kind", obj.GetKind(), "name", obj.GetName()) - } - } else { - log.Error(err, "Failed to get resource", "kind", obj.GetKind(), "name", obj.GetName()) - } - } else { - // Update existing resource's spec - found.Object["spec"] = obj.Object["spec"] - if err := r.Client.Update(ctx, found); err != nil { - log.Error(err, "Failed to update resource", "kind", obj.GetKind(), "name", obj.GetName()) - } - } - } - - // 4. Also ensure RBAC (SA, RoleBinding, ClusterRoleBinding) — these are not in CUE yet - r.ensureTektonRBAC(ctx, app) - - return nil -} - -// ensureTektonRBAC creates ServiceAccount, RoleBinding, ClusterRoleBinding. -// These are infrastructure resources not managed by CUE (they are cluster lifecycle, not app lifecycle). -func (r *HeliosAppReconciler) ensureTektonRBAC(ctx context.Context, app *appv1alpha1.HeliosApp) { - log := logf.FromContext(ctx) - - sa := GenerateServiceAccount(app.Namespace) - if err := ctrl.SetControllerReference(app, sa, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for ServiceAccount") - } else { - foundSA := &unstructured.Unstructured{} - foundSA.SetGroupVersionKind(sa.GroupVersionKind()) - if err := r.Client.Get(ctx, client.ObjectKey{Name: sa.GetName(), Namespace: sa.GetNamespace()}, foundSA); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating ServiceAccount", "name", sa.GetName()) - r.Client.Create(ctx, sa) - } - } - } - - rb := GenerateRoleBinding(app.Namespace) - if err := ctrl.SetControllerReference(app, rb, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for RoleBinding") - } else { - foundRB := &unstructured.Unstructured{} - foundRB.SetGroupVersionKind(rb.GroupVersionKind()) - if err := r.Client.Get(ctx, client.ObjectKey{Name: rb.GetName(), Namespace: rb.GetNamespace()}, foundRB); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating RoleBinding", "name", rb.GetName()) - r.Client.Create(ctx, rb) - } - } - } - - crb := GenerateClusterRoleBinding(app.Namespace) - foundCrb := &unstructured.Unstructured{} - foundCrb.SetGroupVersionKind(crb.GroupVersionKind()) - if err := r.Client.Get(ctx, client.ObjectKey{Name: crb.GetName()}, foundCrb); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating ClusterRoleBinding", "name", crb.GetName()) - r.Client.Create(ctx, crb) - } - } -} - -// updateStatus updates the HeliosApp status -func (r *HeliosAppReconciler) updateStatus(ctx context.Context, app *appv1alpha1.HeliosApp, phase appv1alpha1.HeliosAppPhase, message string) { - app.Status.Phase = phase - app.Status.Message = message - if err := r.Status().Update(ctx, app); err != nil { - logf.FromContext(ctx).Error(err, "Failed to update status") - } -} - -// computeHash returns SHA256 of data -func (r *HeliosAppReconciler) computeHash(data []byte) string { - hash := sha256.Sum256(data) - return hex.EncodeToString(hash[:]) -} - -// SetupWithManager sets up the controller with the Manager -func (r *HeliosAppReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&appv1alpha1.HeliosApp{}). - Owns(&appsv1.Deployment{}). - Owns(&appsv1.StatefulSet{}). - Owns(&corev1.Service{}). - Owns(&networkingv1.Ingress{}). - Watches( - &corev1.Secret{}, - handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret), - ). - Named("heliosapp"). - Complete(r) -} - -// findObjectsForSecret maps Secret changes to HeliosApp reconcile requests. -// This ensures the controller re-reconciles when a referenced secret changes. -func (r *HeliosAppReconciler) findObjectsForSecret(ctx context.Context, obj client.Object) []reconcile.Request { - log := logf.FromContext(ctx) - - // List all HeliosApps in the same namespace - var heliosAppList appv1alpha1.HeliosAppList - if err := r.List(ctx, &heliosAppList, client.InNamespace(obj.GetNamespace())); err != nil { - log.Error(err, "Failed to list HeliosApps for secret watch") - return nil - } - - var requests []reconcile.Request - for _, app := range heliosAppList.Items { - // Check if this app references the changed secret - if app.Spec.GitOpsSecretRef == obj.GetName() || - app.Spec.WebhookSecret == obj.GetName() { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: app.Name, - Namespace: app.Namespace, - }, - }) - } - } - - return requests -} -=== -/* -Copyright 2026. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "cmp" - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "os" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" - heliosCue "github.com/helios-platform-team/helios-platform/apps/operator/internal/cue" - "github.com/helios-platform-team/helios-platform/apps/operator/internal/gitops" -) - -// HeliosAppReconciler reconciles a HeliosApp object -type HeliosAppReconciler struct { - client.Client - Scheme *runtime.Scheme - CueEngine heliosCue.CueEngineInterface - // TektonRenderer renders Tekton CI/CD resources via CUE engine. - TektonRenderer heliosCue.TektonRendererInterface - // GitFactory allows injecting a custom GitOps client (e.g. for testing) - GitFactory func(string, string, string) gitops.GitOpsClientInterface -} - -// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=app.helios.io,resources=heliosapps/finalizers,verbs=update -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete - -// Reconcile handles the reconciliation loop for HeliosApp -// Controller does NOT iterate components/traits - all orchestration is in CUE -func (r *HeliosAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := logf.FromContext(ctx) - - // 1. Fetch HeliosApp CRD - var heliosApp appv1alpha1.HeliosApp - if err := r.Get(ctx, req.NamespacedName, &heliosApp); err != nil { - if errors.IsNotFound(err) { - log.Info("HeliosApp resource not found, ignoring") - return ctrl.Result{}, nil - } - return ctrl.Result{}, err - } - - log.Info("Reconciling HeliosApp", "name", heliosApp.Name, "namespace", heliosApp.Namespace) - - // 2. Map CRD to Application Model - appModel, err := r.mapCRDToModel(&heliosApp) - if err != nil { - log.Error(err, "Failed to map CRD to application model") - return ctrl.Result{}, err - } - - // 3. Render via CUE Engine - manifestBytes, err := r.CueEngine.Render(appModel) - if err != nil { - log.Error(err, "Failed to render application via CUE") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE rendering failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE -1 & 0: Tekton CI/CD Resources (Tasks, Pipeline, Triggers) - // All Tekton resources are rendered via CUE engine. - // ------------------------------------------------------------------ - if err := r.reconcileTektonResourcesCue(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to reconcile Tekton resources via CUE") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("CUE Tekton rendering failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE 0.5: Database Credential Secrets - // Generate and store secure credentials for components with database traits. - // Secrets are created BEFORE GitOps sync to ensure credentials exist - // when the application is deployed. - // ------------------------------------------------------------------ - if err := r.reconcileDatabaseSecrets(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to reconcile database secrets") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret creation failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE 0.7: Database Instance Provisioning - // Provision StatefulSets and headless Services for database traits. - // Runs AFTER secrets so that the credential Secret already exists - // when the database pod starts. - // ------------------------------------------------------------------ - if err := r.reconcileDatabaseInstance(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to reconcile database instance") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database instance provisioning failed: %v", err)) - return ctrl.Result{}, err - } - - // ------------------------------------------------------------------ - // PHASE 0.9: Inject Database Credentials into Backend Deployment - // Patches the live Deployment (deployed by ArgoCD) to add DB_HOST, - // DB_USER, DB_PASS env vars referencing the operator-managed Secret. - // Runs AFTER secrets and instances so the Secret already exists. - // ------------------------------------------------------------------ - if err := r.reconcileDatabaseSecretInjection(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to inject database secrets into Deployment") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Database secret injection failed: %v", err)) - return ctrl.Result{}, err - } - - // VALIDATION: Ensure image is present (Fix "First Commit Missing Image") - // This validation is for application workloads (GitOps pipeline downstream). - // We run this AFTER DB provisioning so databases can come up while app is building. - for _, comp := range appModel.App.Components { - // We can add more specific checks here based on component type - // For now, checks if 'image' property exists and is not empty for all components - // assuming all workloads need an image. - if img, ok := comp.Properties["image"].(string); !ok || img == "" { - msg := fmt.Sprintf("Component '%s' is waiting for image (likely building). Status: Pending.", comp.Name) - log.Info(msg) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhasePending, msg) - return ctrl.Result{}, nil // Wait for next update (CI/CD will update CR with image) - } - } - - // ------------------------------------------------------------------ - // PHASE 0.6: Trigger Initial PipelineRun (if not already done) - // ------------------------------------------------------------------ - if !heliosApp.Status.InitialBuildTriggered { - log.Info("Triggering initial PipelineRun for new HeliosApp") - - pipelineName := heliosApp.Spec.PipelineName - if pipelineName == "" { - pipelineName = "from-code-to-cluster" - } - pr, err := GeneratePipelineRunForManifestGeneration(&heliosApp, pipelineName) - if err != nil { - log.Error(err, "Failed to generate initial PipelineRun") - } else { - if err := ctrl.SetControllerReference(&heliosApp, pr, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for PipelineRun") - } - - if err := r.Client.Create(ctx, pr); err != nil { - if !errors.IsAlreadyExists(err) { - log.Error(err, "Failed to create initial PipelineRun") - } - } else { - log.Info("Created initial PipelineRun", "name", pr.GetName()) - } - - // Mark as triggered to avoid creating multiple PipelineRuns - heliosApp.Status.InitialBuildTriggered = true - if err := r.Status().Update(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to update InitialBuildTriggered status") - } - } - } - - // ------------------------------------------------------------------ - // PHASE 1: Render & GitOps (Moved below) - // ------------------------------------------------------------------ - - // 4. GitOps Helper: Get Token & Username - token := os.Getenv("GITHUB_TOKEN") - username := os.Getenv("GITHUB_USER") - if username == "" { - username = "git" // Default fallback - } - - if heliosApp.Spec.GitOpsSecretRef != "" { - var secret corev1.Secret - // Explicitly log the secret lookup attempt - if err := r.Get(ctx, types.NamespacedName{Name: heliosApp.Spec.GitOpsSecretRef, Namespace: heliosApp.Namespace}, &secret); err == nil { - if t, ok := secret.Data["token"]; ok { - token = string(t) - } else if p, ok := secret.Data["password"]; ok { - // Fallback to 'password' key (standard basic-auth secret from Tekton setup) - token = string(p) - } else { - log.Info("Secret found but 'token' or 'password' key is missing", "Secret", heliosApp.Spec.GitOpsSecretRef) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s missing 'token' key", heliosApp.Spec.GitOpsSecretRef)) - return ctrl.Result{}, nil - } - if u, ok := secret.Data["username"]; ok { - username = string(u) - } - } else { - log.Error(err, "Failed to get GitOps Secret", "Secret", heliosApp.Spec.GitOpsSecretRef) - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("Secret %s not found", heliosApp.Spec.GitOpsSecretRef)) - return ctrl.Result{}, nil - } - } - - // 5. GitOps Sync - - if token == "" { - err := fmt.Errorf("GitOps token is empty. Check Secret or GITHUB_TOKEN env var") - log.Error(err, "Authentication failed") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, "GitOps token missing") - return ctrl.Result{}, nil // Don't retry immediately if config is missing - } - - // OPTIMIZATION: Check Hash - currentHash := r.computeHash(manifestBytes) - if heliosApp.Status.LastAppliedHash == currentHash { - log.Info("Manifest hash unchanged, skipping GitOps sync", "hash", currentHash) - - // Still ensure status is Ready if it was previously set - if heliosApp.Status.Phase != appv1alpha1.PhaseReady { - heliosApp.Status.Phase = appv1alpha1.PhaseReady - if err := r.Status().Update(ctx, &heliosApp); err != nil { - return ctrl.Result{}, err - } - } - } else { - // Use GitFactory if available, otherwise default to NewGitOpsClient - getGitClient := r.GitFactory - if getGitClient == nil { - getGitClient = func(repo, user, token string) gitops.GitOpsClientInterface { - return gitops.NewGitOpsClient(repo, user, token) - } - } - - gitClient := getGitClient(heliosApp.Spec.GitOpsRepo, username, token) - targetPath := fmt.Sprintf("%s/manifest.yaml", heliosApp.Spec.GitOpsPath) - - if err := gitClient.SyncManifest(ctx, targetPath, string(manifestBytes)); err != nil { - log.Error(err, "GitOps sync failed") - r.updateStatus(ctx, &heliosApp, appv1alpha1.PhaseFailed, fmt.Sprintf("GitOps failed: %v", err)) - return ctrl.Result{}, err - } - - // 6. Update Status - heliosApp.Status.Phase = appv1alpha1.PhaseReady - heliosApp.Status.Message = fmt.Sprintf("Manifest pushed to %s/%s", heliosApp.Spec.GitOpsRepo, targetPath) - heliosApp.Status.LastAppliedHash = currentHash - // We clear ResourcesCreated as we are not managing them directly anymore - heliosApp.Status.ResourcesCreated = nil - - if err := r.Status().Update(ctx, &heliosApp); err != nil { - log.Error(err, "Failed to update status") - return ctrl.Result{}, err - } - log.Info("Successfully reconciled HeliosApp via GitOps", "newHash", currentHash) - } - - // 7. Ensure ArgoCD Application exists - log.Info("Ensuring ArgoCD Application exists") - argoApp, err := GenerateArgoApplication(&heliosApp) - if err != nil { - log.Error(err, "Failed to generate ArgoCD Application manifest") - // We don't return error here to avoid loop if GitOps was successful, just log it. - // Or maybe we should retry? Let's log and continue for now. - } else { - // Define ArgoCD Application identity - argoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) - // We use Sever-Side Apply or Create/Update logic - // Since ArgoCD app is in "argocd" namespace usually, we need permissions there. - // For simplicity/demo: Try to get, if not found create. - - foundArgoApp := &unstructured.Unstructured{} - foundArgoApp.SetGroupVersionKind(argoApp.GroupVersionKind()) - - key := client.ObjectKey{ - Name: argoApp.GetName(), - Namespace: argoApp.GetNamespace(), - } - - if err := r.Client.Get(ctx, key, foundArgoApp); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating ArgoCD Application", "name", argoApp.GetName()) - if err := r.Client.Create(ctx, argoApp); err != nil { - log.Error(err, "Failed to create ArgoCD Application") - } - } else { - log.Error(err, "Failed to get ArgoCD Application") - } - } else { - // Optional: Update if needed (checkout 'spec' diff) - log.Info("ArgoCD Application already exists", "name", argoApp.GetName()) - } - } - - // NOTE: Ingress removed - use port-forwarding for EventListener: - // kubectl port-forward svc/el--listener 8080:8080 - - return ctrl.Result{}, nil -} - -// mapCRDToModel converts HeliosApp CRD to CUE Application Model -func (r *HeliosAppReconciler) mapCRDToModel(app *appv1alpha1.HeliosApp) (heliosCue.Application, error) { - components := make([]heliosCue.Component, len(app.Spec.Components)) - - for i, c := range app.Spec.Components { - // Parse properties from RawExtension - var props map[string]any - if c.Properties != nil && c.Properties.Raw != nil { - if err := json.Unmarshal(c.Properties.Raw, &props); err != nil { - return heliosCue.Application{}, fmt.Errorf("failed to parse component properties: %w", err) - } - } - - // Parse traits - traits := make([]heliosCue.Trait, len(c.Traits)) - for j, t := range c.Traits { - var traitProps map[string]any - if t.Properties != nil && t.Properties.Raw != nil { - if err := json.Unmarshal(t.Properties.Raw, &traitProps); err != nil { - return heliosCue.Application{}, fmt.Errorf("failed to parse trait properties: %w", err) - } - } - traits[j] = heliosCue.Trait{ - Type: t.Type, - Properties: traitProps, - } - } - - components[i] = heliosCue.Component{ - Name: c.Name, - Type: c.Type, - Properties: props, - Traits: traits, - } - } - - return heliosCue.Application{ - App: heliosCue.AppSpec{ - Name: app.Name, - Namespace: app.Namespace, - Owner: app.Spec.Owner, - Description: app.Spec.Description, - Components: components, - }, - }, nil -} - -// mapCRDToTektonInput converts HeliosApp CRD to TektonInput for CUE rendering. -// This is the bridge between HeliosApp spec fields and the CUE #TektonInput schema. -func (r *HeliosAppReconciler) mapCRDToTektonInput(app *appv1alpha1.HeliosApp) heliosCue.TektonInput { - input := heliosCue.TektonInput{ - AppName: app.Name, - Namespace: app.Namespace, - GitRepo: app.Spec.GitRepo, - GitBranch: app.Spec.GitBranch, - ImageRepo: app.Spec.ImageRepo, - GitOpsRepo: app.Spec.GitOpsRepo, - GitOpsPath: app.Spec.GitOpsPath, - GitOpsBranch: app.Spec.GitOpsBranch, - GitOpsSecretRef: app.Spec.GitOpsSecretRef, - WebhookDomain: app.Spec.WebhookDomain, - WebhookSecret: app.Spec.WebhookSecret, - PipelineName: app.Spec.PipelineName, - PipelineType: app.Spec.PipelineName, // pipelineType uses same value as pipelineName - TriggerType: "github-push", // Default; extend HeliosAppSpec if needed - ServiceAccount: app.Spec.ServiceAccount, - PVCName: app.Spec.PVCName, - ContextSubpath: app.Spec.ContextSubpath, - Replicas: int(app.Spec.Replicas), - Port: int(app.Spec.Port), - TestCommand: app.Spec.TestCommand, - DockerSecret: "docker-credentials", - ArgoCDNamespace: app.Spec.ArgoCDNamespace, - ArgoCDProject: app.Spec.ArgoCDProject, - } - - // Apply defaults for fields that may be empty - input.GitBranch = cmp.Or(input.GitBranch, "main") - input.GitOpsBranch = cmp.Or(input.GitOpsBranch, "main") - input.GitOpsSecretRef = cmp.Or(input.GitOpsSecretRef, "github-credentials") - input.WebhookSecret = cmp.Or(input.WebhookSecret, "github-webhook-secret") - if input.PipelineName == "" { - input.PipelineName = "from-code-to-cluster" - input.PipelineType = "from-code-to-cluster" - } - input.ServiceAccount = cmp.Or(input.ServiceAccount, "default") - if input.Replicas <= 0 { - input.Replicas = 1 - } - if input.Port <= 0 { - input.Port = 8080 - } - - return input -} - -// reconcileTektonResourcesCue renders Tekton resources via CUE and applies them. -// This is the NEW path that replaces all hardcoded Generate* functions. -func (r *HeliosAppReconciler) reconcileTektonResourcesCue(ctx context.Context, app *appv1alpha1.HeliosApp) error { - log := logf.FromContext(ctx) - - // 1. Map CRD → TektonInput - tektonInput := r.mapCRDToTektonInput(app) - - // 2. Render via CUE - objects, err := r.TektonRenderer.RenderTektonResources(tektonInput) - if err != nil { - return fmt.Errorf("CUE TektonRenderer failed: %w", err) - } - - log.Info("CUE rendered Tekton resources", "count", len(objects)) - - // 3. Apply each rendered resource - for _, obj := range objects { - // Set owner reference (skip cluster-scoped resources) - if obj.GetNamespace() != "" { - if err := ctrl.SetControllerReference(app, obj, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference", "kind", obj.GetKind(), "name", obj.GetName()) - continue - } - } - - // Create or update - found := &unstructured.Unstructured{} - found.SetGroupVersionKind(obj.GroupVersionKind()) - err := r.Client.Get(ctx, client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()}, found) - if err != nil { - if errors.IsNotFound(err) { - log.Info("Creating resource", "kind", obj.GetKind(), "name", obj.GetName()) - if err := r.Client.Create(ctx, obj); err != nil { - log.Error(err, "Failed to create resource", "kind", obj.GetKind(), "name", obj.GetName()) - } - } else { - log.Error(err, "Failed to get resource", "kind", obj.GetKind(), "name", obj.GetName()) - } - } else { - // Update existing resource's spec - found.Object["spec"] = obj.Object["spec"] - if err := r.Client.Update(ctx, found); err != nil { - log.Error(err, "Failed to update resource", "kind", obj.GetKind(), "name", obj.GetName()) - } - } - } - - // 4. Also ensure RBAC (SA, RoleBinding, ClusterRoleBinding) — these are not in CUE yet - r.ensureTektonRBAC(ctx, app) - - return nil -} - -// ensureTektonRBAC creates ServiceAccount, RoleBinding, ClusterRoleBinding. -// These are infrastructure resources not managed by CUE (they are cluster lifecycle, not app lifecycle). -func (r *HeliosAppReconciler) ensureTektonRBAC(ctx context.Context, app *appv1alpha1.HeliosApp) { - log := logf.FromContext(ctx) - - sa := GenerateServiceAccount(app.Namespace) - if err := ctrl.SetControllerReference(app, sa, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for ServiceAccount") - } else { - foundSA := &unstructured.Unstructured{} - foundSA.SetGroupVersionKind(sa.GroupVersionKind()) - if err := r.Client.Get(ctx, client.ObjectKey{Name: sa.GetName(), Namespace: sa.GetNamespace()}, foundSA); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating ServiceAccount", "name", sa.GetName()) - r.Client.Create(ctx, sa) - } - } - } - - rb := GenerateRoleBinding(app.Namespace) - if err := ctrl.SetControllerReference(app, rb, r.Scheme); err != nil { - log.Error(err, "Failed to set owner reference for RoleBinding") - } else { - foundRB := &unstructured.Unstructured{} - foundRB.SetGroupVersionKind(rb.GroupVersionKind()) - if err := r.Client.Get(ctx, client.ObjectKey{Name: rb.GetName(), Namespace: rb.GetNamespace()}, foundRB); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating RoleBinding", "name", rb.GetName()) - r.Client.Create(ctx, rb) - } - } - } - - crb := GenerateClusterRoleBinding(app.Namespace) - foundCrb := &unstructured.Unstructured{} - foundCrb.SetGroupVersionKind(crb.GroupVersionKind()) - if err := r.Client.Get(ctx, client.ObjectKey{Name: crb.GetName()}, foundCrb); err != nil { - if errors.IsNotFound(err) { - log.Info("Creating ClusterRoleBinding", "name", crb.GetName()) - r.Client.Create(ctx, crb) - } - } -} - -// updateStatus updates the HeliosApp status -func (r *HeliosAppReconciler) updateStatus(ctx context.Context, app *appv1alpha1.HeliosApp, phase appv1alpha1.HeliosAppPhase, message string) { - app.Status.Phase = phase - app.Status.Message = message - if err := r.Status().Update(ctx, app); err != nil { - logf.FromContext(ctx).Error(err, "Failed to update status") - } -} - -// computeHash returns SHA256 of data -func (r *HeliosAppReconciler) computeHash(data []byte) string { - hash := sha256.Sum256(data) - return hex.EncodeToString(hash[:]) -} - -// SetupWithManager sets up the controller with the Manager -func (r *HeliosAppReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&appv1alpha1.HeliosApp{}). - Owns(&appsv1.Deployment{}). - Owns(&appsv1.StatefulSet{}). - Owns(&corev1.Service{}). - Owns(&networkingv1.Ingress{}). - Watches( - &corev1.Secret{}, - handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret), - ). - Named("heliosapp"). - Complete(r) -} - -// findObjectsForSecret maps Secret changes to HeliosApp reconcile requests. -// This ensures the controller re-reconciles when a referenced secret changes. -func (r *HeliosAppReconciler) findObjectsForSecret(ctx context.Context, obj client.Object) []reconcile.Request { - log := logf.FromContext(ctx) - - // List all HeliosApps in the same namespace - var heliosAppList appv1alpha1.HeliosAppList - if err := r.List(ctx, &heliosAppList, client.InNamespace(obj.GetNamespace())); err != nil { - log.Error(err, "Failed to list HeliosApps for secret watch") - return nil - } - - var requests []reconcile.Request - for _, app := range heliosAppList.Items { - // Check if this app references the changed secret - if app.Spec.GitOpsSecretRef == obj.GetName() || - app.Spec.WebhookSecret == obj.GetName() { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: app.Name, - Namespace: app.Namespace, - }, - }) - } - } - - return requests -} -``` - -#### [database_resources_test.go](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/operator/internal/controller/database_resources_test.go) - -6 new tests: -| Test | Description | -|------|-------------| -| `TestInjectDatabaseEnvVars/InjectsAllEnvVars` | Verifies DB_HOST, DB_USER, DB_PASS injected with correct secretKeyRef | -| `TestInjectDatabaseEnvVars/Idempotent` | Running twice doesn't duplicate env vars | -| `TestInjectDatabaseEnvVars/NoContainers` | Handles edge case of empty container list | -| `TestReconcileDatabaseSecretInjection/InjectsIntoExistingDeployment` | Full reconciliation with fake K8s client | -| `TestReconcileDatabaseSecretInjection/SkipsWhenNoTraits` | No-op when no database traits | -| `TestReconcileDatabaseSecretInjection/DeploymentNotFound_GracefulSkip` | Graceful skip when ArgoCD hasn't deployed yet | - ---- - -### 2. NestJS + Prisma Template - -Created [nestjs-prisma-template/](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template) with: - -| File | Purpose | -|------|---------| -| [template.yaml](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/template/template.yaml) | Backstage scaffolder template with DatabasePicker | -| [content/source/package.json](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/advanced-template/content/source/package.json) | NestJS v11+ & Prisma v6+ dependencies | -| [content/source/prisma/schema.prisma](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/prisma/schema.prisma) | Prisma schema using `env("DATABASE_URL")` | -| [content/source/src/prisma/prisma.service.ts](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts) | **Key file** — constructs `DATABASE_URL` from `DB_HOST`, `DB_USER`, `DB_PASS` with `encodeURIComponent` | -| [content/source/src/prisma/prisma.module.ts](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.module.ts) | Global module exporting PrismaService | -| [content/source/Dockerfile](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/advanced-template/content/source/Dockerfile) | Multi-stage build, runs `prisma migrate deploy` at startup | -| [content/gitops/helios-app.yaml](file:///home/phuochoan/Workspace/HCMUS/4th_Year/Capstone_Projects/helios-platform/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml) | HeliosApp CRD with database trait | - ---- - -## Verification Results - -| Check | Result | -|-------|--------| -| `go build ./...` | ✅ Passes | -| `go vet ./...` | ✅ No issues | -| `make test` | ✅ All tests pass (68% controller coverage) | -| `make build` | ✅ Operator binary builds | From 2c1647a8e1ce54e4fb415e221560eeb69c5ac568 Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Mon, 16 Mar 2026 21:44:07 +0700 Subject: [PATCH 14/19] chore: untrack .claude directory --- .claude/skills/implement-task/SKILL.md | 137 ---------- .claude/skills/use-modern-cue/SKILL.md | 361 ------------------------- .claude/skills/use-modern-go/SKILL.md | 291 -------------------- .gitignore | 3 + 4 files changed, 3 insertions(+), 789 deletions(-) delete mode 100644 .claude/skills/implement-task/SKILL.md delete mode 100644 .claude/skills/use-modern-cue/SKILL.md delete mode 100644 .claude/skills/use-modern-go/SKILL.md diff --git a/.claude/skills/implement-task/SKILL.md b/.claude/skills/implement-task/SKILL.md deleted file mode 100644 index 30e8753..0000000 --- a/.claude/skills/implement-task/SKILL.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -name: implement-task -description: Implementation workflow for the Helios Platform operator. Use when implementing features, fixing bugs, or making any code changes. Enforces best practices, testing, and verification. ---- - -# Implementation Task Workflow - -## Detected Stack - -!`grep -rh "^go " --include="go.mod" . 2>/dev/null | head -1 | grep . && grep -rh 'language.*version' --include="module.cue" cue.mod/ cue/cue.mod/ 2>/dev/null | head -1 | grep . || echo "unknown stack"` - -## Workflow - -When asked to implement a task, follow this exact sequence. Do NOT skip steps. - ---- - -### 1. Understand & Plan - -- Read and understand the full scope of the task -- Identify all files that need to change -- Check existing code patterns in the codebase — follow them -- If the task is non-trivial (>3 files), create a brief plan and get user approval before coding - -### 2. Implement with Best Practices - -**Code quality principles — apply ALL of these:** - -- **SOLID** — Single responsibility, open/closed, Liskov substitution, interface segregation, dependency inversion -- **DRY** — Don't repeat yourself. Extract reusable functions, constants, and definitions -- **KISS** — Keep it simple. Prefer clarity over cleverness -- **YAGNI** — Don't build what isn't needed yet -- **Separation of Concerns** — Each module/file/function has one clear purpose -- **Defensive Programming** — Validate inputs, handle edge cases, never swallow errors -- **Fail Fast** — Return errors immediately, don't accumulate them silently - -**Go-specific (use `use-modern-go` skill):** - -- Use modern Go idioms: `any`, `cmp.Or`, `slices`, `maps`, `errors.Is/As` -- Proper error wrapping with `fmt.Errorf("context: %w", err)` -- Table-driven tests with descriptive names -- Context propagation for cancellation and timeouts -- Meaningful variable/function names — no single-letter names outside loops -- Comments on exported types and functions (godoc convention) - -**CUE-specific (use `use-modern-cue` skill):** - -- Use `#` definitions for schemas, `_` for hidden fields, `let` for intermediates -- Validate with constraints, not imperative logic -- Follow the module's established patterns (registries, builders, etc.) -- File-level `@extern(embed)` when using `@embed` - -**Non-functional requirements:** - -- **Performance** — avoid unnecessary allocations, use efficient algorithms -- **Reliability** — handle all error paths, use retries where appropriate -- **Observability** — add structured logging at key decision points -- **Security** — no hardcoded secrets, validate all external input - -### 3. Test Everything - -After implementation, run ALL of these. Do NOT stop at the first passing test. - -```sh -# From apps/operator/ directory: - -# 1. Compilation check — must pass cleanly -go build ./... - -# 2. Static analysis -go vet ./... - -# 3. Full test suite with envtest (real K8s API server) -make test - -# 4. Build the operator binary -make build - -# 5. Verify CUE evaluation (if CUE files changed) -# Run CUE-related tests which validate CUE rendering -go test ./internal/cue/... -v -count=1 -``` - -**If any test fails:** -1. Read the error output carefully -2. Fix the root cause (not symptoms) -3. Re-run ALL tests, not just the one that failed -4. If a fix is complex or risky, explain the issue and ask the user before proceeding - -**If a dependency/infrastructure issue prevents testing:** -1. Document what couldn't be tested and why -2. Explain the risk to the user -3. Suggest how to test manually - -### 4. Verify & Report - -After all tests pass, verify: - -- [ ] `go build ./...` — compiles cleanly -- [ ] `go vet ./...` — no issues -- [ ] `make test` — all tests pass with envtest -- [ ] `make build` — operator binary builds -- [ ] No warnings in any output -- [ ] CRD manifests regenerated if API types changed (`controller-gen` runs in `make build`) - -**Report to user:** -- What was implemented (brief) -- What was tested (specific results) -- Any caveats or follow-up items - ---- - -## Rollback Strategy - -If something doesn't work after implementation: - -1. Identify the specific change that broke things -2. Revert that change only (not everything) -3. Re-run all tests to confirm the rollback works -4. Explain what went wrong and propose an alternative approach - ---- - -## Project-Specific Context - -**Directory structure:** -- `apps/operator/` — Go operator (controllers, CUE engine, GitOps) -- `cue/` — CUE definitions (schemas, tekton, components, traits, engine) -- `apps/operator/internal/cue/` — Go↔CUE bridge (engine.go, tekton.go) -- `apps/operator/internal/controller/` — Kubernetes controllers -- `apps/operator/internal/gitops/` — Git operations - -**Key patterns:** -- CUE handles resource generation (Tasks, Pipeline, Triggers, Ingress) -- Go handles runtime resources (PipelineRun, RBAC, ArgoCD Application) -- Controller reconciles HeliosApp CRD → CUE rendering → GitOps sync → ArgoCD -- Tests use envtest (real K8s API) + CUE engine unit tests + E2E validation tests diff --git a/.claude/skills/use-modern-cue/SKILL.md b/.claude/skills/use-modern-cue/SKILL.md deleted file mode 100644 index 9c6d0ee..0000000 --- a/.claude/skills/use-modern-cue/SKILL.md +++ /dev/null @@ -1,361 +0,0 @@ ---- -name: use-modern-cue -description: Apply modern CUE language syntax guidelines based on project's CUE version. Use when user asks for modern CUE code guidelines. ---- - -# Modern CUE Guidelines - -## Detected CUE Version - -!`grep -rh '"language"' --include="module.cue" cue.mod/ 2>/dev/null | grep -oP 'version:\s*"v?\K[^"]+' | head -1 | grep . || cue version 2>/dev/null | head -1 | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1 | grep . || echo unknown` - -## How to Use This Skill - -DO NOT search for module.cue files or try to detect the version yourself. Use ONLY the version shown above. - -**If version detected (not "unknown"):** - -- Say: "This project is using CUE X.XX.X, so I'll stick to modern CUE best practices and freely use language features up to and including this version. If you'd prefer a different target version, just let me know." -- Do NOT list features, do NOT ask for confirmation - -**If version is "unknown":** - -- Say: "Could not detect CUE version in this repository" -- Use AskUserQuestion: "Which CUE version should I target?" → [0.14] / [0.15] / [0.16] - -**When writing CUE code**, use ALL features from this document up to the target version: - -- Prefer modern patterns over deprecated/legacy ones -- Never use features from newer CUE versions than the target -- Always follow the best practices and avoid anti-patterns listed below - ---- - -## Core CUE Principles - -### Types Are Values - -CUE unifies types and values into a single lattice. A field can hold a type constraint, a concrete value, or something in between. - -```cue -port: int // type constraint -port: >0 & <65536 // narrower constraint -port: 8080 // concrete value -``` - -### Unification (`&`) - -Fundamental, commutative, associative, and idempotent — order never matters. - -```cue -a: {name: string, port: int} -a: {name: "web", replicas: 1} -// Result: a: {name: "web", port: int, replicas: 1} -``` - -### Definitions (`#`) and Hidden Definitions (`_#`) - -`#` defines schemas — closed structs, NOT emitted in output. `_#` for package-internal schemas. - -```cue -#Service: { - name: string - port: int & >0 & <=65535 - replicas: int | *1 -} - -myService: #Service & {name: "api", port: 8080} - -_#internalConfig: {debug: bool | *false} -``` - -### Hidden Fields (`_`) - -Not exported, exempt from closed struct rules. Use for internal values. - -```cue -_basePort: 8000 -services: { - api: {port: _basePort} - grpc: {port: _basePort + 1000} -} -``` - -### Disjunctions (`|`), Defaults (`*`), Optional (`?`), Required (`!`) - -```cue -#Deployment: { - name!: string // MUST be specified - protocol: "HTTP" | "HTTPS" | *"HTTPS" // enum with default - replicas?: int & >0 // MAY be specified - annotations?: {[string]: string} -} -``` - -### Closed vs Open Structs - -Definitions are closed by default. Add `...` to allow additional fields. - -```cue -#Strict: {name: string, port: int} // no extra fields -#Flexible: {name: string, port: int, ...} // extra fields allowed -``` - -### Pattern Constraints, Comprehensions, Interpolation, `let` - -```cue -// Pattern constraint: all string-keyed fields must satisfy #Service -[string]: #Service - -// List comprehension -ports: [for s in services {s.port}] - -// Field comprehension with conditional -rendered: { - for name, svc in services { - (name): {image: "reg/\(name):latest", port: svc.port} - } -} - -// Conditional field inclusion -if env == "prod" {replicas: 3} - -// let for intermediate computation (not emitted) -let _fullName = "app-\(name)" -metadata: labels: app: _fullName -``` - -### Validators and Constraints - -```cue -import "strings" - -#Label: { - key: string & =~"^[a-z][a-z0-9-]*$" - value: strings.MinRunes(1) & strings.MaxRunes(63) -} -#Port: int & >0 & <=65535 -``` - -### Builtin Functions - -```cue -len("hello") // 5 -close({a: 1}) // closes an open struct -and([int, >0, <100]) // unifies: int & >0 & <100 -or(["a", "b", "c"]) // disjunction: "a" | "b" | "c" -``` - ---- - -## Standard Library (Key Packages) - -```cue -import ("strings"; "list"; "regexp"; "math"; "encoding/json"; "encoding/yaml"; "struct"; "net"; "strconv") - -// strings -strings.Join(["a","b"], ",") strings.Split("a,b", ",") strings.Contains("ab", "a") -strings.HasPrefix/HasSuffix strings.Replace strings.ToUpper/ToLower strings.TrimSpace -strings.MinRunes(1) strings.MaxRunes(63) - -// list -list.Concat([[1,2],[3,4]]) list.Repeat([0],3) list.Contains([1,2],2) -list.Sort([3,1,2], list.Ascending) list.FlattenN list.UniqueItems -list.MinItems(1) list.MaxItems(10) - -// regexp -regexp.Match("^[a-z]+$", "hello") regexp.Find regexp.FindAll regexp.ReplaceAll - -// math -math.Floor math.Ceil math.Abs math.Pow - -// encoding -json.Marshal({a:1}) json.Unmarshal(str) json.Validate(str, schema) -yaml.Marshal yaml.Validate - -// struct -struct.MinFields(1) struct.MaxFields(10) - -// net -net.IPv4 net.IP net.FQDN -net.InCIDR net.ParseCIDR net.CompareIP // v0.16+ - -// strconv -strconv.ParseNumber // v0.16+: parse CUE numbers like "1Ki" -``` - ---- - -## Breaking Changes by Version - -| Version | Breaking Change | -|---------|----------------| -| **v0.9** | `language.version` REQUIRED in `module.cue` | -| **v0.11** | List arithmetic (`+`, `*`) removed → use `list.Concat`, `list.Repeat` | -| **v0.12** | `cue.Value.Decode` returns `int64` (not `int`) for CUE integers | -| **v0.12** | `@embed` stable — requires `@extern(embed)` file attribute | -| **v0.13** | Removed deprecated `cue.Runtime` methods | -| **v0.14** | Multiline strings require trailing newline | -| **v0.14** | Custom errors: `error("msg")` builtin | -| **v0.14** | JSON Schema generation: `cue def --out jsonschema` | -| **v0.16** | Multiline trailing newline **strictly enforced** | -| **v0.16** | `cue` commands inside `cue.mod/` directory now fail | -| **v0.16** | `cue mod publish` no longer ignores sub-dirs with `go.mod` | -| **v0.16** | `#"""#` accepted as string literal for `"` | -| **v0.16** | `cmdreferencepkg` experiment stabilized (always on) | -| **v0.16** | Removed deprecated: `cue/ast.Node.Comments`, `cue/parser.FromVersion`, `cue.Instance.Eval` | - -## Key Features by Version - -| Version | Feature | -|---------|---------| -| **v0.10** | `@if(tag)` conditional file inclusion, `@tag(name)` value injection, `@ignore()` | -| **v0.12** | File embedding: `@extern(embed)` + `@embed(file="x.json")` | -| **v0.13** | `cue refactor imports`, `cue mod mirror`, keywords as field labels | -| **v0.14** | `error()` builtin, `cue def --out jsonschema`, `cue fix --exp`, `cue help experiments` | -| **v0.15** | Full LSP support (go-to-def, find-refs, rename, completion, hover) | -| **v0.16** | `net.InCIDR/ParseCIDR/CompareIP`, `strconv.ParseNumber`, `tool/file.Symlink` | -| **v0.16** | Up to 80% faster evaluation, 60% less memory, embedded JSON/YAML LSP support | - ---- - -## Module & Package Best Practices - -### Module Declaration (v0.9+, REQUIRED) - -```cue -// cue.mod/module.cue -module: "github.com/myorg/myproject@v0" -language: version: "v0.16.0" - -deps: { - "github.com/some/dep@v0": v: "v0.1.0" -} -``` - -**ALWAYS** declare `language.version`. Never omit it. - -### Import Best Practices - -```cue -import ( - "strings" // built-in (no domain) - "list" - "github.com/myorg/project/schema" // user-defined (fully-qualified) - k8s "k8s.io/api/apps/v1" // named import for conflicts -) -``` - -- ALWAYS use absolute import paths (no relative imports) -- Group built-in and user-defined imports separately - -### Package Organization - -- One package per directory -- `package` declaration at top of every file -- Definitions in same package accessible across files without imports -- Hidden fields (`_`) scoped to package — not externally accessible -- Do NOT place CUE code inside `cue.mod/` (v0.16+: this errors) - -### File Embedding (v0.12+) - -```cue -@extern(embed) -package config - -data: _ @embed(file="config.json") -readme: string @embed(file="README.md", type=text) -cert: bytes @embed(file="cert.pem", type=binary) -templates: _ @embed(glob="templates/*.json") -optional: _ @embed(glob="overrides/*.yaml", allowEmptyGlob) -``` - -ALWAYS use `@extern(embed)` at file level. Only files within the same CUE module can be embedded. - ---- - -## CLI Quick Reference - -```sh -cue mod init github.com/myorg/project@v0 # init module -cue mod tidy # clean deps -cue fmt ./... # format -cue eval ./... # evaluate -cue eval -c ./... # require concreteness -cue vet data.yaml schema.cue -d '#Schema' # validate -cue export ./... --out json # export JSON -cue export ./... --out yaml # export YAML -cue def --out jsonschema ./... # JSON Schema (v0.14+) -cue fix ./... # fix deprecated syntax -cue get go k8s.io/api/apps/v1 # generate CUE from Go -cue refactor imports old/path new/path # refactor imports (v0.13+) -``` - ---- - -## Anti-Patterns - -### ❌ List arithmetic (removed in v0.11) - -```cue -// BAD // GOOD -combined: [1,2] + [3,4] import "list" -repeated: [0] * 3 combined: list.Concat([[1,2],[3,4]]) - repeated: list.Repeat([0], 3) -``` - -### ❌ Missing `language.version` - -```cue -// BAD // GOOD -module: "example.com/foo@v0" module: "example.com/foo@v0" - language: version: "v0.16.0" -``` - -### ❌ Overly permissive types - -```cue -// BAD — defeats CUE's purpose // GOOD — constrain narrowly -config: _ config: #AppConfig -``` - -### ❌ Concrete values in definitions - -```cue -// BAD — mixes data and schema // GOOD — definitions are constraints -#Service: {name: "my-svc"} #Service: {name: string & strings.MinRunes(1)} -``` - -### ❌ Repeated constraints - -```cue -// BAD // GOOD -svcA: port: int & >0 & <=65535 #Port: int & >0 & <=65535 -svcB: port: int & >0 & <=65535 svcA: port: #Port - svcB: port: #Port -``` - -### ❌ `@embed` without `@extern(embed)` (v0.12+) - -```cue -// BAD // GOOD -package config @extern(embed) -data: _ @embed(file="d.json") package config - data: _ @embed(file="d.json") -``` - -### ❌ Relative imports - -```cue -// BAD // GOOD -import "./utils" import "github.com/myorg/project/utils" -``` - -### ❌ Multiline strings without trailing newline (v0.14+) - -```cue -// BAD // GOOD -msg: """ msg: """ - Hello""" Hello - """ -``` diff --git a/.claude/skills/use-modern-go/SKILL.md b/.claude/skills/use-modern-go/SKILL.md deleted file mode 100644 index 3755a1a..0000000 --- a/.claude/skills/use-modern-go/SKILL.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -name: use-modern-go -description: Apply modern Go syntax guidelines based on project's Go version. Use when user ask for modern Go code guidelines. ---- - -# Modern Go Guidelines - -## Detected Go Version - -!`grep -rh "^go " --include="go.mod" . 2>/dev/null | cut -d' ' -f2 | sort | uniq -c | sort -nr | head -1 | xargs | cut -d' ' -f2 | grep . || echo unknown` - -## How to Use This Skill - -DO NOT search for go.mod files or try to detect the version yourself. Use ONLY the version shown above. - -**If version detected (not "unknown"):** -- Say: "This project is using Go X.XX, so I’ll stick to modern Go best practices and freely use language features up to and including this version. If you’d prefer a different target version, just let me know." -- Do NOT list features, do NOT ask for confirmation - -**If version is "unknown":** -- Say: "Could not detect Go version in this repository" -- Use AskUserQuestion: "Which Go version should I target?" → [1.23] / [1.24] / [1.25] / [1.26] - -**When writing Go code**, use ALL features from this document up to the target version: -- Prefer modern built-ins and packages (`slices`, `maps`, `cmp`) over legacy patterns -- Never use features from newer Go versions than the target -- Never use outdated patterns when a modern alternative is available - ---- - -## Features by Go Version - -### Go 1.0+ - -- `time.Since`: `time.Since(start)` instead of `time.Now().Sub(start)` - -### Go 1.8+ - -- `time.Until`: `time.Until(deadline)` instead of `deadline.Sub(time.Now())` - -### Go 1.13+ - -- `errors.Is`: `errors.Is(err, target)` instead of `err == target` (works with wrapped errors) - -### Go 1.18+ - -- `any`: Use `any` instead of `interface{}` -- `bytes.Cut`: `before, after, found := bytes.Cut(b, sep)` instead of Index+slice -- `strings.Cut`: `before, after, found := strings.Cut(s, sep)` - -### Go 1.19+ - -- `fmt.Appendf`: `buf = fmt.Appendf(buf, "x=%d", x)` instead of `[]byte(fmt.Sprintf(...))` -- `atomic.Bool`/`atomic.Int64`/`atomic.Pointer[T]`: Type-safe atomics instead of `atomic.StoreInt32` - -```go -var flag atomic.Bool -flag.Store(true) -if flag.Load() { ... } - -var ptr atomic.Pointer[Config] -ptr.Store(cfg) -``` - -### Go 1.20+ - -- `strings.Clone`: `strings.Clone(s)` to copy string without sharing memory -- `bytes.Clone`: `bytes.Clone(b)` to copy byte slice -- `strings.CutPrefix/CutSuffix`: `if rest, ok := strings.CutPrefix(s, "pre:"); ok { ... }` -- `errors.Join`: `errors.Join(err1, err2)` to combine multiple errors -- `context.WithCancelCause`: `ctx, cancel := context.WithCancelCause(parent)` then `cancel(err)` -- `context.Cause`: `context.Cause(ctx)` to get the error that caused cancellation - -### Go 1.21+ - -**Built-ins:** -- `min`/`max`: `max(a, b)` instead of if/else comparisons -- `clear`: `clear(m)` to delete all map entries, `clear(s)` to zero slice elements - -**slices package:** -- `slices.Contains`: `slices.Contains(items, x)` instead of manual loops -- `slices.Index`: `slices.Index(items, x)` returns index (-1 if not found) -- `slices.IndexFunc`: `slices.IndexFunc(items, func(item T) bool { return item.ID == id })` -- `slices.SortFunc`: `slices.SortFunc(items, func(a, b T) int { return cmp.Compare(a.X, b.X) })` -- `slices.Sort`: `slices.Sort(items)` for ordered types -- `slices.Max`/`slices.Min`: `slices.Max(items)` instead of manual loop -- `slices.Reverse`: `slices.Reverse(items)` instead of manual swap loop -- `slices.Compact`: `slices.Compact(items)` removes consecutive duplicates in-place -- `slices.Clip`: `slices.Clip(s)` removes unused capacity -- `slices.Clone`: `slices.Clone(s)` creates a copy - -**maps package:** -- `maps.Clone`: `maps.Clone(m)` instead of manual map iteration -- `maps.Copy`: `maps.Copy(dst, src)` copies entries from src to dst -- `maps.DeleteFunc`: `maps.DeleteFunc(m, func(k K, v V) bool { return condition })` - -**sync package:** -- `sync.OnceFunc`: `f := sync.OnceFunc(func() { ... })` instead of `sync.Once` + wrapper -- `sync.OnceValue`: `getter := sync.OnceValue(func() T { return computeValue() })` - -**context package:** -- `context.AfterFunc`: `stop := context.AfterFunc(ctx, cleanup)` runs cleanup on cancellation -- `context.WithTimeoutCause`: `ctx, cancel := context.WithTimeoutCause(parent, d, err)` -- `context.WithDeadlineCause`: Similar with deadline instead of duration - -### Go 1.22+ - -**Loops:** -- `for i := range n`: `for i := range len(items)` instead of `for i := 0; i < len(items); i++` -- Loop variables are now safe to capture in goroutines (each iteration has its own copy) - -**cmp package:** -- `cmp.Or`: `cmp.Or(flag, env, config, "default")` returns first non-zero value - -```go -// Instead of: -name := os.Getenv("NAME") -if name == "" { - name = "default" -} -// Use: -name := cmp.Or(os.Getenv("NAME"), "default") -``` - -**reflect package:** -- `reflect.TypeFor`: `reflect.TypeFor[T]()` instead of `reflect.TypeOf((*T)(nil)).Elem()` - -**net/http:** -- Enhanced `http.ServeMux` patterns: `mux.HandleFunc("GET /api/{id}", handler)` with method and path params -- `r.PathValue("id")` to get path parameters - -### Go 1.23+ - -- `maps.Keys(m)` / `maps.Values(m)` return iterators -- `slices.Collect(iter)` not manual loop to build slice from iterator -- `slices.Sorted(iter)` to collect and sort in one step - -```go -keys := slices.Collect(maps.Keys(m)) // not: for k := range m { keys = append(keys, k) } -sortedKeys := slices.Sorted(maps.Keys(m)) // collect + sort -for k := range maps.Keys(m) { process(k) } // iterate directly -``` - -**time package** - -- `time.Tick`: Use `time.Tick` freely — as of Go 1.23, the garbage collector can recover unreferenced tickers, even if they haven't been stopped. The Stop method is no longer necessary to help the garbage collector. There is no longer any reason to prefer NewTicker when Tick will do. - -### Go 1.24+ - -- `t.Context()` not `context.WithCancel(context.Background())` in tests. - ALWAYS use t.Context() when a test function needs a context. - -Before: -```go -func TestFoo(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - result := doSomething(ctx) -} -``` -After: -```go -func TestFoo(t *testing.T) { - ctx := t.Context() - result := doSomething(ctx) -} -``` - -- `omitzero` not `omitempty` in JSON struct tags. - ALWAYS use omitzero for time.Duration, time.Time, structs, slices, maps. - -Before: -```go -type Config struct { - Timeout time.Duration `json:"timeout,omitempty"` // doesn't work for Duration! -} -``` -After: -```go -type Config struct { - Timeout time.Duration `json:"timeout,omitzero"` -} -``` - -- `b.Loop()` not `for i := 0; i < b.N; i++` in benchmarks. - ALWAYS use b.Loop() for the main loop in benchmark functions. - -Before: -```go -func BenchmarkFoo(b *testing.B) { - for i := 0; i < b.N; i++ { - doWork() - } -} -``` -After: -```go -func BenchmarkFoo(b *testing.B) { - for b.Loop() { - doWork() - } -} -``` - -- `strings.SplitSeq` not `strings.Split` when iterating. - ALWAYS use SplitSeq/FieldsSeq when iterating over split results in a for-range loop. - -Before: -```go -for _, part := range strings.Split(s, ",") { - process(part) -} -``` -After: -```go -for part := range strings.SplitSeq(s, ",") { - process(part) -} -``` -Also: `strings.FieldsSeq`, `bytes.SplitSeq`, `bytes.FieldsSeq`. - -### Go 1.25+ - -- `wg.Go(fn)` not `wg.Add(1)` + `go func() { defer wg.Done(); ... }()`. - ALWAYS use wg.Go() when spawning goroutines with sync.WaitGroup. - -Before: -```go -var wg sync.WaitGroup -for _, item := range items { - wg.Add(1) - go func() { - defer wg.Done() - process(item) - }() -} -wg.Wait() -``` -After: -```go -var wg sync.WaitGroup -for _, item := range items { - wg.Go(func() { - process(item) - }) -} -wg.Wait() -``` - -### Go 1.26+ - -- `new(val)` not `x := val; &x` — returns pointer to any value. - Go 1.26 extends new() to accept expressions, not just types. - Type is inferred: new(0) → *int, new("s") → *string, new(T{}) → *T. - DO NOT use `x := val; &x` pattern — always use new(val) directly. - DO NOT use redundant casts like new(int(0)) — just write new(0). - Common use case: struct fields with pointer types. - -Before: -```go -timeout := 30 -debug := true -cfg := Config{ - Timeout: &timeout, - Debug: &debug, -} -``` -After: -```go -cfg := Config{ - Timeout: new(30), // *int - Debug: new(true), // *bool -} -``` - -- `errors.AsType[T](err)` not `errors.As(err, &target)`. - ALWAYS use errors.AsType when checking if error matches a specific type. - -Before: -```go -var pathErr *os.PathError -if errors.As(err, &pathErr) { - handle(pathErr) -} -``` -After: -```go -if pathErr, ok := errors.AsType[*os.PathError](err); ok { - handle(pathErr) -} -``` diff --git a/.gitignore b/.gitignore index 4c97ca2..ba250fb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ apps/portal/packages/backend/*.sqlite # Environment secrets .env !.env.example + +# Claude +.claude/ From bb65ced21e4e761e84494e19535d043e576a9357 Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 22 Mar 2026 20:23:01 +0700 Subject: [PATCH 15/19] chore: harden operator DB flow and upgrade portal to Backstage 1.49.1 - harden operator database reconciliation with required secret validation, postgres-only handling, targeted env injection, and probe command fixes - narrow Argo CD ignoreDifferences to DB env vars and update related operator tests - upgrade portal dependencies to Backstage 1.49.1 and fix EntityPage compatibility - wire database settings through scaffolder templates and generated GitOps manifests - improve cross-platform startup/prereq scripts with safe .env parsing and portable base64 decoding - update CI workflow action versions, docs Go requirement, and env-file gitignore patterns --- .github/workflows/operator-ci.yml | 8 +- .github/workflows/portal-ci.yml | 6 +- .gitignore | 4 + Taskfile.yml | 23 +- apps/operator/.github/workflows/lint.yml | 8 +- apps/operator/.github/workflows/test-e2e.yml | 4 +- apps/operator/.github/workflows/test.yml | 4 +- apps/operator/config/rbac/role.yaml | 13 - apps/operator/go.mod | 2 +- .../internal/controller/argocd_resources.go | 4 +- .../internal/controller/database_resources.go | 106 +- .../controller/database_resources_test.go | 126 +- apps/portal/backstage.json | 2 +- .../content/gitops/helios-app.yaml | 6 + .../examples/advanced-template/template.yaml | 2 + .../content/gitops/helios-app.yaml | 4 +- .../source/src/prisma/prisma.service.ts | 6 +- .../nestjs-prisma-template/template.yaml | 2 + apps/portal/package.json | 8 +- apps/portal/packages/app/package.json | 69 +- apps/portal/packages/app/src/App.tsx | 19 +- .../app/src/components/catalog/EntityPage.tsx | 41 +- .../DatabasePicker.tsx | 16 +- .../DatabasePickerExtension/extension.ts | 6 +- .../DatabasePickerExtension/index.ts | 2 +- .../packages/app/src/scaffolder/index.ts | 2 +- apps/portal/packages/backend/package.json | 66 +- apps/portal/packages/backend/src/index.ts | 7 +- apps/portal/start-dev.sh | 76 +- apps/portal/yarn.lock | 7631 +++++++++-------- docs/APP_STARTUP_GUIDE.md | 2 +- scripts/check-prereqs.sh | 60 +- scripts/start-portal.ps1 | 31 +- 33 files changed, 4727 insertions(+), 3639 deletions(-) diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml index a9f53b5..4b47d00 100644 --- a/.github/workflows/operator-ci.yml +++ b/.github/workflows/operator-ci.yml @@ -11,7 +11,7 @@ on: pull_request: branches: - main - - 'features/*' + - "features/*" jobs: test: @@ -23,12 +23,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: - go-version: '1.26' + go-version: "1.26" cache-dependency-path: apps/operator/go.sum - name: Run tests diff --git a/.github/workflows/portal-ci.yml b/.github/workflows/portal-ci.yml index 7bd633a..82851cf 100644 --- a/.github/workflows/portal-ci.yml +++ b/.github/workflows/portal-ci.yml @@ -19,12 +19,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: - node-version: "22" + node-version: "24" cache: "yarn" cache-dependency-path: "apps/portal/yarn.lock" diff --git a/.gitignore b/.gitignore index ba250fb..79acdeb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,11 @@ apps/portal/packages/backend/*.sqlite # Environment secrets .env +.env.* +**/.env +**/.env.* !.env.example +!**/.env.example # Claude .claude/ diff --git a/Taskfile.yml b/Taskfile.yml index 5657fb9..85d1496 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -217,7 +217,28 @@ tasks: dir: apps/portal cmds: - cmd: | - ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) + decode_base64() { + if printf 'Zg==' | base64 --decode >/dev/null 2>&1; then + base64 --decode + return + fi + if printf 'Zg==' | base64 -d >/dev/null 2>&1; then + base64 -d + return + fi + if printf 'Zg==' | base64 -D >/dev/null 2>&1; then + base64 -D + return + fi + if command -v openssl >/dev/null 2>&1; then + openssl base64 -d -A + return + fi + echo "No compatible base64 decoder found" >&2 + return 1 + } + + ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | decode_base64) TOKEN_JSON=$(curl -k -s -X POST \ -H "Content-Type: application/json" \ -d "{\"username\":\"admin\",\"password\":\"$ARGOCD_PASS\"}" \ diff --git a/apps/operator/.github/workflows/lint.yml b/apps/operator/.github/workflows/lint.yml index 86e3845..330765d 100644 --- a/apps/operator/.github/workflows/lint.yml +++ b/apps/operator/.github/workflows/lint.yml @@ -10,14 +10,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Run linter - uses: golangci/golangci-lint-action@v8 + uses: golangci/golangci-lint-action@v9 with: - version: v2.1.0 + version: v2.11.3 diff --git a/apps/operator/.github/workflows/test-e2e.yml b/apps/operator/.github/workflows/test-e2e.yml index 68fd1ed..43a62f5 100644 --- a/apps/operator/.github/workflows/test-e2e.yml +++ b/apps/operator/.github/workflows/test-e2e.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod diff --git a/apps/operator/.github/workflows/test.yml b/apps/operator/.github/workflows/test.yml index fc2e80d..415418d 100644 --- a/apps/operator/.github/workflows/test.yml +++ b/apps/operator/.github/workflows/test.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod diff --git a/apps/operator/config/rbac/role.yaml b/apps/operator/config/rbac/role.yaml index a0f6593..ef8fe77 100644 --- a/apps/operator/config/rbac/role.yaml +++ b/apps/operator/config/rbac/role.yaml @@ -47,19 +47,6 @@ rules: - apps resources: - deployments - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - networking.k8s.io - resources: - - ingresses verbs: - create - delete diff --git a/apps/operator/go.mod b/apps/operator/go.mod index 85d77d0..30c6eba 100644 --- a/apps/operator/go.mod +++ b/apps/operator/go.mod @@ -1,6 +1,6 @@ module github.com/helios-platform-team/helios-platform/apps/operator -go 1.26.1 +go 1.26 require ( cuelang.org/go v0.16.0 diff --git a/apps/operator/internal/controller/argocd_resources.go b/apps/operator/internal/controller/argocd_resources.go index 9e67943..f68cfe8 100644 --- a/apps/operator/internal/controller/argocd_resources.go +++ b/apps/operator/internal/controller/argocd_resources.go @@ -51,8 +51,8 @@ func GenerateArgoApplication(heliosApp *appv1alpha1.HeliosApp) (*unstructured.Un map[string]any{ "group": "apps", "kind": "Deployment", - "jsonPointers": []any{ - "/spec/template/spec/containers/0/env", + "jqPathExpressions": []any{ + `.spec.template.spec.containers[].env[] | select(.name == "DB_HOST" or .name == "DB_USER" or .name == "DB_PASS")`, }, }, }, diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index 7712846..b2cd32c 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -62,6 +62,8 @@ const ( PostgresDataSubPath = "pgdata" ) +var requiredDatabaseSecretKeys = []string{"DB_USER", "DB_PASS", "DB_HOST"} + // DatabaseCredentials holds generated database credentials type DatabaseCredentials struct { Username string @@ -86,7 +88,7 @@ func GenerateSecurePassword(length int) (string, error) { password := make([]byte, length) charsetLen := big.NewInt(int64(len(PasswordCharset))) - for i := range length { + for i := 0; i < length; i++ { idx, err := rand.Int(rand.Reader, charsetLen) if err != nil { return "", fmt.Errorf("failed to generate random index: %w", err) @@ -224,6 +226,13 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app } for _, dbTrait := range dbTraits { + if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { + log.V(1).Info("Skipping credential secret creation for non-postgres database type", + "component", dbTrait.ComponentName, + "dbType", dbTrait.Properties.DBType) + continue + } + secretName := GetDatabaseSecretName(dbTrait.ComponentName) dbHost := GetDatabaseHost(dbTrait.ComponentName) @@ -235,6 +244,13 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecrets(ctx context.Context, app }, existingSecret) if err == nil { + if validateErr := ValidateDatabaseSecret(existingSecret, dbHost); validateErr != nil { + log.Error(validateErr, "Existing database secret is missing required keys", + "component", dbTrait.ComponentName, + "secret", secretName) + return fmt.Errorf("existing database secret %s is invalid: %w", secretName, validateErr) + } + // Secret already exists - do not overwrite to preserve credentials log.Info("Database secret already exists, skipping", "component", dbTrait.ComponentName, @@ -462,11 +478,25 @@ var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} // If an env var exists but points to a different source (wrong secret, plain // value, etc.), it is updated to the expected secretKeyRef. func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { + changed, _ := InjectDatabaseEnvVarsForContainer(deploy, secretName, "") + return changed +} + +// InjectDatabaseEnvVarsForContainer patches a Deployment container to include +// DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. +// If preferredContainerName is not found, it falls back to the first container. +// Returns (changed, exactMatch). +func InjectDatabaseEnvVarsForContainer(deploy *appsv1.Deployment, secretName, preferredContainerName string) (bool, bool) { if len(deploy.Spec.Template.Spec.Containers) == 0 { - return false + return false, false + } + + containerIndex, exactMatch := selectTargetContainerIndex(deploy.Spec.Template.Spec.Containers, preferredContainerName) + if containerIndex < 0 { + return false, false } - container := &deploy.Spec.Template.Spec.Containers[0] + container := &deploy.Spec.Template.Spec.Containers[containerIndex] changed := false for _, envName := range databaseEnvVarNames { @@ -510,7 +540,25 @@ func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { } } - return changed + return changed, exactMatch +} + +func selectTargetContainerIndex(containers []corev1.Container, preferredContainerName string) (int, bool) { + if len(containers) == 0 { + return -1, false + } + + if preferredContainerName == "" { + return 0, true + } + + for i := range containers { + if containers[i].Name == preferredContainerName { + return i, true + } + } + + return 0, false } // reconcileDatabaseSecretInjection patches live Deployments (deployed by ArgoCD) @@ -533,6 +581,13 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte pendingInjection := false for _, dbTrait := range dbTraits { + if strings.ToLower(dbTrait.Properties.DBType) != "postgres" { + log.V(1).Info("Skipping env var injection for non-postgres database type", + "component", dbTrait.ComponentName, + "dbType", dbTrait.Properties.DBType) + continue + } + secretName := GetDatabaseSecretName(dbTrait.ComponentName) deployName := dbTrait.ComponentName @@ -556,7 +611,16 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte } // Inject env vars if not already present. - if !InjectDatabaseEnvVars(deploy, secretName) { + changed, exactContainerMatch := InjectDatabaseEnvVarsForContainer(deploy, secretName, dbTrait.ComponentName) + if !exactContainerMatch { + log.Info("Preferred application container not found, using first container for DB env injection", + "component", dbTrait.ComponentName, + "deployment", deployName, + "preferredContainer", dbTrait.ComponentName, + "fallbackContainer", deploy.Spec.Template.Spec.Containers[0].Name) + } + + if !changed { log.V(1).Info("Database env vars already injected, skipping", "component", dbTrait.ComponentName, "deployment", deployName) @@ -593,6 +657,8 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s "helios.io/db-type": "postgres", } + probeCommand := fmt.Sprintf("pg_isready -U \"$POSTGRES_USER\" -d %q -p \"$PGPORT\"", dbName) + return &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -684,7 +750,7 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s ReadinessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, + Command: []string{"sh", "-c", probeCommand}, }, }, InitialDelaySeconds: 5, @@ -693,7 +759,7 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)", "-d", dbName, "-p", "$(PGPORT)"}, + Command: []string{"sh", "-c", probeCommand}, }, }, InitialDelaySeconds: 30, @@ -724,6 +790,32 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s }, nil } +// ValidateDatabaseSecret ensures an existing database credential Secret has all +// required keys and the expected DB_HOST value. +func ValidateDatabaseSecret(secret *corev1.Secret, expectedHost string) error { + if secret == nil { + return fmt.Errorf("secret is nil") + } + + missingKeys := make([]string, 0, len(requiredDatabaseSecretKeys)) + for _, key := range requiredDatabaseSecretKeys { + value, ok := secret.Data[key] + if !ok || len(value) == 0 { + missingKeys = append(missingKeys, key) + } + } + + if len(missingKeys) > 0 { + return fmt.Errorf("missing required keys: %s", strings.Join(missingKeys, ", ")) + } + + if expectedHost != "" && string(secret.Data["DB_HOST"]) != expectedHost { + return fmt.Errorf("DB_HOST mismatch: got %q, expected %q", string(secret.Data["DB_HOST"]), expectedHost) + } + + return nil +} + // GenerateDatabaseService creates a headless Service for a database StatefulSet. // The headless Service (clusterIP: None) provides stable DNS resolution // so resolves to the database pod. diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 9d2a29d..14115d0 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -137,7 +137,7 @@ func TestGenerateCredentialsUniqueness(t *testing.T) { credentials := make(map[string]bool) iterations := 100 - for i := range iterations { + for i := 0; i < iterations; i++ { creds, err := GenerateCredentials() if err != nil { t.Fatalf("GenerateCredentials failed on iteration %d: %v", i, err) @@ -399,7 +399,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { Data: map[string][]byte{ "DB_USER": []byte("existing-user"), "DB_PASS": []byte("existing-pass"), - "DB_HOST": []byte("existing-host"), + "DB_HOST": []byte("api-server-db"), }, } @@ -665,7 +665,10 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { t.Error("LivenessProbe should be set on Postgres container") } else { cmdStr := strings.Join(container.LivenessProbe.Exec.Command, " ") - if !strings.Contains(cmdStr, "-p $(PGPORT)") { + if len(container.LivenessProbe.Exec.Command) != 3 || container.LivenessProbe.Exec.Command[0] != "sh" || container.LivenessProbe.Exec.Command[1] != "-c" { + t.Errorf("LivenessProbe command should use shell expansion, got: %v", container.LivenessProbe.Exec.Command) + } + if !strings.Contains(cmdStr, `-p "$PGPORT"`) { t.Errorf("LivenessProbe command missing custom port flag. Got: %s", cmdStr) } } @@ -675,7 +678,10 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { t.Error("ReadinessProbe should be set on Postgres container") } else { cmdStr := strings.Join(container.ReadinessProbe.Exec.Command, " ") - if !strings.Contains(cmdStr, "-p $(PGPORT)") { + if len(container.ReadinessProbe.Exec.Command) != 3 || container.ReadinessProbe.Exec.Command[0] != "sh" || container.ReadinessProbe.Exec.Command[1] != "-c" { + t.Errorf("ReadinessProbe command should use shell expansion, got: %v", container.ReadinessProbe.Exec.Command) + } + if !strings.Contains(cmdStr, `-p "$PGPORT"`) { t.Errorf("ReadinessProbe command missing custom port flag. Got: %s", cmdStr) } } @@ -1114,6 +1120,118 @@ func TestInjectDatabaseEnvVars(t *testing.T) { t.Error("Expected no changes for Deployment with no containers") } }) + + t.Run("TargetsNamedContainerWhenPresent", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "istio-proxy", Image: "proxy:v1"}, + {Name: "api-server", Image: "myregistry/api:v1"}, + }, + }, + }, + }, + } + + changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "api-server-db-secret", "api-server") + if !changed { + t.Fatal("Expected env injection changes") + } + if !exactMatch { + t.Fatal("Expected exact container match") + } + + proxyEnvCount := len(deploy.Spec.Template.Spec.Containers[0].Env) + if proxyEnvCount != 0 { + t.Fatalf("Expected sidecar env to stay unchanged, got %d", proxyEnvCount) + } + + appContainer := deploy.Spec.Template.Spec.Containers[1] + expected := map[string]bool{"DB_HOST": false, "DB_USER": false, "DB_PASS": false} + for _, env := range appContainer.Env { + if _, ok := expected[env.Name]; ok { + expected[env.Name] = true + } + } + for name, found := range expected { + if !found { + t.Errorf("Expected env %s on target container", name) + } + } + }) + + t.Run("FallsBackToFirstContainerWhenPreferredMissing", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "only-container", Image: "myregistry/app:v1"}, + }, + }, + }, + }, + } + + changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "app-db-secret", "missing-app") + if !changed { + t.Fatal("Expected env injection changes") + } + if exactMatch { + t.Fatal("Expected fallback because preferred container does not exist") + } + if len(deploy.Spec.Template.Spec.Containers[0].Env) != 3 { + t.Fatalf("Expected 3 injected DB env vars, got %d", len(deploy.Spec.Template.Spec.Containers[0].Env)) + } + }) +} + +func TestValidateDatabaseSecret(t *testing.T) { + t.Run("ValidSecret", func(t *testing.T) { + secret := &corev1.Secret{ + Data: map[string][]byte{ + "DB_USER": []byte("user"), + "DB_PASS": []byte("pass"), + "DB_HOST": []byte("api-server-db"), + }, + } + + if err := ValidateDatabaseSecret(secret, "api-server-db"); err != nil { + t.Fatalf("Expected secret to be valid, got %v", err) + } + }) + + t.Run("MissingRequiredKeys", func(t *testing.T) { + secret := &corev1.Secret{Data: map[string][]byte{"DB_USER": []byte("user")}} + + err := ValidateDatabaseSecret(secret, "api-server-db") + if err == nil { + t.Fatal("Expected validation error for missing keys") + } + if !strings.Contains(err.Error(), "missing required keys") { + t.Fatalf("Expected missing keys error, got %v", err) + } + }) + + t.Run("MismatchedHost", func(t *testing.T) { + secret := &corev1.Secret{ + Data: map[string][]byte{ + "DB_USER": []byte("user"), + "DB_PASS": []byte("pass"), + "DB_HOST": []byte("wrong-host"), + }, + } + + err := ValidateDatabaseSecret(secret, "api-server-db") + if err == nil { + t.Fatal("Expected validation error for host mismatch") + } + if !strings.Contains(err.Error(), "DB_HOST mismatch") { + t.Fatalf("Expected host mismatch error, got %v", err) + } + }) } func TestReconcileDatabaseSecretInjection(t *testing.T) { diff --git a/apps/portal/backstage.json b/apps/portal/backstage.json index c0ce7c1..5691b96 100644 --- a/apps/portal/backstage.json +++ b/apps/portal/backstage.json @@ -1,3 +1,3 @@ { - "version": "1.46.0" + "version": "1.49.1" } diff --git a/apps/portal/examples/advanced-template/content/gitops/helios-app.yaml b/apps/portal/examples/advanced-template/content/gitops/helios-app.yaml index 35f1c7e..2b9999d 100644 --- a/apps/portal/examples/advanced-template/content/gitops/helios-app.yaml +++ b/apps/portal/examples/advanced-template/content/gitops/helios-app.yaml @@ -37,3 +37,9 @@ spec: properties: port: ${{ values.port }} targetPort: ${{ values.port }} + - type: database + properties: + dbType: ${{ values.databaseType | default("none") }} + dbName: ${{ values.databaseName }} + version: "16" + storage: "1Gi" diff --git a/apps/portal/examples/advanced-template/template.yaml b/apps/portal/examples/advanced-template/template.yaml index 6d0e8f0..a58945d 100644 --- a/apps/portal/examples/advanced-template/template.yaml +++ b/apps/portal/examples/advanced-template/template.yaml @@ -128,6 +128,8 @@ spec: dockerOrg: ${{ parameters.dockerOrg }} repoName: ${{ parameters.repoName }} port: ${{ parameters.port }} + databaseType: ${{ parameters.databaseConfig.dbType }} + databaseName: ${{ parameters.databaseConfig.dbName }} sourceRepo: ${{ steps['publish-source'].output.remoteUrl }} gitopsRepo: ${{ steps['publish-source'].output.remoteUrl | replace(".git", "") }}-gitops webhookUrl: ${{ parameters.webhookUrl }} diff --git a/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml b/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml index 30b84ae..c8a3b0c 100644 --- a/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml +++ b/apps/portal/examples/nestjs-prisma-template/content/gitops/helios-app.yaml @@ -29,7 +29,7 @@ spec: port: ${{ values.port }} - type: database properties: - dbType: postgres - dbName: ${{ values.name | replace('-', '_') }}_db + dbType: ${{ values.databaseType | default("postgres") }} + dbName: ${{ values.databaseName }} version: "16" storage: "1Gi" diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts index d932300..0a2f14e 100644 --- a/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts @@ -13,8 +13,10 @@ import * as pg from 'pg'; * - DB_HOST: database hostname (from K8s Secret) * - DB_USER: database username (from K8s Secret) * - DB_PASS: database password (from K8s Secret) - * - DB_NAME: database name (from K8s ConfigMap, or defaults to component-db) - * - DB_PORT: database port (from K8s ConfigMap, or defaults to 5432) + * + * Optional vars below are application-level overrides and may be unset: + * - DB_NAME: database name (defaults to postgres) + * - DB_PORT: database port (defaults to 5432) */ @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { diff --git a/apps/portal/examples/nestjs-prisma-template/template.yaml b/apps/portal/examples/nestjs-prisma-template/template.yaml index 713447a..24a3ba8 100644 --- a/apps/portal/examples/nestjs-prisma-template/template.yaml +++ b/apps/portal/examples/nestjs-prisma-template/template.yaml @@ -127,6 +127,8 @@ spec: dockerOrg: ${{ parameters.dockerOrg }} repoName: ${{ parameters.repoName }} port: ${{ parameters.port }} + databaseType: ${{ parameters.databaseConfig.dbType }} + databaseName: ${{ parameters.databaseConfig.dbName }} owner: ${{ parameters.owner }} sourceRepo: ${{ steps['publish-source'].output.remoteUrl }} gitopsRepo: ${{ steps['publish-source'].output.remoteUrl | replace(".git", "") }}-gitops diff --git a/apps/portal/package.json b/apps/portal/package.json index cfb08ef..2c2a265 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -29,8 +29,9 @@ ] }, "devDependencies": { - "@backstage/cli": "^0.35.0", - "@backstage/e2e-test-utils": "^0.1.1", + "@backstage/cli": "^0.36.0", + "@backstage/cli-defaults": "^0.1.0", + "@backstage/e2e-test-utils": "^0.1.2", "@jest/environment-jsdom-abstract": "^30.0.0", "@playwright/test": "^1.32.3", "@types/jest": "^30.0.0", @@ -38,7 +39,8 @@ "jsdom": "^27.1.0", "node-gyp": "^10.0.0", "prettier": "^2.3.2", - "typescript": "~5.8.0" + "typescript": "~5.8.0", + "webpack": "~5.103.0" }, "resolutions": { "@types/react": "^18", diff --git a/apps/portal/packages/app/package.json b/apps/portal/packages/app/package.json index a3f4ba5..5e6da8a 100644 --- a/apps/portal/packages/app/package.json +++ b/apps/portal/packages/app/package.json @@ -15,50 +15,55 @@ }, "dependencies": { "@backstage-community/plugin-tekton": "^3.34.1", - "@backstage/app-defaults": "^1.7.3", - "@backstage/catalog-model": "^1.7.6", - "@backstage/cli": "^0.35.0", - "@backstage/core-app-api": "^1.19.3", - "@backstage/core-components": "^0.18.4", - "@backstage/core-plugin-api": "^1.12.1", - "@backstage/integration-react": "^1.2.13", - "@backstage/plugin-api-docs": "^0.13.2", - "@backstage/plugin-catalog": "^1.32.1", - "@backstage/plugin-catalog-common": "^1.1.7", - "@backstage/plugin-catalog-graph": "^0.5.4", - "@backstage/plugin-catalog-import": "^0.13.8", - "@backstage/plugin-catalog-react": "^1.21.4", - "@backstage/plugin-kubernetes": "^0.12.14", - "@backstage/plugin-notifications": "^0.5.12", - "@backstage/plugin-org": "^0.6.47", - "@backstage/plugin-permission-react": "^0.4.39", - "@backstage/plugin-scaffolder": "^1.35.0", - "@backstage/plugin-search": "^1.5.1", - "@backstage/plugin-search-react": "^1.10.1", - "@backstage/plugin-signals": "^0.0.26", - "@backstage/plugin-techdocs": "^1.16.1", - "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.31", - "@backstage/plugin-techdocs-react": "^1.3.6", - "@backstage/plugin-user-settings": "^0.8.30", - "@backstage/theme": "^0.7.1", - "@backstage/ui": "^0.10.0", + "@backstage/app-defaults": "^1.7.6", + "@backstage/catalog-model": "^1.7.7", + "@backstage/cli": "^0.36.0", + "@backstage/core-app-api": "^1.19.6", + "@backstage/core-components": "^0.18.8", + "@backstage/core-plugin-api": "^1.12.4", + "@backstage/integration-react": "^1.2.16", + "@backstage/plugin-api-docs": "^0.13.5", + "@backstage/plugin-catalog": "^2.0.1", + "@backstage/plugin-catalog-common": "^1.1.8", + "@backstage/plugin-catalog-graph": "^0.6.0", + "@backstage/plugin-catalog-import": "^0.13.11", + "@backstage/plugin-catalog-react": "^2.1.0", + "@backstage/plugin-kubernetes": "^0.12.17", + "@backstage/plugin-notifications": "^0.5.15", + "@backstage/plugin-org": "^0.7.0", + "@backstage/plugin-permission-react": "^0.4.41", + "@backstage/plugin-scaffolder": "^1.36.1", + "@backstage/plugin-scaffolder-react": "^1.20.0", + "@backstage/plugin-search": "^1.7.0", + "@backstage/plugin-search-react": "^1.11.0", + "@backstage/plugin-signals": "^0.0.29", + "@backstage/plugin-techdocs": "^1.17.2", + "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.34", + "@backstage/plugin-techdocs-react": "^1.3.9", + "@backstage/plugin-user-settings": "^0.9.1", + "@backstage/theme": "^0.7.2", + "@backstage/ui": "^0.13.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@roadiehq/backstage-plugin-argo-cd": "^2.12.2", "react": "^18.0.2", "react-dom": "^18.0.2", "react-router": "^6.3.0", - "react-router-dom": "^6.3.0" + "react-router-dom": "^6.3.0", + "react-use": "^17.6.0" }, "devDependencies": { - "@backstage/test-utils": "^1.7.14", + "@backstage/test-utils": "^1.7.16", "@playwright/test": "^1.32.3", - "@testing-library/dom": "^9.0.0", + "@testing-library/dom": "^10.0.0", "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^14.0.0", + "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.0.0", + "@types/react": "*", "@types/react-dom": "*", - "cross-env": "^7.0.0" + "cross-env": "^7.0.0", + "jest": "^30.2.0", + "webpack": "~5.103.0" }, "browserslist": { "production": [ diff --git a/apps/portal/packages/app/src/App.tsx b/apps/portal/packages/app/src/App.tsx index f66801d..e57a4d7 100644 --- a/apps/portal/packages/app/src/App.tsx +++ b/apps/portal/packages/app/src/App.tsx @@ -76,8 +76,6 @@ const app = createApp({ }, }); -const scaffolderFieldExtensions = [DatabasePickerExtension]; - const routes = ( } /> @@ -97,13 +95,16 @@ const routes = ( - - - - - - } /> + + + + + + } + /> } /> {entityWarningContent} - + - + - + ); @@ -197,10 +197,10 @@ const serviceEntityPage = ( - + - + @@ -230,10 +230,10 @@ const websiteEntityPage = ( - + - + @@ -286,7 +286,7 @@ const apiPage = ( - + @@ -318,10 +318,10 @@ const userPage = ( {entityWarningContent} - + - + @@ -334,10 +334,10 @@ const groupPage = ( {entityWarningContent} - + - + @@ -356,28 +356,27 @@ const systemPage = ( {entityWarningContent} - + - + - + - + - + {entityWarningContent} - + - + - + diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx index ce6da02..138b44b 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx @@ -1,6 +1,12 @@ -import React from 'react'; +import type { ChangeEvent } from 'react'; import { FieldExtensionComponentProps } from '@backstage/plugin-scaffolder-react'; -import { FormControl, InputLabel, Select, MenuItem, TextField } from '@material-ui/core'; +import { + FormControl, + InputLabel, + Select, + MenuItem, + TextField, +} from '@material-ui/core'; // Define the exact state structure expected by PhuocHoan's CUE schema interface DatabaseConfig { @@ -18,7 +24,7 @@ export const DatabasePicker = ({ const dbType = formData?.dbType || 'none'; const dbName = formData?.dbName || ''; - const handleTypeChange = (event: React.ChangeEvent<{ value: unknown }>) => { + const handleTypeChange = (event: ChangeEvent<{ value: unknown }>) => { const newType = event.target.value as string; onChange({ dbType: newType, @@ -27,7 +33,7 @@ export const DatabasePicker = ({ }); }; - const handleNameChange = (event: React.ChangeEvent) => { + const handleNameChange = (event: ChangeEvent) => { onChange({ dbType, dbName: event.target.value, @@ -58,4 +64,4 @@ export const DatabasePicker = ({ )} ); -}; \ No newline at end of file +}; diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts index 2cd7b6c..fda085e 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -10,9 +10,11 @@ export const DatabasePickerExtension: any = scaffolderPlugin.provide( // Custom validation: If postgres is selected, dbName MUST be provided if (value?.dbType === 'postgres') { if (!value?.dbName) { - validation.addError('Database Name is required when PostgreSQL is selected'); + validation.addError( + 'Database Name is required when PostgreSQL is selected', + ); } } }, }), -); \ No newline at end of file +); diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts index 6634724..6e0f747 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/index.ts @@ -1,2 +1,2 @@ export * from './DatabasePicker'; -export * from './extension'; \ No newline at end of file +export * from './extension'; diff --git a/apps/portal/packages/app/src/scaffolder/index.ts b/apps/portal/packages/app/src/scaffolder/index.ts index 951310d..31d1850 100644 --- a/apps/portal/packages/app/src/scaffolder/index.ts +++ b/apps/portal/packages/app/src/scaffolder/index.ts @@ -1 +1 @@ -export { DatabasePickerExtension } from './DatabasePickerExtension/extension'; \ No newline at end of file +export { DatabasePickerExtension } from './DatabasePickerExtension/extension'; diff --git a/apps/portal/packages/backend/package.json b/apps/portal/packages/backend/package.json index 874b0fe..38ffe78 100644 --- a/apps/portal/packages/backend/package.json +++ b/apps/portal/packages/backend/package.json @@ -16,37 +16,39 @@ "build-image": "docker build ../.. -f Dockerfile --tag backstage" }, "dependencies": { - "@backstage/backend-defaults": "^0.14.0", - "@backstage/backend-plugin-api": "^1.6.1", - "@backstage/catalog-model": "^1.7.6", + "@backstage/backend-defaults": "^0.16.0", + "@backstage/backend-plugin-api": "^1.8.0", + "@backstage/catalog-model": "^1.7.7", "@backstage/config": "^1.3.6", - "@backstage/plugin-app-backend": "^0.5.9", - "@backstage/plugin-auth-backend": "^0.25.7", - "@backstage/plugin-auth-backend-module-github-provider": "^0.4.0", - "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.15", - "@backstage/plugin-auth-node": "^0.6.10", - "@backstage/plugin-catalog-backend": "^3.3.0", - "@backstage/plugin-catalog-backend-module-github": "^0.12.1", - "@backstage/plugin-catalog-backend-module-github-org": "^0.3.18", - "@backstage/plugin-catalog-backend-module-logs": "^0.1.17", - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.15", - "@backstage/plugin-kubernetes-backend": "^0.21.0", - "@backstage/plugin-notifications-backend": "^0.6.1", - "@backstage/plugin-permission-backend": "^0.7.7", - "@backstage/plugin-permission-backend-module-allow-all-policy": "^0.2.15", - "@backstage/plugin-permission-common": "^0.9.3", - "@backstage/plugin-permission-node": "^0.10.7", - "@backstage/plugin-proxy-backend": "^0.6.9", - "@backstage/plugin-scaffolder-backend": "^3.1.0", - "@backstage/plugin-scaffolder-backend-module-github": "^0.9.3", - "@backstage/plugin-scaffolder-backend-module-notifications": "^0.1.17", - "@backstage/plugin-search-backend": "^2.0.9", - "@backstage/plugin-search-backend-module-catalog": "^0.3.11", - "@backstage/plugin-search-backend-module-pg": "^0.5.51", - "@backstage/plugin-search-backend-module-techdocs": "^0.4.9", - "@backstage/plugin-search-backend-node": "^1.4.0", - "@backstage/plugin-signals-backend": "^0.3.11", - "@backstage/plugin-techdocs-backend": "^2.1.3", + "@backstage/errors": "^1.2.7", + "@backstage/plugin-app-backend": "^0.5.12", + "@backstage/plugin-auth-backend": "^0.27.2", + "@backstage/plugin-auth-backend-module-github-provider": "^0.5.1", + "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.17", + "@backstage/plugin-auth-node": "^0.6.14", + "@backstage/plugin-catalog-backend": "^3.5.0", + "@backstage/plugin-catalog-backend-module-github": "^0.13.0", + "@backstage/plugin-catalog-backend-module-github-org": "^0.3.20", + "@backstage/plugin-catalog-backend-module-logs": "^0.1.20", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.18", + "@backstage/plugin-kubernetes-backend": "^0.21.2", + "@backstage/plugin-notifications-backend": "^0.6.3", + "@backstage/plugin-permission-backend": "^0.7.10", + "@backstage/plugin-permission-backend-module-allow-all-policy": "^0.2.17", + "@backstage/plugin-permission-common": "^0.9.7", + "@backstage/plugin-permission-node": "^0.10.11", + "@backstage/plugin-proxy-backend": "^0.6.11", + "@backstage/plugin-scaffolder-backend": "^3.2.0", + "@backstage/plugin-scaffolder-backend-module-github": "^0.9.7", + "@backstage/plugin-scaffolder-backend-module-notifications": "^0.1.20", + "@backstage/plugin-scaffolder-node": "^0.13.0", + "@backstage/plugin-search-backend": "^2.1.0", + "@backstage/plugin-search-backend-module-catalog": "^0.3.13", + "@backstage/plugin-search-backend-module-pg": "^0.5.53", + "@backstage/plugin-search-backend-module-techdocs": "^0.4.12", + "@backstage/plugin-search-backend-node": "^1.4.2", + "@backstage/plugin-signals-backend": "^0.3.13", + "@backstage/plugin-techdocs-backend": "^2.1.6", "app": "link:../app", "better-sqlite3": "^12.0.0", "dotenv": "^17.2.3", @@ -54,7 +56,9 @@ "pg": "^8.11.3" }, "devDependencies": { - "@backstage/cli": "^0.35.0" + "@backstage/cli": "^0.36.0", + "jest": "^30.2.0", + "webpack": "~5.103.0" }, "files": [ "dist" diff --git a/apps/portal/packages/backend/src/index.ts b/apps/portal/packages/backend/src/index.ts index c782691..40da57a 100644 --- a/apps/portal/packages/backend/src/index.ts +++ b/apps/portal/packages/backend/src/index.ts @@ -6,12 +6,13 @@ * Happy hacking! */ -import { resolve } from 'path'; import * as dotenv from 'dotenv'; import { createBackend } from '@backstage/backend-defaults'; +import { customAuth } from './extensions/auth'; +import { scaffolderModuleCustomActions } from './extensions/scaffolder'; // Load env vars from root .env -dotenv.config({ path: resolve(__dirname, '../../../.env'), debug: true }); +dotenv.config({ debug: true }); const backend = createBackend(); @@ -21,9 +22,7 @@ backend.add(import('@backstage/plugin-proxy-backend')); // scaffolder plugin backend.add(import('@backstage/plugin-scaffolder-backend')); backend.add(import('@backstage/plugin-scaffolder-backend-module-github')); -import { scaffolderModuleCustomActions } from './extensions/scaffolder'; backend.add(scaffolderModuleCustomActions); -import { customAuth } from './extensions/auth'; backend.add( import('@backstage/plugin-scaffolder-backend-module-notifications'), diff --git a/apps/portal/start-dev.sh b/apps/portal/start-dev.sh index 8ab80be..33bff03 100755 --- a/apps/portal/start-dev.sh +++ b/apps/portal/start-dev.sh @@ -1,6 +1,70 @@ #!/bin/bash set -e +trim_ws() { + local s="$1" + s="${s#"${s%%[![:space:]]*}"}" + s="${s%"${s##*[![:space:]]}"}" + printf '%s' "$s" +} + +read_env_value() { + local env_file="$1" + local wanted_key="$2" + local line key value + + while IFS= read -r line || [[ -n "$line" ]]; do + line="$(trim_ws "$line")" + [[ -z "$line" || "${line:0:1}" == "#" ]] && continue + + if [[ "$line" == export\ * ]]; then + line="${line#export }" + line="$(trim_ws "$line")" + fi + + [[ "$line" != *=* ]] && continue + key="$(trim_ws "${line%%=*}")" + value="$(trim_ws "${line#*=}")" + + if [[ "$key" != "$wanted_key" ]]; then + continue + fi + + if [[ "$value" == \"*\" && "$value" == *\" ]]; then + value="${value:1:${#value}-2}" + elif [[ "$value" == \'*\' && "$value" == *\' ]]; then + value="${value:1:${#value}-2}" + fi + + printf '%s' "$value" + return 0 + done < "$env_file" + + return 1 +} + +decode_base64() { + if printf 'Zg==' | base64 --decode >/dev/null 2>&1; then + base64 --decode + return + fi + if printf 'Zg==' | base64 -d >/dev/null 2>&1; then + base64 -d + return + fi + if printf 'Zg==' | base64 -D >/dev/null 2>&1; then + base64 -D + return + fi + if command -v openssl >/dev/null 2>&1; then + openssl base64 -d -A + return + fi + + echo "No compatible base64 decoder found" >&2 + return 1 +} + # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -9,7 +73,7 @@ NC='\033[0m' # No Color # 1. ArgoCD Token Automation echo -e "${YELLOW}🔑 Fetching ArgoCD Admin Password...${NC}" -ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) +ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | decode_base64) echo -e "${YELLOW}🔄 Generating ArgoCD Token...${NC}" # Use temporary port-forward to get token if not running @@ -58,9 +122,13 @@ trap cleanup EXIT INT TERM echo -e "${GREEN}🌟 Starting Backstage Portal...${NC}" # Load existing .env if present (but override TOKEN) if [ -f .env ]; then - set -a - source .env - set +a + SAFE_ENV_KEYS=(AUTH_GITHUB_CLIENT_ID AUTH_GITHUB_CLIENT_SECRET GITHUB_ORG GITHUB_TOKEN) + for key in "${SAFE_ENV_KEYS[@]}"; do + value="$(read_env_value .env "$key" || true)" + if [ -n "$value" ]; then + export "$key=$value" + fi + done fi export ARGOCD_AUTH_TOKEN # Re-export to ensure override diff --git a/apps/portal/yarn.lock b/apps/portal/yarn.lock index 5047e1d..9de9c86 100644 --- a/apps/portal/yarn.lock +++ b/apps/portal/yarn.lock @@ -1435,7 +1435,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0, @babel/code-frame@npm:^7.8.3": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0, @babel/code-frame@npm:^7.8.3": version: 7.29.0 resolution: "@babel/code-frame@npm:7.29.0" dependencies: @@ -1893,148 +1893,64 @@ __metadata: languageName: node linkType: hard -"@backstage/app-defaults@npm:^1.7.3": - version: 1.7.4 - resolution: "@backstage/app-defaults@npm:1.7.4" +"@backstage/app-defaults@npm:^1.7.6": + version: 1.7.6 + resolution: "@backstage/app-defaults@npm:1.7.6" dependencies: - "@backstage/core-app-api": "npm:^1.19.3" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@backstage/theme": "npm:^0.7.1" + "@backstage/core-app-api": "npm:^1.19.6" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/theme": "npm:^0.7.2" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/6ce117ec663292b9901e9958364d52cee133126c7b9d46cde8b6aefe73859c62dd38203b06fc4299c58094928556b5c980a1094274c2fb05ea6cdd19e84e9552 + checksum: 10c0/4b6411424e331e955a5ca61fb615b7628812ff14cca6bbc8896fd8b502cf0bf925ef302c57ee67a06d743f2dc6e44b6b31b4b6856074ef70f2892409de13f0ff languageName: node linkType: hard -"@backstage/backend-app-api@npm:^1.4.0, @backstage/backend-app-api@npm:^1.4.1": - version: 1.4.1 - resolution: "@backstage/backend-app-api@npm:1.4.1" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - checksum: 10c0/73607f6984486c23aaa2758c5801e96bb3288a880afe2293b2fd0e16fdc73ae4d319012c48da4c806fdffbf34e6c9a2add4f21ac3a3e42b21f4872f8b177465d - languageName: node - linkType: hard - -"@backstage/backend-defaults@npm:^0.14.0": - version: 0.14.1 - resolution: "@backstage/backend-defaults@npm:0.14.1" +"@backstage/backend-app-api@npm:^1.6.0": + version: 1.6.0 + resolution: "@backstage/backend-app-api@npm:1.6.0" dependencies: - "@aws-sdk/abort-controller": "npm:^3.347.0" - "@aws-sdk/client-codecommit": "npm:^3.350.0" - "@aws-sdk/client-s3": "npm:^3.350.0" - "@aws-sdk/credential-providers": "npm:^3.350.0" - "@aws-sdk/types": "npm:^3.347.0" - "@azure/storage-blob": "npm:^12.5.0" - "@backstage/backend-app-api": "npm:^1.4.0" - "@backstage/backend-dev-utils": "npm:^0.1.6" - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/cli-node": "npm:^0.2.16" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" - "@backstage/config-loader": "npm:^1.10.7" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.1" - "@backstage/integration-aws-node": "npm:^0.1.19" - "@backstage/plugin-auth-node": "npm:^0.6.10" - "@backstage/plugin-events-node": "npm:^0.4.18" - "@backstage/plugin-permission-node": "npm:^0.10.7" - "@backstage/types": "npm:^1.2.2" - "@google-cloud/storage": "npm:^7.0.0" - "@keyv/memcache": "npm:^2.0.1" - "@keyv/redis": "npm:^4.0.1" - "@keyv/valkey": "npm:^1.0.1" - "@manypkg/get-packages": "npm:^1.1.3" - "@octokit/rest": "npm:^19.0.3" - "@opentelemetry/api": "npm:^1.9.0" - "@types/cors": "npm:^2.8.6" - "@types/express": "npm:^4.17.6" - archiver: "npm:^7.0.0" - base64-stream: "npm:^1.0.0" - compression: "npm:^1.7.4" - concat-stream: "npm:^2.0.0" - cookie: "npm:^0.7.0" - cors: "npm:^2.8.5" - cron: "npm:^3.0.0" - express: "npm:^4.22.0" - express-promise-router: "npm:^4.1.0" - express-rate-limit: "npm:^7.5.0" - fs-extra: "npm:^11.2.0" - git-url-parse: "npm:^15.0.0" - helmet: "npm:^6.0.0" - infinispan: "npm:^0.12.0" - is-glob: "npm:^4.0.3" - jose: "npm:^5.0.0" - keyv: "npm:^5.2.1" - knex: "npm:^3.0.0" - lodash: "npm:^4.17.21" - logform: "npm:^2.3.2" - luxon: "npm:^3.0.0" - minimatch: "npm:^9.0.0" - mysql2: "npm:^3.0.0" - node-fetch: "npm:^2.7.0" - node-forge: "npm:^1.3.2" - p-limit: "npm:^3.1.0" - path-to-regexp: "npm:^8.0.0" - pg: "npm:^8.11.3" - pg-connection-string: "npm:^2.3.0" - pg-format: "npm:^1.0.4" - rate-limit-redis: "npm:^4.2.0" - raw-body: "npm:^2.4.1" - selfsigned: "npm:^2.0.0" - tar: "npm:^6.1.12" - triple-beam: "npm:^1.4.1" - uuid: "npm:^11.0.0" - winston: "npm:^3.2.1" - winston-transport: "npm:^4.5.0" - yauzl: "npm:^3.0.0" - yn: "npm:^4.0.0" - zod: "npm:^3.22.4" - zod-to-json-schema: "npm:^3.20.4" - peerDependencies: - "@google-cloud/cloud-sql-connector": ^1.4.0 - better-sqlite3: ^12.0.0 - peerDependenciesMeta: - "@google-cloud/cloud-sql-connector": - optional: true - better-sqlite3: - optional: true - checksum: 10c0/bfe9c9218508aa9447a5880cb1cfac4735bf912aab48ec8134b4cf7bfd3f04b7b9022871554a7f7f187068f51fb9e7113e9b5df849826ba41c3077004e058a07 + checksum: 10c0/d1c8690513b656ee2f7cdb626efcd14e122c473be8e86f8a7771682d71f0bac8330b229139644ac39a124a51b5cca29ad313f26a54c0f1bfefbcf89c7aaa495a languageName: node linkType: hard -"@backstage/backend-defaults@npm:^0.15.0, @backstage/backend-defaults@npm:^0.15.1": - version: 0.15.1 - resolution: "@backstage/backend-defaults@npm:0.15.1" +"@backstage/backend-defaults@npm:^0.16.0": + version: 0.16.0 + resolution: "@backstage/backend-defaults@npm:0.16.0" dependencies: "@aws-sdk/abort-controller": "npm:^3.347.0" "@aws-sdk/client-codecommit": "npm:^3.350.0" "@aws-sdk/client-s3": "npm:^3.350.0" "@aws-sdk/credential-providers": "npm:^3.350.0" "@aws-sdk/types": "npm:^3.347.0" + "@azure/identity": "npm:^4.0.0" "@azure/storage-blob": "npm:^12.5.0" - "@backstage/backend-app-api": "npm:^1.4.1" - "@backstage/backend-dev-utils": "npm:^0.1.6" - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/cli-node": "npm:^0.2.17" + "@backstage/backend-app-api": "npm:^1.6.0" + "@backstage/backend-dev-utils": "npm:^0.1.7" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/cli-node": "npm:^0.3.0" "@backstage/config": "npm:^1.3.6" - "@backstage/config-loader": "npm:^1.10.7" + "@backstage/config-loader": "npm:^1.10.9" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/integration-aws-node": "npm:^0.1.19" - "@backstage/plugin-auth-node": "npm:^0.6.12" - "@backstage/plugin-events-node": "npm:^0.4.18" - "@backstage/plugin-permission-node": "npm:^0.10.9" + "@backstage/integration": "npm:^2.0.0" + "@backstage/integration-aws-node": "npm:^0.1.20" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-events-node": "npm:^0.4.20" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" "@backstage/types": "npm:^1.2.2" "@google-cloud/storage": "npm:^7.0.0" "@keyv/memcache": "npm:^2.0.1" @@ -2054,7 +1970,7 @@ __metadata: cron: "npm:^3.0.0" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" - express-rate-limit: "npm:^7.5.0" + express-rate-limit: "npm:^8.2.2" fs-extra: "npm:^11.2.0" git-url-parse: "npm:^15.0.0" helmet: "npm:^6.0.0" @@ -2066,7 +1982,7 @@ __metadata: lodash: "npm:^4.17.21" logform: "npm:^2.3.2" luxon: "npm:^3.0.0" - minimatch: "npm:^9.0.0" + minimatch: "npm:^10.2.1" mysql2: "npm:^3.0.0" node-fetch: "npm:^2.7.0" node-forge: "npm:^1.3.2" @@ -2078,14 +1994,14 @@ __metadata: rate-limit-redis: "npm:^4.2.0" raw-body: "npm:^2.4.1" selfsigned: "npm:^2.0.0" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" triple-beam: "npm:^1.4.1" uuid: "npm:^11.0.0" winston: "npm:^3.2.1" winston-transport: "npm:^4.5.0" - yauzl: "npm:^3.0.0" + yauzl: "npm:^3.2.1" yn: "npm:^4.0.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" peerDependencies: "@google-cloud/cloud-sql-connector": ^1.4.0 @@ -2095,23 +2011,23 @@ __metadata: optional: true better-sqlite3: optional: true - checksum: 10c0/dfa19715d873ea9751d5330ec5a54172d054338c5351f73e85aa5ed96b05692ce63cd7d776d98b9c8cbc80b9ad478a8c39442ac248a0b6d2f81b9a3bc56914d8 + checksum: 10c0/2baa5887860fb0e7ae5f8ff9d450f737011e8673373ac384dbdf550d61511ac404d5d41264e01cc256f3bc2be3d478ba8b93413c0f9b0f6184adaf5d0c9bafb8 languageName: node linkType: hard -"@backstage/backend-dev-utils@npm:^0.1.6": - version: 0.1.6 - resolution: "@backstage/backend-dev-utils@npm:0.1.6" - checksum: 10c0/5b473d550b9d38d2cd6c08255a59739a6d8ecfb1df80a287d2fc1575b3f8b83359426f6fb45760682e0ea4bf3e19ebae0560df23e61da706142857011a3216d6 +"@backstage/backend-dev-utils@npm:^0.1.7": + version: 0.1.7 + resolution: "@backstage/backend-dev-utils@npm:0.1.7" + checksum: 10c0/3a0f54a6303bf4815e8d2e1a7536d9f7ee8028a04dd796ca233261f3170f3c4343468841590a5b0faf60b0864eae028a6086bff4a674a8345e0de100b5550da2 languageName: node linkType: hard -"@backstage/backend-openapi-utils@npm:^0.6.5": - version: 0.6.5 - resolution: "@backstage/backend-openapi-utils@npm:0.6.5" +"@backstage/backend-openapi-utils@npm:^0.6.7": + version: 0.6.7 + resolution: "@backstage/backend-openapi-utils@npm:0.6.7" dependencies: "@apidevtools/swagger-parser": "npm:^10.1.0" - "@backstage/backend-plugin-api": "npm:^1.6.1" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/errors": "npm:^1.2.7" "@backstage/types": "npm:^1.2.2" "@types/express": "npm:^4.17.6" @@ -2126,20 +2042,20 @@ __metadata: mockttp: "npm:^3.13.0" openapi-merge: "npm:^1.3.2" openapi3-ts: "npm:^3.1.2" - checksum: 10c0/26f623942a62472d7debf66c52ddd735b3936628433afa7bf52105531750a4c1067004d88def52089a285dec8b42e97cb04ffcf7e2f214e3bbbea2d7fb45b64b + checksum: 10c0/76def9609c614361ee46688df8b0823eab7e662999bc0891915a280d1d7559b75de5fed2de1832d51106f8d87e82f07ebc21a827fbaff6573f6cb7618eb110a6 languageName: node linkType: hard -"@backstage/backend-plugin-api@npm:^1.6.0, @backstage/backend-plugin-api@npm:^1.6.1, @backstage/backend-plugin-api@npm:^1.6.2": - version: 1.6.2 - resolution: "@backstage/backend-plugin-api@npm:1.6.2" +"@backstage/backend-plugin-api@npm:^1.8.0": + version: 1.8.0 + resolution: "@backstage/backend-plugin-api@npm:1.8.0" dependencies: - "@backstage/cli-common": "npm:^0.1.17" + "@backstage/cli-common": "npm:^0.2.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.12" - "@backstage/plugin-permission-common": "npm:^0.9.5" - "@backstage/plugin-permission-node": "npm:^0.10.9" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" "@backstage/types": "npm:^1.2.2" "@types/express": "npm:^4.17.6" "@types/json-schema": "npm:^7.0.6" @@ -2147,8 +2063,8 @@ __metadata: json-schema: "npm:^0.4.0" knex: "npm:^3.0.0" luxon: "npm:^3.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/45bb716fa8f5bcbbba2414c8f405039a6682973e695647e374dc0c7c7a4491bdc7522a2eacb808fe55e8358584e3d03c4535dac68ca8825be332a212a0fe36f2 + zod: "npm:^3.25.76 || ^4.0.0" + checksum: 10c0/0b316cc22bf5641f88d2ab9bfbadef8bcf7721136429871bcf49373a363dbb912b9d8fdc833029f3b91c06c36f66df2fb5b84c132574710fce340244a4023be5 languageName: node linkType: hard @@ -2164,6 +2080,20 @@ __metadata: languageName: node linkType: hard +"@backstage/catalog-client@npm:^1.14.0": + version: 1.14.0 + resolution: "@backstage/catalog-client@npm:1.14.0" + dependencies: + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/errors": "npm:^1.2.7" + "@backstage/filter-predicates": "npm:^0.1.1" + cross-fetch: "npm:^4.0.0" + lodash: "npm:^4.17.21" + uri-template: "npm:^2.0.0" + checksum: 10c0/f54ba5a6c2b375a465693c3efe4593f5e42c3f3a9e8a154d5f68c6db9890d658a5c96934b1260f3cb216da3ddf7e40b39119c9c0b979c469ab32ec4902b29ccf + languageName: node + linkType: hard + "@backstage/catalog-model@npm:^1.7.5, @backstage/catalog-model@npm:^1.7.6": version: 1.7.6 resolution: "@backstage/catalog-model@npm:1.7.6" @@ -2176,51 +2106,100 @@ __metadata: languageName: node linkType: hard -"@backstage/cli-common@npm:^0.1.16, @backstage/cli-common@npm:^0.1.17": - version: 0.1.17 - resolution: "@backstage/cli-common@npm:0.1.17" +"@backstage/catalog-model@npm:^1.7.7": + version: 1.7.7 + resolution: "@backstage/catalog-model@npm:1.7.7" + dependencies: + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.2" + ajv: "npm:^8.10.0" + lodash: "npm:^4.17.21" + checksum: 10c0/eba74e24a59893f24b35e7d16be2d8c328dde1d87f5c38a14d8b5291e9b4a03bc30d72096b97ff1446ab5d2350bdb3c5ad4f2bf11a6686ed6cb8808dfa2fd1a8 + languageName: node + linkType: hard + +"@backstage/cli-common@npm:^0.2.0": + version: 0.2.0 + resolution: "@backstage/cli-common@npm:0.2.0" dependencies: "@backstage/errors": "npm:^1.2.7" cross-spawn: "npm:^7.0.3" global-agent: "npm:^3.0.0" undici: "npm:^7.2.3" - checksum: 10c0/85dc7c7ecfbd1dc47f83b6a320a09c83773f9a6357b4186bbf53541f1f58db6dc576ed7de84c1285e38fdbe9c02851b63f48dd4474845f36fe3f33dc4ecb28e9 + checksum: 10c0/99bd1cca5db1bb97b400b10486933717b539ca03d4885f5db08ae0c52879c55f04e79e64af0b23f700cf3e1a913c946f6b3b29f6295304914838b0a5046d3823 languageName: node linkType: hard -"@backstage/cli-node@npm:^0.2.16, @backstage/cli-node@npm:^0.2.17": - version: 0.2.17 - resolution: "@backstage/cli-node@npm:0.2.17" +"@backstage/cli-defaults@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-defaults@npm:0.1.0" + dependencies: + "@backstage/cli-module-actions": "npm:^0.0.1" + "@backstage/cli-module-auth": "npm:^0.1.0" + "@backstage/cli-module-build": "npm:^0.1.0" + "@backstage/cli-module-config": "npm:^0.1.0" + "@backstage/cli-module-github": "npm:^0.1.0" + "@backstage/cli-module-info": "npm:^0.1.0" + "@backstage/cli-module-lint": "npm:^0.1.0" + "@backstage/cli-module-maintenance": "npm:^0.1.0" + "@backstage/cli-module-migrate": "npm:^0.1.0" + "@backstage/cli-module-new": "npm:^0.1.0" + "@backstage/cli-module-test-jest": "npm:^0.1.0" + "@backstage/cli-module-translations": "npm:^0.1.0" + checksum: 10c0/1cfbeb156f4427213411cb74d22bc7ff2666047588ae7bebf032e241758c9144f924c8f3569a70d59ac8cb4578641d3628694b16291d717579081b8c73fdf4ea + languageName: node + linkType: hard + +"@backstage/cli-module-actions@npm:^0.0.1": + version: 0.0.1 + resolution: "@backstage/cli-module-actions@npm:0.0.1" dependencies: - "@backstage/cli-common": "npm:^0.1.17" + "@backstage/cli-node": "npm:^0.3.0" "@backstage/errors": "npm:^1.2.7" - "@backstage/types": "npm:^1.2.2" - "@manypkg/get-packages": "npm:^1.1.3" - "@yarnpkg/parsers": "npm:^3.0.0" + cleye: "npm:^2.3.0" + zod: "npm:^3.25.76 || ^4.0.0" + bin: + cli-module-actions: bin/backstage-cli-module-actions + checksum: 10c0/70afc3bfa791237c15927e2c093e4c6ccd16196227484f4d8d963f7821b7ee28100cfd34993470871cf2f1c8ae6daa8dcdf51ffef085a3a04b2ae1911e46c4ad + languageName: node + linkType: hard + +"@backstage/cli-module-auth@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-auth@npm:0.1.0" + dependencies: + "@backstage/cli-node": "npm:^0.3.0" + "@backstage/errors": "npm:^1.2.7" + cleye: "npm:^2.3.0" fs-extra: "npm:^11.2.0" - semver: "npm:^7.5.3" + glob: "npm:^7.1.7" + inquirer: "npm:^8.2.0" + keytar: "npm:^7.9.0" + proper-lockfile: "npm:^4.1.2" + yaml: "npm:^2.0.0" zod: "npm:^3.25.76" - checksum: 10c0/0dc8dc104784a4a89695b02426330bfa1b25cca6327be48e4dccd5a38372209c804ff7b02862859ec0d3768fa440eba7baaa5d6f48c3d2cdad3878eaddad9f8a + dependenciesMeta: + keytar: + optional: true + bin: + cli-module-auth: bin/backstage-cli-module-auth + checksum: 10c0/9381462c23376177852ea050aca165a3fa7d57b5668e3bc7c1da51fe5c8492bce961d578f702c09230b58c05212fceb8c626580db097484ed00d6aba9d63c87e languageName: node linkType: hard -"@backstage/cli@npm:^0.35.0": - version: 0.35.3 - resolution: "@backstage/cli@npm:0.35.3" +"@backstage/cli-module-build@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-build@npm:0.1.0" dependencies: - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/cli-common": "npm:^0.1.17" - "@backstage/cli-node": "npm:^0.2.17" + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" "@backstage/config": "npm:^1.3.6" - "@backstage/config-loader": "npm:^1.10.7" + "@backstage/config-loader": "npm:^1.10.9" "@backstage/errors": "npm:^1.2.7" - "@backstage/eslint-plugin": "npm:^0.2.0" - "@backstage/integration": "npm:^1.19.2" - "@backstage/release-manifests": "npm:^0.0.13" - "@backstage/types": "npm:^1.2.2" + "@backstage/module-federation-common": "npm:^0.1.2" "@manypkg/get-packages": "npm:^1.1.3" - "@module-federation/enhanced": "npm:^0.9.0" - "@octokit/request": "npm:^8.0.0" + "@module-federation/enhanced": "npm:^0.21.6" + "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.6.0" "@rollup/plugin-commonjs": "npm:^26.0.0" "@rollup/plugin-json": "npm:^6.0.0" "@rollup/plugin-node-resolve": "npm:^15.0.0" @@ -2228,133 +2207,334 @@ __metadata: "@rspack/core": "npm:^1.4.11" "@rspack/dev-server": "npm:^1.1.4" "@rspack/plugin-react-refresh": "npm:^1.4.3" - "@spotify/eslint-config-base": "npm:^15.0.0" - "@spotify/eslint-config-react": "npm:^15.0.0" - "@spotify/eslint-config-typescript": "npm:^15.0.0" "@swc/core": "npm:^1.15.6" - "@swc/helpers": "npm:^0.5.17" - "@swc/jest": "npm:^0.2.39" - "@types/webpack-env": "npm:^1.15.2" - "@typescript-eslint/eslint-plugin": "npm:^8.17.0" - "@typescript-eslint/parser": "npm:^8.16.0" - "@yarnpkg/lockfile": "npm:^1.1.0" - "@yarnpkg/parsers": "npm:^3.0.0" - bfj: "npm:^8.0.0" + bfj: "npm:^9.0.2" buffer: "npm:^6.0.3" chalk: "npm:^4.0.0" chokidar: "npm:^3.3.1" - commander: "npm:^12.0.0" - cross-fetch: "npm:^4.0.0" + cleye: "npm:^2.3.0" cross-spawn: "npm:^7.0.3" css-loader: "npm:^6.5.1" ctrlc-windows: "npm:^2.1.0" - esbuild: "npm:^0.27.0" - eslint: "npm:^8.6.0" - eslint-config-prettier: "npm:^9.0.0" - eslint-formatter-friendly: "npm:^7.0.0" - eslint-plugin-deprecation: "npm:^3.0.0" - eslint-plugin-import: "npm:^2.31.0" - eslint-plugin-jest: "npm:^28.9.0" - eslint-plugin-jsx-a11y: "npm:^6.10.2" - eslint-plugin-react: "npm:^7.37.2" - eslint-plugin-react-hooks: "npm:^5.0.0" - eslint-plugin-unused-imports: "npm:^4.1.4" + esbuild-loader: "npm:^4.0.0" eslint-rspack-plugin: "npm:^4.2.1" - express: "npm:^4.22.0" + eslint-webpack-plugin: "npm:^4.2.0" + fork-ts-checker-webpack-plugin: "npm:^9.0.0" fs-extra: "npm:^11.2.0" - git-url-parse: "npm:^15.0.0" glob: "npm:^7.1.7" - global-agent: "npm:^3.0.0" - globby: "npm:^11.1.0" - handlebars: "npm:^4.7.3" html-webpack-plugin: "npm:^5.6.3" - inquirer: "npm:^8.2.0" - jest-css-modules: "npm:^2.1.0" - json-schema: "npm:^0.4.0" lodash: "npm:^4.17.21" - minimatch: "npm:^9.0.0" + mini-css-extract-plugin: "npm:^2.4.2" node-stdlib-browser: "npm:^1.3.1" npm-packlist: "npm:^5.0.0" - ora: "npm:^5.3.0" p-queue: "npm:^6.6.2" - pirates: "npm:^4.0.6" postcss: "npm:^8.1.0" + postcss-import: "npm:^16.1.0" process: "npm:^0.11.10" raw-loader: "npm:^4.0.2" react-dev-utils: "npm:^12.0.0-next.60" - react-refresh: "npm:^0.17.0" - recursive-readdir: "npm:^2.2.2" - replace-in-file: "npm:^7.1.0" + react-refresh: "npm:^0.18.0" rollup: "npm:^4.27.3" rollup-plugin-dts: "npm:^6.1.0" rollup-plugin-esbuild: "npm:^6.1.1" rollup-plugin-postcss: "npm:^4.0.0" rollup-pluginutils: "npm:^2.8.2" - semver: "npm:^7.5.3" + shell-quote: "npm:^1.8.1" style-loader: "npm:^3.3.1" - sucrase: "npm:^3.20.2" swc-loader: "npm:^0.2.3" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" ts-checker-rspack-plugin: "npm:^1.1.5" ts-morph: "npm:^24.0.0" - undici: "npm:^7.2.3" util: "npm:^0.12.3" - yaml: "npm:^2.0.0" - yargs: "npm:^16.2.0" + webpack: "npm:~5.105.0" + webpack-dev-server: "npm:^5.0.0" yml-loader: "npm:^2.1.0" yn: "npm:^4.0.0" + bin: + cli-module-build: bin/backstage-cli-module-build + checksum: 10c0/7b77439c80626d96e33e8d8e4f7803da2002610a0850b8a8d5ec295ecbcdcf17d0caeb4ef0a9a67bd7038c29e8adc72bc5fe6e48bfd5f282be4919968d5406c8 + languageName: node + linkType: hard + +"@backstage/cli-module-config@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-config@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + "@backstage/config": "npm:^1.3.6" + "@backstage/config-loader": "npm:^1.10.9" + "@backstage/types": "npm:^1.2.2" + "@manypkg/get-packages": "npm:^1.1.3" + chalk: "npm:^4.0.0" + cleye: "npm:^2.3.0" + json-schema: "npm:^0.4.0" + react-dev-utils: "npm:^12.0.0-next.60" + yaml: "npm:^2.0.0" + bin: + cli-module-config: bin/backstage-cli-module-config + checksum: 10c0/6c30c714ce5e95debd2ae69edd520a70081893bc4f450d3692d74bf232306f386f8a0ab650a33bde8e8aee5ddff5c9b0e3f7623ff9cc18cab7478bca0aaf515e + languageName: node + linkType: hard + +"@backstage/cli-module-github@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-github@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + "@octokit/request": "npm:^8.0.0" + chalk: "npm:^4.0.0" + cleye: "npm:^2.3.0" + express: "npm:^4.22.0" + fs-extra: "npm:^11.2.0" + inquirer: "npm:^8.2.0" + react-dev-utils: "npm:^12.0.0-next.60" + yaml: "npm:^2.0.0" + bin: + cli-module-github: bin/backstage-cli-module-github + checksum: 10c0/c36d37c950d21a4b06258aca2442f9efd1a0e140ca5b705058c46042dbcd239d0a67c8b14746dc47425c69d34a2aca55f541a8f2a876501578af094fad09bc44 + languageName: node + linkType: hard + +"@backstage/cli-module-info@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-info@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + cleye: "npm:^2.3.0" + fs-extra: "npm:^11.2.0" + minimatch: "npm:^10.2.1" + bin: + cli-module-info: bin/backstage-cli-module-info + checksum: 10c0/dfee7fe60dde9cc764b22ebaf83675718ca60d3db1a62643244be8aedcd822291bfe4702ecd3e413db3ec3bc759d5f2d46fdc0121820e64af59073f5d899d98d + languageName: node + linkType: hard + +"@backstage/cli-module-lint@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-lint@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + chalk: "npm:^4.0.0" + cleye: "npm:^2.3.0" + eslint: "npm:^8.6.0" + eslint-formatter-friendly: "npm:^7.0.0" + fs-extra: "npm:^11.2.0" + globby: "npm:^11.1.0" + shell-quote: "npm:^1.8.1" + bin: + cli-module-lint: bin/backstage-cli-module-lint + checksum: 10c0/fdd00eebdbce71f0a20206ec3dcce81624df5d8480dff9dee0f5f3bd076bb2091afe4b12c95def0cad89ed750bc65d0e1ee74c4b57bf6f9142489baeacbc0f65 + languageName: node + linkType: hard + +"@backstage/cli-module-maintenance@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-maintenance@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + chalk: "npm:^4.0.0" + cleye: "npm:^2.3.0" + eslint: "npm:^8.6.0" + fs-extra: "npm:^11.2.0" + bin: + cli-module-maintenance: bin/backstage-cli-module-maintenance + checksum: 10c0/a8c970e53380d9ba42e64bd6997744ebdb59fd6fb52c2ced30b3a5796e35621784776d3d12c9a6dfb4fc40afeb9f9953d372f01fa4aeb7ddc897d0c2a2b00978 + languageName: node + linkType: hard + +"@backstage/cli-module-migrate@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-migrate@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + "@backstage/errors": "npm:^1.2.7" + "@backstage/release-manifests": "npm:^0.0.13" + "@manypkg/get-packages": "npm:^1.1.3" + chalk: "npm:^4.0.0" + cleye: "npm:^2.3.0" + fs-extra: "npm:^11.2.0" + minimatch: "npm:^10.2.1" + ora: "npm:^5.3.0" + replace-in-file: "npm:^7.1.0" + semver: "npm:^7.5.3" + bin: + cli-module-migrate: bin/backstage-cli-module-migrate + checksum: 10c0/c9689aa84cadec2c4e996b0d2f182c4185483bb85b8dcc05b2e0f350a7a3114d5268546ff57c9a5b1aa57b47a5a8da35f7517f8632ebcccaed897f165c333230 + languageName: node + linkType: hard + +"@backstage/cli-module-new@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-new@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + "@backstage/errors": "npm:^1.2.7" + chalk: "npm:^4.0.0" + cleye: "npm:^2.3.0" + fs-extra: "npm:^11.2.0" + handlebars: "npm:^4.7.3" + inquirer: "npm:^8.2.0" + lodash: "npm:^4.17.21" + ora: "npm:^5.3.0" + recursive-readdir: "npm:^2.2.2" + semver: "npm:^7.5.3" + yaml: "npm:^2.0.0" zod: "npm:^3.25.76" zod-validation-error: "npm:^4.0.2" + bin: + cli-module-new: bin/backstage-cli-module-new + checksum: 10c0/afdc02fe78f94f14ae2c8b8c8ddd1d42dfec03daa60bab1a1dcb174f72dcc37483269418518be46f75777198cc3a0859363f41f222461bf0d8d06d61d90300e6 + languageName: node + linkType: hard + +"@backstage/cli-module-test-jest@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-test-jest@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + "@swc/core": "npm:^1.15.6" + "@swc/jest": "npm:^0.2.39" + cleye: "npm:^2.3.0" + cross-fetch: "npm:^4.0.0" + fs-extra: "npm:^11.2.0" + glob: "npm:^7.1.7" + jest-css-modules: "npm:^2.1.0" + sucrase: "npm:^3.20.2" + yargs: "npm:^16.2.0" peerDependencies: "@jest/environment-jsdom-abstract": ^30.0.0 - "@module-federation/enhanced": ^0.9.0 - "@pmmmwh/react-refresh-webpack-plugin": ^0.6.0 - esbuild-loader: ^4.0.0 - eslint-webpack-plugin: ^4.2.0 - fork-ts-checker-webpack-plugin: ^9.0.0 jest: ^29.0.0 || ^30.0.0 jest-environment-jsdom: "*" jsdom: ^27.1.0 - mini-css-extract-plugin: ^2.4.2 - terser-webpack-plugin: ^5.1.3 - webpack: ~5.103.0 - webpack-dev-server: ^5.0.0 peerDependenciesMeta: "@jest/environment-jsdom-abstract": optional: true - "@module-federation/enhanced": + jest-environment-jsdom: optional: true - "@pmmmwh/react-refresh-webpack-plugin": + jsdom: optional: true - esbuild-loader: + bin: + cli-module-test-jest: bin/backstage-cli-module-test-jest + checksum: 10c0/debceb8ccd2ad2ea0be1a03f0bc9d2a7b59aaaf687a6f9d70d9b63dca4e5ecb4678f2c52af76899aa9dce08ac02abca75a1ee73f740ff9d5db8c22f700838328 + languageName: node + linkType: hard + +"@backstage/cli-module-translations@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/cli-module-translations@npm:0.1.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-node": "npm:^0.3.0" + cleye: "npm:^2.3.0" + fs-extra: "npm:^11.2.0" + ts-morph: "npm:^24.0.0" + bin: + cli-module-translations: bin/backstage-cli-module-translations + checksum: 10c0/786de0f095d6c7300e93d3928313ad286f9ab666393831fd31e2731671bdd2aa4708b64ff91fe36b07aa065884c17ca6df527cc7e157e7848054dd9223cb37ea + languageName: node + linkType: hard + +"@backstage/cli-node@npm:^0.3.0": + version: 0.3.0 + resolution: "@backstage/cli-node@npm:0.3.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.2" + "@manypkg/get-packages": "npm:^1.1.3" + "@yarnpkg/lockfile": "npm:^1.1.0" + "@yarnpkg/parsers": "npm:^3.0.0" + chalk: "npm:^4.0.0" + commander: "npm:^12.0.0" + fs-extra: "npm:^11.2.0" + keytar: "npm:^7.9.0" + pirates: "npm:^4.0.6" + proper-lockfile: "npm:^4.1.2" + semver: "npm:^7.5.3" + yaml: "npm:^2.0.0" + zod: "npm:^3.25.76 || ^4.0.0" + peerDependencies: + "@swc/core": ^1.15.6 + dependenciesMeta: + keytar: optional: true - eslint-webpack-plugin: + peerDependenciesMeta: + "@swc/core": optional: true - fork-ts-checker-webpack-plugin: + checksum: 10c0/ada61a8f65a8a45299488748244d131fb0ac737295da52b41f3d5efa9b41559bc325d8f1651d95f5cc51c115661e54df263d271ab1acc8988119c26f0daf54fa + languageName: node + linkType: hard + +"@backstage/cli@npm:^0.36.0": + version: 0.36.0 + resolution: "@backstage/cli@npm:0.36.0" + dependencies: + "@backstage/cli-common": "npm:^0.2.0" + "@backstage/cli-defaults": "npm:^0.1.0" + "@backstage/cli-module-build": "npm:^0.1.0" + "@backstage/cli-module-test-jest": "npm:^0.1.0" + "@backstage/cli-node": "npm:^0.3.0" + "@backstage/errors": "npm:^1.2.7" + "@backstage/eslint-plugin": "npm:^0.2.2" + "@manypkg/get-packages": "npm:^1.1.3" + "@spotify/eslint-config-base": "npm:^15.0.0" + "@spotify/eslint-config-react": "npm:^15.0.0" + "@spotify/eslint-config-typescript": "npm:^15.0.0" + "@swc/core": "npm:^1.15.6" + "@swc/jest": "npm:^0.2.39" + "@types/webpack-env": "npm:^1.15.2" + "@typescript-eslint/eslint-plugin": "npm:^8.17.0" + "@typescript-eslint/parser": "npm:^8.16.0" + chalk: "npm:^4.0.0" + commander: "npm:^14.0.3" + cross-fetch: "npm:^4.0.0" + eslint: "npm:^8.6.0" + eslint-config-prettier: "npm:^9.0.0" + eslint-plugin-deprecation: "npm:^3.0.0" + eslint-plugin-import: "npm:^2.31.0" + eslint-plugin-jest: "npm:^28.9.0" + eslint-plugin-jsx-a11y: "npm:^6.10.2" + eslint-plugin-react: "npm:^7.37.2" + eslint-plugin-react-hooks: "npm:^5.0.0" + eslint-plugin-unused-imports: "npm:^4.1.4" + fs-extra: "npm:^11.2.0" + glob: "npm:^7.1.7" + jest-css-modules: "npm:^2.1.0" + pirates: "npm:^4.0.6" + postcss: "npm:^8.1.0" + sucrase: "npm:^3.20.2" + yaml: "npm:^2.0.0" + peerDependencies: + "@jest/environment-jsdom-abstract": ^30.0.0 + jest: ^29.0.0 || ^30.0.0 + jest-environment-jsdom: "*" + jsdom: ^27.1.0 + peerDependenciesMeta: + "@jest/environment-jsdom-abstract": optional: true jest-environment-jsdom: optional: true jsdom: optional: true - mini-css-extract-plugin: - optional: true - terser-webpack-plugin: - optional: true - webpack: - optional: true - webpack-dev-server: - optional: true bin: backstage-cli: bin/backstage-cli - checksum: 10c0/ba02845ebf05b8b98f37774cced83f3a4bdb7fbf3ffda456665d56a8e95a1322fe1b191456c3625173e25057317f2f17f55b95f8308dcb83342822d7aec85650 + checksum: 10c0/856f8aef4a3406f2067489c9df7ba62b3efbaa310766f7497f015a5fa134f4a65a5311cab39b9e35e32feb15885ab47148c1f35b07a208a5491ef10f2d481dbe languageName: node linkType: hard -"@backstage/config-loader@npm:^1.10.7": - version: 1.10.7 - resolution: "@backstage/config-loader@npm:1.10.7" +"@backstage/config-loader@npm:^1.10.9": + version: 1.10.9 + resolution: "@backstage/config-loader@npm:1.10.9" dependencies: - "@backstage/cli-common": "npm:^0.1.16" + "@backstage/cli-common": "npm:^0.2.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" "@backstage/types": "npm:^1.2.2" @@ -2369,7 +2549,7 @@ __metadata: minimist: "npm:^1.2.5" typescript-json-schema: "npm:^0.67.0" yaml: "npm:^2.0.0" - checksum: 10c0/3e8a6dc4835532259cf2729fb5c3a12ba8720728b2ad09421d70c0e71bdfd085548d1c00d9e500279381d46731e1d953b541c47b5fd2ea83593dda1fa757756a + checksum: 10c0/603b631d50b24651db6cc6c8298a556f26bc0ef91cfa7664d96b35678d99558a5fb9b7e0a664eee8b07894c4064cc733c003eeefd8431d5a752af14b7bae6500 languageName: node linkType: hard @@ -2412,7 +2592,36 @@ __metadata: languageName: node linkType: hard -"@backstage/core-compat-api@npm:^0.5.3, @backstage/core-compat-api@npm:^0.5.6, @backstage/core-compat-api@npm:^0.5.7": +"@backstage/core-app-api@npm:^1.19.6": + version: 1.19.6 + resolution: "@backstage/core-app-api@npm:1.19.6" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/types": "npm:^1.2.2" + "@backstage/ui": "npm:^0.13.0" + "@backstage/version-bridge": "npm:^1.0.12" + "@types/prop-types": "npm:^15.7.3" + history: "npm:^5.0.0" + i18next: "npm:^22.4.15" + lodash: "npm:^4.17.21" + prop-types: "npm:^15.7.2" + react-use: "npm:^17.2.4" + zen-observable: "npm:^0.10.0" + zod: "npm:^3.25.76 || ^4.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/5cd60623e09035b1240142941dd336a399e4f2d2455c2e4ad3de5f4e6f641181c000a0a3071c9e0f0af04ef6a68bcd4538fcfaf82b2ca2722d18e28b8f85335c + languageName: node + linkType: hard + +"@backstage/core-compat-api@npm:^0.5.3, @backstage/core-compat-api@npm:^0.5.7": version: 0.5.7 resolution: "@backstage/core-compat-api@npm:0.5.7" dependencies: @@ -2435,7 +2644,33 @@ __metadata: languageName: node linkType: hard -"@backstage/core-components@npm:^0.18.2, @backstage/core-components@npm:^0.18.4, @backstage/core-components@npm:^0.18.5, @backstage/core-components@npm:^0.18.6": +"@backstage/core-compat-api@npm:^0.5.9": + version: 0.5.9 + resolution: "@backstage/core-compat-api@npm:0.5.9" + dependencies: + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/errors": "npm:^1.2.7" + "@backstage/filter-predicates": "npm:^0.1.1" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-app-react": "npm:^0.2.1" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/types": "npm:^1.2.2" + "@backstage/version-bridge": "npm:^1.0.12" + lodash: "npm:^4.17.21" + zod: "npm:^3.25.76 || ^4.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/b2a57b43c96ee56bfd0eb21779ebc4e8afd836d4e334be6b8c4cdd3b1d744be25da7fbd521ee93ec1fca41d7dd30e47094ae2dfde1e7d4a63f2975f8aee78454 + languageName: node + linkType: hard + +"@backstage/core-components@npm:^0.18.2, @backstage/core-components@npm:^0.18.5, @backstage/core-components@npm:^0.18.6": version: 0.18.6 resolution: "@backstage/core-components@npm:0.18.6" dependencies: @@ -2493,6 +2728,64 @@ __metadata: languageName: node linkType: hard +"@backstage/core-components@npm:^0.18.8": + version: 0.18.8 + resolution: "@backstage/core-components@npm:0.18.8" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/errors": "npm:^1.2.7" + "@backstage/theme": "npm:^0.7.2" + "@backstage/version-bridge": "npm:^1.0.12" + "@dagrejs/dagre": "npm:^1.1.4" + "@date-io/core": "npm:^1.3.13" + "@material-table/core": "npm:^3.1.0" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@react-hookz/web": "npm:^24.0.0" + "@testing-library/react": "npm:^16.0.0" + "@types/react-sparklines": "npm:^1.7.0" + ansi-regex: "npm:^6.0.1" + classnames: "npm:^2.2.6" + d3-selection: "npm:^3.0.0" + d3-shape: "npm:^3.0.0" + d3-zoom: "npm:^3.0.0" + js-yaml: "npm:^4.1.0" + linkify-react: "npm:4.3.2" + linkifyjs: "npm:4.3.2" + lodash: "npm:^4.17.21" + parse5: "npm:^6.0.0" + pluralize: "npm:^8.0.0" + qs: "npm:^6.9.4" + rc-progress: "npm:3.5.1" + react-full-screen: "npm:^1.1.1" + react-helmet: "npm:6.1.0" + react-hook-form: "npm:^7.12.2" + react-idle-timer: "npm:5.7.2" + react-markdown: "npm:^8.0.0" + react-sparklines: "npm:^1.7.0" + react-syntax-highlighter: "npm:^15.4.5" + react-use: "npm:^17.3.2" + react-virtualized-auto-sizer: "npm:^1.0.11" + react-window: "npm:^1.8.6" + rehype-raw: "npm:^6.0.0" + rehype-sanitize: "npm:^5.0.0" + remark-gfm: "npm:^3.0.1" + zen-observable: "npm:^0.10.0" + zod: "npm:^3.25.76 || ^4.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/34ba90ca5c0925fee9f7785f0b23fc45b602c0705889f3a9efb109850298d428e77343b95d9539e043aff303549a65b7a4517e8ff0ddde607441a7d002b8b53d + languageName: node + linkType: hard + "@backstage/core-plugin-api@npm:^1.11.1, @backstage/core-plugin-api@npm:^1.12.1, @backstage/core-plugin-api@npm:^1.12.2": version: 1.12.2 resolution: "@backstage/core-plugin-api@npm:1.12.2" @@ -2516,9 +2809,32 @@ __metadata: languageName: node linkType: hard -"@backstage/e2e-test-utils@npm:^0.1.1": - version: 0.1.1 - resolution: "@backstage/e2e-test-utils@npm:0.1.1" +"@backstage/core-plugin-api@npm:^1.12.4": + version: 1.12.4 + resolution: "@backstage/core-plugin-api@npm:1.12.4" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/errors": "npm:^1.2.7" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/types": "npm:^1.2.2" + "@backstage/version-bridge": "npm:^1.0.12" + history: "npm:^5.0.0" + zod: "npm:^3.25.76 || ^4.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/0226ea1fe27392ce6e66c79453aa17d758341f73b943f87acd04123b38291962ac5350975005cb0b3a5a920073cbfe3d661b8c5332c78835401a5507cb8633bc + languageName: node + linkType: hard + +"@backstage/e2e-test-utils@npm:^0.1.2": + version: 0.1.2 + resolution: "@backstage/e2e-test-utils@npm:0.1.2" dependencies: "@manypkg/get-packages": "npm:^1.1.3" fs-extra: "npm:^11.0.0" @@ -2527,7 +2843,7 @@ __metadata: peerDependenciesMeta: "@playwright/test": optional: true - checksum: 10c0/d567023ecb011f6b979076a9fe901ac09be932804a4e1d270f1126fe37ceb37cda60a5ebb87ce61d83298e0cbc44f64c1463d08cb15bfd91d3cd31fb6ee14b69 + checksum: 10c0/ed923163c0b5dfbee870cf76cadf919708662119089816ce0bf30b59b7e6742c0e94287ebf081ee549a74225cd0d8558dc93d1f53dc3f1504ded39cd2a0546bf languageName: node linkType: hard @@ -2541,13 +2857,26 @@ __metadata: languageName: node linkType: hard -"@backstage/eslint-plugin@npm:^0.2.0": - version: 0.2.0 - resolution: "@backstage/eslint-plugin@npm:0.2.0" +"@backstage/eslint-plugin@npm:^0.2.2": + version: 0.2.2 + resolution: "@backstage/eslint-plugin@npm:0.2.2" dependencies: "@manypkg/get-packages": "npm:^1.1.3" - minimatch: "npm:^9.0.0" - checksum: 10c0/7f57cd42629084b0363cf9adb37d33479fd540226a29705da623db9032890a6e67d1eb15b8076ea2fe3b502e36810ad819c93936406524e56e33801b561d6ff3 + minimatch: "npm:^10.2.1" + checksum: 10c0/5659f5cfc6aeb4b498e6030ffcba4b83364a7eac3cdda51efcae657c904ec46b2999f97804655bfa11c78f553448ed2cc658721850f717925ea951218b812e2b + languageName: node + linkType: hard + +"@backstage/filter-predicates@npm:^0.1.1": + version: 0.1.1 + resolution: "@backstage/filter-predicates@npm:0.1.1" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.2" + zod: "npm:^3.25.76 || ^4.0.0" + zod-validation-error: "npm:^4.0.2" + checksum: 10c0/f4bce2259af0e953ef30d292394aeea614ae42fbd825678a3abc36a97a6020a9964f40046aa3dc189f040ecf9674fb30dbcbbfd2ff3f807a513ad0353094bea5 languageName: node linkType: hard @@ -2624,7 +2953,7 @@ __metadata: languageName: node linkType: hard -"@backstage/frontend-plugin-api@npm:^0.13.2, @backstage/frontend-plugin-api@npm:^0.13.3, @backstage/frontend-plugin-api@npm:^0.13.4": +"@backstage/frontend-plugin-api@npm:^0.13.3, @backstage/frontend-plugin-api@npm:^0.13.4": version: 0.13.4 resolution: "@backstage/frontend-plugin-api@npm:0.13.4" dependencies: @@ -2645,6 +2974,28 @@ __metadata: languageName: node linkType: hard +"@backstage/frontend-plugin-api@npm:^0.15.0, @backstage/frontend-plugin-api@npm:^0.15.1": + version: 0.15.1 + resolution: "@backstage/frontend-plugin-api@npm:0.15.1" + dependencies: + "@backstage/errors": "npm:^1.2.7" + "@backstage/filter-predicates": "npm:^0.1.1" + "@backstage/types": "npm:^1.2.2" + "@backstage/version-bridge": "npm:^1.0.12" + zod: "npm:^3.25.76 || ^4.0.0" + zod-to-json-schema: "npm:^3.25.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/8064e0845849f7f27af62ff30007bc08a89fc052330e46138735669280e962f2643485a5e2e38dcec16d0ad8a93c150d5fc0b94b9e045106cba11c23bff365dc + languageName: node + linkType: hard + "@backstage/frontend-test-utils@npm:^0.4.5": version: 0.4.5 resolution: "@backstage/frontend-test-utils@npm:0.4.5" @@ -2671,9 +3022,9 @@ __metadata: languageName: node linkType: hard -"@backstage/integration-aws-node@npm:^0.1.19": - version: 0.1.19 - resolution: "@backstage/integration-aws-node@npm:0.1.19" +"@backstage/integration-aws-node@npm:^0.1.20": + version: 0.1.20 + resolution: "@backstage/integration-aws-node@npm:0.1.20" dependencies: "@aws-sdk/client-sts": "npm:^3.350.0" "@aws-sdk/credential-provider-node": "npm:^3.350.0" @@ -2682,11 +3033,11 @@ __metadata: "@aws-sdk/util-arn-parser": "npm:^3.310.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - checksum: 10c0/803ba256051c6a9f7e45ba199e6d219e99ce5fd0e6145ec3ad9ff18dc333c4b148aac7a8cc1a67ea48f0a5f7e461271949284d9c5101bd1b4f317043d5f3a87b + checksum: 10c0/7202f84ae1db449a7e3fa337abfe7d109d6207ff90ab7907befd1b7f7ef683cd8a9229239c3ebed58687ca8ab3c55b074cb6b33e4c93a8a92cb3b9cd452064de languageName: node linkType: hard -"@backstage/integration-react@npm:^1.2.13, @backstage/integration-react@npm:^1.2.14": +"@backstage/integration-react@npm:^1.2.14": version: 1.2.14 resolution: "@backstage/integration-react@npm:1.2.14" dependencies: @@ -2699,15 +3050,36 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/ec097263425673d7aec640732f45765d19e0fbfc50a9d13e3df32fc7f8718974e4280ff9792e9416ed91401167759bbf707265951c5fe18aa953ee2641af6abd + languageName: node + linkType: hard + +"@backstage/integration-react@npm:^1.2.16": + version: 1.2.16 + resolution: "@backstage/integration-react@npm:1.2.16" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/integration": "npm:^2.0.0" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/ec097263425673d7aec640732f45765d19e0fbfc50a9d13e3df32fc7f8718974e4280ff9792e9416ed91401167759bbf707265951c5fe18aa953ee2641af6abd + checksum: 10c0/70ff6dca97e1ff797e322771061ad642cd8f138f63fd44b743fd2eab1d975823e55a6bd95a04a4fe05b9974301164ceaae5ff79e91e90520e77710405ddf43ba languageName: node linkType: hard -"@backstage/integration@npm:^1.19.1, @backstage/integration@npm:^1.19.2": +"@backstage/integration@npm:^1.19.2": version: 1.19.2 resolution: "@backstage/integration@npm:1.19.2" dependencies: @@ -2725,50 +3097,95 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-api-docs@npm:^0.13.2": - version: 0.13.3 - resolution: "@backstage/plugin-api-docs@npm:0.13.3" +"@backstage/integration@npm:^2.0.0": + version: 2.0.0 + resolution: "@backstage/integration@npm:2.0.0" + dependencies: + "@azure/identity": "npm:^4.0.0" + "@azure/storage-blob": "npm:^12.5.0" + "@backstage/config": "npm:^1.3.6" + "@backstage/errors": "npm:^1.2.7" + "@octokit/auth-app": "npm:^4.0.0" + "@octokit/rest": "npm:^19.0.3" + cross-fetch: "npm:^4.0.0" + git-url-parse: "npm:^15.0.0" + lodash: "npm:^4.17.21" + luxon: "npm:^3.0.0" + p-throttle: "npm:^4.1.1" + checksum: 10c0/d7e0e45cc11277ca2b843f98d15df8150b8c264852b734279a1965ccc81ef2724871e048aa1b0c4b3fe656c041d96ab0ca8c97db2bd236582d8f4349a93cd5cd + languageName: node + linkType: hard + +"@backstage/module-federation-common@npm:^0.1.2": + version: 0.1.2 + resolution: "@backstage/module-federation-common@npm:0.1.2" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.2" + "@module-federation/runtime": "npm:^0.21.6" + peerDependencies: + "@emotion/react": ^11.10.5 + "@material-ui/core": ^4.12.2 + "@material-ui/styles": ^4.10.0 + "@mui/material": ^5.12.2 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router: ^6.30.2 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/3809a6881b959ecab3062aa4b6b950fe5c08b3b4f985c2dc7fa68f450d1c9cdba0cf19d8402fad79001ad8ab513346cccf658328d0fa4cf10775f64b6cc6271c + languageName: node + linkType: hard + +"@backstage/plugin-api-docs@npm:^0.13.5": + version: 0.13.5 + resolution: "@backstage/plugin-api-docs@npm:0.13.5" dependencies: "@asyncapi/react-component": "npm:^2.3.3" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-catalog": "npm:^1.32.2" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@graphiql/react": "npm:^0.23.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog": "npm:^2.0.0" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/ui": "npm:^0.13.0" + "@graphiql/react": "npm:0.29.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" - graphiql: "npm:3.1.1" + "@remixicon/react": "npm:^4.6.0" + graphiql: "npm:^3.9.0" graphql: "npm:^16.0.0" - graphql-config: "npm:^5.0.2" graphql-ws: "npm:^5.4.1" swagger-ui-react: "npm:^5.27.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/6cdee8e5f6352a0db079a4fe1f6f7d56bd7cdc4fc82185bcafe5cdf8d01cad923eb68a45819ee2eaab384c05783deecb492c0540c02cc1407d0f2593cbb81943 + checksum: 10c0/fe7f6c86d72e8fc83c89de36797dd702d924e8f011933cd7de5de89c1f0be6667a9fc8d838c4ebabf7d0c08439ba307fd9b3f2239ac4f6835cbc1b67770d751d languageName: node linkType: hard -"@backstage/plugin-app-backend@npm:^0.5.9": - version: 0.5.10 - resolution: "@backstage/plugin-app-backend@npm:0.5.10" +"@backstage/plugin-app-backend@npm:^0.5.12": + version: 0.5.12 + resolution: "@backstage/plugin-app-backend@npm:0.5.12" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" - "@backstage/config-loader": "npm:^1.10.7" + "@backstage/config-loader": "npm:^1.10.9" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-app-node": "npm:^0.1.41" - "@backstage/plugin-auth-node": "npm:^0.6.11" + "@backstage/plugin-app-node": "npm:^0.1.43" + "@backstage/plugin-auth-node": "npm:^0.6.14" "@backstage/types": "npm:^1.2.2" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" @@ -2779,20 +3196,20 @@ __metadata: lodash: "npm:^4.17.21" luxon: "npm:^3.0.0" yn: "npm:^4.0.0" - checksum: 10c0/9bfed55188f2b050087dd47e1f983ffd8581402854c03b4697d5199dc60a7299016304a3cee42e49e13227ccd60629eb62beabb4e460685cd8679486b7665561 + checksum: 10c0/df271ab12c22b121c3753c11300040602414c6e55cf9a3a0daa0cf573621ff454e6ea5adcdf939bf57db10834a66130c5256e3c42da023bbe9bf01c812a55a2f languageName: node linkType: hard -"@backstage/plugin-app-node@npm:^0.1.41": - version: 0.1.41 - resolution: "@backstage/plugin-app-node@npm:0.1.41" +"@backstage/plugin-app-node@npm:^0.1.43": + version: 0.1.43 + resolution: "@backstage/plugin-app-node@npm:0.1.43" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/config-loader": "npm:^1.10.7" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/config-loader": "npm:^1.10.9" "@types/express": "npm:^4.17.6" express: "npm:^4.22.0" fs-extra: "npm:^11.2.0" - checksum: 10c0/159cc36578630c2622fe733785ac913d6f08268197c93d0c21fc04d8af096054b9fac73e55a4d5f23c0d88f05b7b86acdc9e3cfd1dbae6a064b918fd0da19918 + checksum: 10c0/7621ae5e67db7fafe72554e99b752d582b41607242ad37b359ebb44e6aedca4ca24ce9bd45b6727ba4672c88df0133eedeb707d5b21688a6f787c1ca31a40af4 languageName: node linkType: hard @@ -2815,6 +3232,25 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-app-react@npm:^0.2.1": + version: 0.2.1 + resolution: "@backstage/plugin-app-react@npm:0.2.1" + dependencies: + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@material-ui/core": "npm:^4.9.13" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/e343bc8b67105bd1484824c66628005e8bfc8f5815960a7fd894ddb7ca3c14915c778095c549591f78cbe9a8f3acd4cfed565b5ffc569a2d7f07555ba5ab1886 + languageName: node + linkType: hard + "@backstage/plugin-app@npm:^0.3.5": version: 0.3.5 resolution: "@backstage/plugin-app@npm:0.3.5" @@ -2846,41 +3282,41 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-auth-backend-module-github-provider@npm:^0.4.0": - version: 0.4.1 - resolution: "@backstage/plugin-auth-backend-module-github-provider@npm:0.4.1" +"@backstage/plugin-auth-backend-module-github-provider@npm:^0.5.1": + version: 0.5.1 + resolution: "@backstage/plugin-auth-backend-module-github-provider@npm:0.5.1" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/plugin-auth-node": "npm:^0.6.12" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/plugin-auth-node": "npm:^0.6.14" passport-github2: "npm:^0.1.12" - zod: "npm:^3.25.76" - checksum: 10c0/0cb3b5767bc1b0a17cad0fcf09ad5cd2b820704f21e64e64aac22c365b2aff3ae3375d4fb59a94f82f95bb8f91ae4596d2fa9f51038b2e5e1fd75ccdb1d53c98 + zod: "npm:^3.25.76 || ^4.0.0" + checksum: 10c0/2a714a5efd0be56ddad6a4f12978accea0cb2a0191553b69e493f59cc37312d43337bb1b8c3b3e923f354221fd9bbf09d69b723aacde9367c5118faaf88435ca languageName: node linkType: hard -"@backstage/plugin-auth-backend-module-guest-provider@npm:^0.2.15": - version: 0.2.15 - resolution: "@backstage/plugin-auth-backend-module-guest-provider@npm:0.2.15" +"@backstage/plugin-auth-backend-module-guest-provider@npm:^0.2.17": + version: 0.2.17 + resolution: "@backstage/plugin-auth-backend-module-guest-provider@npm:0.2.17" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.10" + "@backstage/plugin-auth-node": "npm:^0.6.14" passport-oauth2: "npm:^1.7.0" - checksum: 10c0/a68019126964e952abe05632977e8ad48a40e5357fd0a796d21db22f545cf761b5e646e222826d978840b1e2d42f2896db8c71f95d3b331806a4bdd17fb3d13c + checksum: 10c0/d73bac816b4175e5a773296e7864cd033eb7146d66abb5b4c7ac3f2504889fd9fce05a9416d7db3c6f683ea558897914a6e1d58b08fb45c87f0e377fd7336bc4 languageName: node linkType: hard -"@backstage/plugin-auth-backend@npm:^0.25.7": - version: 0.25.7 - resolution: "@backstage/plugin-auth-backend@npm:0.25.7" +"@backstage/plugin-auth-backend@npm:^0.27.2": + version: 0.27.2 + resolution: "@backstage/plugin-auth-backend@npm:0.27.2" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.10" - "@backstage/plugin-catalog-node": "npm:^1.20.1" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-catalog-node": "npm:^2.1.0" "@backstage/types": "npm:^1.2.2" "@google-cloud/firestore": "npm:^7.0.0" connect-session-knex: "npm:^4.0.0" @@ -2888,25 +3324,28 @@ __metadata: express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" express-session: "npm:^1.17.1" + ipaddr.js: "npm:^2.3.0" jose: "npm:^5.0.0" knex: "npm:^3.0.0" lodash: "npm:^4.17.21" luxon: "npm:^3.0.0" matcher: "npm:^4.0.0" - minimatch: "npm:^9.0.0" + minimatch: "npm:^10.2.1" passport: "npm:^0.7.0" uuid: "npm:^11.0.0" - checksum: 10c0/0a626a6515c3a57485aecc7059b73130263953a9760d2e6965d11ee1b35816e3666aed0726ede9d3d030368bb11cd20bf361f3ee5d032e0e080efe8c3921d847 + zod: "npm:^3.25.76 || ^4.0.0" + zod-validation-error: "npm:^5.0.0" + checksum: 10c0/ab260061c42c29719c1e422594fc2cf54dee1c960f2ca9282895660e73f5c4a910756c5137056dbea979931e2a8a8e45cf3454e2eb2bd4ac914b9706b143ceab languageName: node linkType: hard -"@backstage/plugin-auth-node@npm:^0.6.10, @backstage/plugin-auth-node@npm:^0.6.11, @backstage/plugin-auth-node@npm:^0.6.12": - version: 0.6.12 - resolution: "@backstage/plugin-auth-node@npm:0.6.12" +"@backstage/plugin-auth-node@npm:^0.6.14": + version: 0.6.14 + resolution: "@backstage/plugin-auth-node@npm:0.6.14" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" "@backstage/types": "npm:^1.2.2" @@ -2916,19 +3355,19 @@ __metadata: jose: "npm:^5.0.0" lodash: "npm:^4.17.21" passport: "npm:^0.7.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" zod-validation-error: "npm:^4.0.2" - checksum: 10c0/8bc9b9e09dee366ff9805281ef57c381029a617e9cbe32c493076c8969727fc0c0887abd6aefac13e10d60deefc7b604b7e94821f184b1ddbb7cb32f6b5c21d0 + checksum: 10c0/c78a40c7057e9819cd4303b6aca111a1b50b40c60e903c259b1ef346819650e91ffae5140388c1eece2ebbf8f9c0993143d2b4c61948e9ae4dba9fe91497daa2 languageName: node linkType: hard -"@backstage/plugin-auth-react@npm:^0.1.23": - version: 0.1.23 - resolution: "@backstage/plugin-auth-react@npm:0.1.23" +"@backstage/plugin-auth-react@npm:^0.1.25": + version: 0.1.25 + resolution: "@backstage/plugin-auth-react@npm:0.1.25" dependencies: - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" "@material-ui/core": "npm:^4.9.13" "@react-hookz/web": "npm:^24.0.0" @@ -2936,100 +3375,96 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/065388d94ef644efd803e53d4679f4a849fa305df40482c41a51e42587402494cca1d7357d41f7162bdd5917d80ce3c373bfa6b996ff161fd952b5a8ebfeb737 - languageName: node - linkType: hard - -"@backstage/plugin-bitbucket-cloud-common@npm:^0.3.6": - version: 0.3.6 - resolution: "@backstage/plugin-bitbucket-cloud-common@npm:0.3.6" - dependencies: - "@backstage/integration": "npm:^1.19.2" - cross-fetch: "npm:^4.0.0" - checksum: 10c0/56dd646f22685d12d822a66c97ea20dfbc577a49ea6877c21e5871c48cb7b9b9ab813c854c97cd26f7546ce24c9a4ad5ed8e6b67eba80fba5162f4860c62d5d3 + checksum: 10c0/401a35f629047a6205610380a329b96b66984ed8613fb557dac5f93e9637949150da69337597efa12d52ded1919b5c2c610660d77e3dcd7e075c53c661b9ddec languageName: node linkType: hard -"@backstage/plugin-catalog-backend-module-github-org@npm:^0.3.18": - version: 0.3.18 - resolution: "@backstage/plugin-catalog-backend-module-github-org@npm:0.3.18" +"@backstage/plugin-catalog-backend-module-github-org@npm:^0.3.20": + version: 0.3.20 + resolution: "@backstage/plugin-catalog-backend-module-github-org@npm:0.3.20" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" - "@backstage/plugin-catalog-backend-module-github": "npm:^0.12.1" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-events-node": "npm:^0.4.18" - checksum: 10c0/3158ce5575f17cdeb1023d2c38a67028e5ef278d8b3e8b9662090ba7969846981c26444fcf00abd9a8f726a9e7dcabe78099d57cae20c0bcde00a356543ef272 + "@backstage/plugin-catalog-backend-module-github": "npm:^0.13.0" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-events-node": "npm:^0.4.20" + checksum: 10c0/80ebf8e07656a1ef4a43eb2b6499cb12fa6c7619fcb116432337dd93fbbeb17cfebdceff5eaa430f96413dd3181f426fd39d51e3c87e4eccde1e7f344718f853 languageName: node linkType: hard -"@backstage/plugin-catalog-backend-module-github@npm:^0.12.1": - version: 0.12.1 - resolution: "@backstage/plugin-catalog-backend-module-github@npm:0.12.1" +"@backstage/plugin-catalog-backend-module-github@npm:^0.13.0": + version: 0.13.0 + resolution: "@backstage/plugin-catalog-backend-module-github@npm:0.13.0" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-events-node": "npm:^0.4.18" + "@backstage/errors": "npm:^1.2.7" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-events-node": "npm:^0.4.20" + "@backstage/types": "npm:^1.2.2" + "@octokit/auth-callback": "npm:^5.0.0" "@octokit/core": "npm:^5.2.0" "@octokit/graphql": "npm:^7.0.2" "@octokit/plugin-throttling": "npm:^8.1.3" "@octokit/rest": "npm:^19.0.3" + "@octokit/webhooks-types": "npm:^7.6.1" git-url-parse: "npm:^15.0.0" lodash: "npm:^4.17.21" - minimatch: "npm:^9.0.0" + minimatch: "npm:^10.2.1" + octokit: "npm:^3.0.0" uuid: "npm:^11.0.0" - checksum: 10c0/d226973f11b051628f4bbc32307055fb6f10a37cdf298588a6dc66eb9b1547e4140e8bfa2644b7763dc42f10a0882cdec2558c0cd198a93fe5b6df3e19707f70 + checksum: 10c0/d04002363cdb683e4d34c468d42da91b372d44578665c06425c6a64fa13c6206348fe0d7931198abe1418a8a985e671d166cfa963505057a6f7fd1a53b9df84a languageName: node linkType: hard -"@backstage/plugin-catalog-backend-module-logs@npm:^0.1.17": - version: 0.1.18 - resolution: "@backstage/plugin-catalog-backend-module-logs@npm:0.1.18" +"@backstage/plugin-catalog-backend-module-logs@npm:^0.1.20": + version: 0.1.20 + resolution: "@backstage/plugin-catalog-backend-module-logs@npm:0.1.20" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/plugin-catalog-backend": "npm:^3.3.1" - "@backstage/plugin-events-node": "npm:^0.4.18" - checksum: 10c0/647a5c1d058563fb40421afa531a87c166134523ed90ea9aaf528270869183f0bf274c7f304ae210197583a7f1f551636cfde80e1f61e9d3b38f27e864d5cc98 + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/plugin-catalog-backend": "npm:^3.5.0" + "@backstage/plugin-events-node": "npm:^0.4.20" + checksum: 10c0/d192204df463ec576c9da39812c54a15fdec5e49d08ea543ff23ad80c57c45d0c63a302eea3bc1eeaf3cae16897c3bb64f417209042a0b5c19163f389a89f1f4 languageName: node linkType: hard -"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.2.15, @backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.2.16": - version: 0.2.16 - resolution: "@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:0.2.16" +"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.2.18": + version: 0.2.18 + resolution: "@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:0.2.18" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-scaffolder-common": "npm:^1.7.5" - checksum: 10c0/db9a980f10b5ea5d27f242655ecaa14de5bbe8fe2496447f3b6a61c2fc0c457aab6711cfe80ebd1d9866d3a6c347965be265bfd7d5d9801e10997a464500f381 + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-scaffolder-common": "npm:^2.0.0" + checksum: 10c0/837ed4778461634e8d3cc23b13f2a2613c248b6597caa9a4a9abfb218a6cb02f1cdfaed83ced80336655f6cae85448f784c0099e50c29863db0cd2a05e48fd45 languageName: node linkType: hard -"@backstage/plugin-catalog-backend@npm:^3.3.0, @backstage/plugin-catalog-backend@npm:^3.3.1": - version: 3.3.2 - resolution: "@backstage/plugin-catalog-backend@npm:3.3.2" +"@backstage/plugin-catalog-backend@npm:^3.5.0": + version: 3.5.0 + resolution: "@backstage/plugin-catalog-backend@npm:3.5.0" dependencies: - "@backstage/backend-openapi-utils": "npm:^0.6.5" - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-openapi-utils": "npm:^0.6.7" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-events-node": "npm:^0.4.18" - "@backstage/plugin-permission-common": "npm:^0.9.5" - "@backstage/plugin-permission-node": "npm:^0.10.9" + "@backstage/filter-predicates": "npm:^0.1.1" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-events-node": "npm:^0.4.20" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" "@backstage/types": "npm:^1.2.2" "@opentelemetry/api": "npm:^1.9.0" codeowners-utils: "npm:^1.0.2" @@ -3042,14 +3477,15 @@ __metadata: knex: "npm:^3.0.0" lodash: "npm:^4.17.21" luxon: "npm:^3.0.0" - minimatch: "npm:^9.0.0" + minimatch: "npm:^10.2.1" p-limit: "npm:^3.0.2" prom-client: "npm:^15.0.0" uuid: "npm:^11.0.0" yaml: "npm:^2.0.0" yn: "npm:^4.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/a53d745e83ad112e0308acfde8367d93fe791b3b03aee0a74e1a618e62489bdfbb55d21453977a0c022c01c439459d11ed80d3f1eea7b8c0d8b372f962211eee + zod: "npm:^3.25.76 || ^4.0.0" + zod-validation-error: "npm:^4.0.2" + checksum: 10c0/661002ae3b2ac18c97355fe3414321805dff3be3a77f8c3f05cb9135b3008045ec296acedb307126e3ab2d778ce7c0461887c19a5ac69f5b09c4726a7354b434 languageName: node linkType: hard @@ -3064,20 +3500,33 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-graph@npm:^0.5.4": - version: 0.5.6 - resolution: "@backstage/plugin-catalog-graph@npm:0.5.6" +"@backstage/plugin-catalog-common@npm:^1.1.8": + version: 1.1.8 + resolution: "@backstage/plugin-catalog-common@npm:1.1.8" dependencies: - "@backstage/catalog-client": "npm:^1.12.1" "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-catalog-react": "npm:^1.21.5" + "@backstage/plugin-permission-common": "npm:^0.9.6" + "@backstage/plugin-search-common": "npm:^1.2.22" + checksum: 10c0/ff5182cd43eb0b2e4cd7237ffee29cb26cbda855144768c32385da5084f8ffa617dc3a0e2fbe13158993c24af00e9af361deaf87136ea4a56a1a596a0f8aed02 + languageName: node + linkType: hard + +"@backstage/plugin-catalog-graph@npm:^0.6.0": + version: 0.6.0 + resolution: "@backstage/plugin-catalog-graph@npm:0.6.0" + dependencies: + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog-react": "npm:^2.1.0" "@backstage/types": "npm:^1.2.2" + "@backstage/ui": "npm:^0.13.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" + "@remixicon/react": "npm:^4.6.0" classnames: "npm:^2.3.1" lodash: "npm:^4.17.15" p-limit: "npm:^3.1.0" @@ -3087,30 +3536,30 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/6cabbe0da7ad5a4ab6b3cad2fd249339ce31789197ac9792c57d683c0226a4aebe137664f281f7268e31f1f13e72f9e7bb58ea7a0870f0613e939e663ee6945d + checksum: 10c0/b90a2facf490cd59c12cc0dc3488817b9184a6605e5ad9514b558e7e4aa0d2979d99cf4e81ce01cea24d90a196847a896c1580899b6726ffc36a603e74ed04a1 languageName: node linkType: hard -"@backstage/plugin-catalog-import@npm:^0.13.8": - version: 0.13.9 - resolution: "@backstage/plugin-catalog-import@npm:0.13.9" +"@backstage/plugin-catalog-import@npm:^0.13.11": + version: 0.13.11 + resolution: "@backstage/plugin-catalog-import@npm:0.13.11" dependencies: - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/integration": "npm:^1.19.2" - "@backstage/integration-react": "npm:^1.2.14" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-permission-react": "npm:^0.4.39" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/integration": "npm:^2.0.0" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-permission-react": "npm:^0.4.41" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" @@ -3125,33 +3574,39 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/5a579d93cf1133101b16b33cc4eb097ed29778ea2c357021cb18444fde1e354c4e06bc49a7fcfb7c5a1934aab18dcf1a19e6a4806584890833720707c3fcb348 + checksum: 10c0/6c86489298e08fc36aa49ac796a5f81a80d190431caee9bc2ad14461fdf9e1197dfe02812e6eb873e516c3c4578fead5ee18578cd14be880482aa468ea6980e8 languageName: node linkType: hard -"@backstage/plugin-catalog-node@npm:^1.20.1": - version: 1.20.1 - resolution: "@backstage/plugin-catalog-node@npm:1.20.1" +"@backstage/plugin-catalog-node@npm:^2.1.0": + version: 2.1.0 + resolution: "@backstage/plugin-catalog-node@npm:2.1.0" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-permission-node": "npm:^0.10.7" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" "@backstage/types": "npm:^1.2.2" + "@opentelemetry/api": "npm:^1.9.0" lodash: "npm:^4.17.21" yaml: "npm:^2.0.0" - checksum: 10c0/820e84dbc072e83eac57173702baa50b373e17665997c148dcb8e61a499145d023209ad5a100c61d1a877eef4e847956dc88de113fd45a6566be508a5533d9b5 + peerDependencies: + "@backstage/backend-test-utils": ^1.11.1 + peerDependenciesMeta: + "@backstage/backend-test-utils": + optional: true + checksum: 10c0/1c4e122dbd418b31cf728a57a34766c24ad3bf331923b5df7213a61e69b90ae9b093635888c1a4a0c0184ca4771940f63072d7706ed871acdb73120b43306e5d languageName: node linkType: hard -"@backstage/plugin-catalog-react@npm:^1.21.2, @backstage/plugin-catalog-react@npm:^1.21.4, @backstage/plugin-catalog-react@npm:^1.21.5, @backstage/plugin-catalog-react@npm:^1.21.6": +"@backstage/plugin-catalog-react@npm:^1.21.2, @backstage/plugin-catalog-react@npm:^1.21.6": version: 1.21.6 resolution: "@backstage/plugin-catalog-react@npm:1.21.6" dependencies: @@ -3192,28 +3647,74 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog@npm:^1.32.1, @backstage/plugin-catalog@npm:^1.32.2": - version: 1.32.2 - resolution: "@backstage/plugin-catalog@npm:1.32.2" +"@backstage/plugin-catalog-react@npm:^2.1.0": + version: 2.1.0 + resolution: "@backstage/plugin-catalog-react@npm:2.1.0" dependencies: - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-compat-api": "npm:^0.5.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-compat-api": "npm:^0.5.9" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/integration-react": "npm:^1.2.14" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@backstage/plugin-scaffolder-common": "npm:^1.7.5" - "@backstage/plugin-search-common": "npm:^1.2.21" - "@backstage/plugin-search-react": "npm:^1.10.2" + "@backstage/filter-predicates": "npm:^0.1.1" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/types": "npm:^1.2.2" + "@backstage/ui": "npm:^0.13.0" + "@backstage/version-bridge": "npm:^1.0.12" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@react-hookz/web": "npm:^24.0.0" + classnames: "npm:^2.2.6" + lodash: "npm:^4.17.21" + material-ui-popup-state: "npm:^5.3.6" + qs: "npm:^6.9.4" + react-use: "npm:^17.2.4" + yaml: "npm:^2.0.0" + zen-observable: "npm:^0.10.0" + peerDependencies: + "@backstage/frontend-test-utils": ^0.5.1 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@backstage/frontend-test-utils": + optional: true + "@types/react": + optional: true + checksum: 10c0/3c73017e8040a33fa1c88c4db70c6ae1890cfe759eb62f488d60909c71e1400b79740e53c198d27a2f7a65078965cf01c755af172af996511483e10ffbfc4774 + languageName: node + linkType: hard + +"@backstage/plugin-catalog@npm:^2.0.0, @backstage/plugin-catalog@npm:^2.0.1": + version: 2.0.1 + resolution: "@backstage/plugin-catalog@npm:2.0.1" + dependencies: + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-compat-api": "npm:^0.5.9" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/errors": "npm:^1.2.7" + "@backstage/frontend-plugin-api": "npm:^0.15.1" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/plugin-scaffolder-common": "npm:^2.0.0" + "@backstage/plugin-search-common": "npm:^1.2.22" + "@backstage/plugin-search-react": "npm:^1.11.0" "@backstage/plugin-techdocs-common": "npm:^0.1.1" - "@backstage/plugin-techdocs-react": "npm:^1.3.7" + "@backstage/plugin-techdocs-react": "npm:^1.3.9" "@backstage/types": "npm:^1.2.2" - "@backstage/version-bridge": "npm:^1.0.11" + "@backstage/ui": "npm:^0.13.1" + "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" @@ -3230,19 +3731,19 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/6539c80840c3e1a6a1cc6cd87b1c1f1ecd5c6a9e3b7e216f17a956aedb4a691ccc1cdc0911b03c7ad22029754346c13fee6ac23bdf8761760d3b079505dc8cd9 + checksum: 10c0/b9c044ae699c63f6305a9d04741eccefbe7e3e0abd8dac2ca6bb5019697b04efd0b81399d57a85e08b4cc5aed758ecf8ad53f810a69688bdba05e6f9ffe6b8f0 languageName: node linkType: hard -"@backstage/plugin-events-node@npm:^0.4.18": - version: 0.4.18 - resolution: "@backstage/plugin-events-node@npm:0.4.18" +"@backstage/plugin-events-node@npm:^0.4.20": + version: 0.4.20 + resolution: "@backstage/plugin-events-node@npm:0.4.20" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/errors": "npm:^1.2.7" "@backstage/types": "npm:^1.2.2" "@types/content-type": "npm:^1.1.8" @@ -3251,28 +3752,28 @@ __metadata: cross-fetch: "npm:^4.0.0" express: "npm:^4.22.0" uri-template: "npm:^2.0.0" - checksum: 10c0/65d4cd88ea693c34897025f2d97390393effc712f0493bc2b43845e984e4cdffe887523a563d08fb8046f3b5454ae20f8921f6286dd017d26b7213f4f24342ce + checksum: 10c0/54970736abd2ed621645044554bed2d888f3e6741bb492851d50bd96e5d19870d079854eefb28aafd5fcdad1a39adb1e810fb1d4002d723d44940de8b85eca94 languageName: node linkType: hard -"@backstage/plugin-kubernetes-backend@npm:^0.21.0": - version: 0.21.0 - resolution: "@backstage/plugin-kubernetes-backend@npm:0.21.0" +"@backstage/plugin-kubernetes-backend@npm:^0.21.2": + version: 0.21.2 + resolution: "@backstage/plugin-kubernetes-backend@npm:0.21.2" dependencies: "@aws-crypto/sha256-js": "npm:^5.0.0" "@aws-sdk/credential-providers": "npm:^3.350.0" "@azure/identity": "npm:^4.0.0" - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration-aws-node": "npm:^0.1.19" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-kubernetes-common": "npm:^0.9.9" - "@backstage/plugin-kubernetes-node": "npm:^0.4.0" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-permission-node": "npm:^0.10.7" + "@backstage/integration-aws-node": "npm:^0.1.20" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-kubernetes-common": "npm:^0.9.10" + "@backstage/plugin-kubernetes-node": "npm:^0.4.2" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" "@backstage/types": "npm:^1.2.2" "@google-cloud/container": "npm:^5.0.0" "@jest-mock/express": "npm:^2.0.1" @@ -3286,7 +3787,22 @@ __metadata: lodash: "npm:^4.17.21" luxon: "npm:^3.0.0" node-fetch: "npm:^2.7.0" - checksum: 10c0/17362ea71600e8641782f88fa5200f702239be70630a40e7aa9dee2970cdeac457ebf49f89dd1eb16b8970072c5246e24806f8e3c28c32e182602e4c043dd0d2 + checksum: 10c0/6c60f24c753a68bb6a9cd250585608c03842c43a8cb04f787880828d3e77eb567d90b365314864140d8799a8cfb56282fa795cfe2258c5c521c37cc4955ff4cb + languageName: node + linkType: hard + +"@backstage/plugin-kubernetes-common@npm:^0.9.10": + version: 0.9.10 + resolution: "@backstage/plugin-kubernetes-common@npm:0.9.10" + dependencies: + "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/plugin-permission-common": "npm:^0.9.6" + "@backstage/types": "npm:^1.2.2" + "@kubernetes/client-node": "npm:1.4.0" + kubernetes-models: "npm:^4.3.1" + lodash: "npm:^4.17.21" + luxon: "npm:^3.0.0" + checksum: 10c0/d5dad4324567a316ae73c523fc5d87c9cbfb866c2dd65c0385a376271f8174ad647db6092d92ac9d8b1cd687730fa395d113e828fa1f3fbb930b04fc2eab4e24 languageName: node linkType: hard @@ -3305,19 +3821,19 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-kubernetes-node@npm:^0.4.0": - version: 0.4.0 - resolution: "@backstage/plugin-kubernetes-node@npm:0.4.0" +"@backstage/plugin-kubernetes-node@npm:^0.4.2": + version: 0.4.2 + resolution: "@backstage/plugin-kubernetes-node@npm:0.4.2" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/plugin-kubernetes-common": "npm:^0.9.9" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/plugin-kubernetes-common": "npm:^0.9.10" "@backstage/types": "npm:^1.2.2" "@kubernetes/client-node": "npm:1.4.0" "@types/express": "npm:^4.17.6" node-fetch: "npm:^2.7.0" winston: "npm:^3.2.1" - checksum: 10c0/213151880be577320b3f854b70cdd1499f52aa4caafdd598a9a98595f23e308dcb4dc82d7fdfac65965c65b8d2c419cb0c6c10744ed912e7dec21e939a40e72f + checksum: 10c0/42bd7bd9a416b135c6c74c1fe42cd8ebe7f37c43fa37b85af04c76051c9826e5c75674e85b8545a7aa466b5a0fb0972d0983a592e63d2b39275d9fae0d4e258c languageName: node linkType: hard @@ -3358,90 +3874,128 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-kubernetes@npm:^0.12.14": - version: 0.12.15 - resolution: "@backstage/plugin-kubernetes@npm:0.12.15" +"@backstage/plugin-kubernetes-react@npm:^0.5.17": + version: 0.5.17 + resolution: "@backstage/plugin-kubernetes-react@npm:0.5.17" dependencies: - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-kubernetes-common": "npm:^0.9.9" - "@backstage/plugin-kubernetes-react": "npm:^0.5.15" - "@backstage/plugin-permission-react": "npm:^0.4.39" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/errors": "npm:^1.2.7" + "@backstage/plugin-kubernetes-common": "npm:^0.9.10" + "@backstage/types": "npm:^1.2.2" + "@kubernetes-models/apimachinery": "npm:^2.0.0" + "@kubernetes-models/base": "npm:^5.0.0" + "@kubernetes/client-node": "npm:1.4.0" + "@material-ui/core": "npm:^4.9.13" + "@material-ui/icons": "npm:^4.11.3" + "@material-ui/lab": "npm:^4.0.0-alpha.61" + "@xterm/addon-attach": "npm:^0.12.0" + "@xterm/addon-fit": "npm:^0.11.0" + "@xterm/xterm": "npm:^5.5.0" + cronstrue: "npm:^2.32.0" + js-yaml: "npm:^4.1.0" + kubernetes-models: "npm:^4.3.1" + lodash: "npm:^4.17.21" + luxon: "npm:^3.0.0" + react-use: "npm:^17.4.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/c6072183485157e41641e928229909ed3f992a7e97b9bfff06ceb8334be31fcbd005f491dac43f3909c06caf2b296d997cfc2e8d8de7fddc4ac869d795d3e5a3 + languageName: node + linkType: hard + +"@backstage/plugin-kubernetes@npm:^0.12.17": + version: 0.12.17 + resolution: "@backstage/plugin-kubernetes@npm:0.12.17" + dependencies: + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-kubernetes-common": "npm:^0.9.10" + "@backstage/plugin-kubernetes-react": "npm:^0.5.17" + "@backstage/plugin-permission-react": "npm:^0.4.41" "@material-ui/core": "npm:^4.12.2" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/08a6f48763162c344459f229e5bdfbccd08328e66ad656170a8a241195b3ced81cf6b0013dd6038328b6ae70f30a5283ab5b8f6fa0f83da2d9eeafa3ae8c21cb + checksum: 10c0/16f1b7ae584f619306a42da9c841c5e0428990919e6931856d100a06eedc9f6b510812a416942d96c03f7e823725187007f3c09dc2f2fe68ee6077d2084fffb7 languageName: node linkType: hard -"@backstage/plugin-notifications-backend@npm:^0.6.1": - version: 0.6.1 - resolution: "@backstage/plugin-notifications-backend@npm:0.6.1" +"@backstage/plugin-notifications-backend@npm:^0.6.3": + version: 0.6.3 + resolution: "@backstage/plugin-notifications-backend@npm:0.6.3" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-notifications-common": "npm:^0.2.0" - "@backstage/plugin-notifications-node": "npm:^0.2.22" - "@backstage/plugin-signals-node": "npm:^0.1.27" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-notifications-common": "npm:^0.2.1" + "@backstage/plugin-notifications-node": "npm:^0.2.24" + "@backstage/plugin-signals-node": "npm:^0.1.29" "@backstage/types": "npm:^1.2.2" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" knex: "npm:^3.0.0" p-throttle: "npm:^4.1.1" uuid: "npm:^11.0.0" - checksum: 10c0/1eca036fb9eaa7727e8d1ceb01fabee514cce596de865edc0bd7ed65abd837047397dcda724baa04501cd2c51d6542a174f9e705c8bce68a648829a2394f419b + checksum: 10c0/4651da1b2ab92512c9363ba5b159ef688efe9713666505419b32504bf96794919706635bba7de759e385b67060ea4faca885e0c8ad166bf4d7f17ea7d8fcce64 languageName: node linkType: hard -"@backstage/plugin-notifications-common@npm:^0.2.0": - version: 0.2.0 - resolution: "@backstage/plugin-notifications-common@npm:0.2.0" +"@backstage/plugin-notifications-common@npm:^0.2.1": + version: 0.2.1 + resolution: "@backstage/plugin-notifications-common@npm:0.2.1" dependencies: "@backstage/config": "npm:^1.3.6" "@backstage/types": "npm:^1.2.2" "@material-ui/icons": "npm:^4.9.1" - checksum: 10c0/c1a390f59d396d8fd8b0f186d7327a45da636aa6a5ac52c869d122a22c17ec88c74ee85032b23bb20e4ede232f3b1b2be56c2d64e11480d0c357ae46c994b5e4 + checksum: 10c0/f60093c8fceb414b36457a799918d364ac89af55b4a2ed98babee2a8913a2a1fdf70bbef204a64bdcff7f0336903e7473692f69e6ae934730595346be52e8f10 languageName: node linkType: hard -"@backstage/plugin-notifications-node@npm:^0.2.22": - version: 0.2.22 - resolution: "@backstage/plugin-notifications-node@npm:0.2.22" +"@backstage/plugin-notifications-node@npm:^0.2.24": + version: 0.2.24 + resolution: "@backstage/plugin-notifications-node@npm:0.2.24" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/plugin-notifications-common": "npm:^0.2.0" - "@backstage/plugin-signals-node": "npm:^0.1.27" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/plugin-notifications-common": "npm:^0.2.1" + "@backstage/plugin-signals-node": "npm:^0.1.29" knex: "npm:^3.0.0" uuid: "npm:^11.0.0" - checksum: 10c0/77f7c87f6b899beef150f1743e93de8d7a64a442356d2484e5f348bedce4eb148d80cc99a48209c33731a087e262c17eb8fe9d3eb3e48d630e957e1aa546e4d4 + checksum: 10c0/f374045293ed32208d8d518e77f71aab6567daa7bda10cd1a6060208fe47e46ff1bb133c36724e34baa57792a5ceb6370f135c63dcb6996362913998441bd7b2 languageName: node linkType: hard -"@backstage/plugin-notifications@npm:^0.5.12": - version: 0.5.13 - resolution: "@backstage/plugin-notifications@npm:0.5.13" +"@backstage/plugin-notifications@npm:^0.5.15": + version: 0.5.15 + resolution: "@backstage/plugin-notifications@npm:0.5.15" dependencies: - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-notifications-common": "npm:^0.2.0" - "@backstage/plugin-signals-react": "npm:^0.0.18" - "@backstage/theme": "npm:^0.7.1" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-notifications-common": "npm:^0.2.1" + "@backstage/plugin-signals-react": "npm:^0.0.20" + "@backstage/theme": "npm:^0.7.2" + "@backstage/ui": "npm:^0.13.0" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" lodash: "npm:^4.17.21" @@ -3453,24 +4007,25 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/712d36180461bcc75ccca67878700c4787eae426d78c6442781972008dc0558abc2136d630d425e1aacd85190d05e9c5680a4a7902ac19c534bb3a26e402ac85 + checksum: 10c0/e53e5a278c3bcc1a00e801b365b47d922828dde2b6c9dfdc1ba033570e0b954fcf32a2852e552bacb038a0b145e9af3f81872f30eb67e77a96f30db043e393f7 languageName: node linkType: hard -"@backstage/plugin-org@npm:^0.6.47": - version: 0.6.48 - resolution: "@backstage/plugin-org@npm:0.6.48" - dependencies: - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-react": "npm:^1.21.5" +"@backstage/plugin-org@npm:^0.7.0": + version: 0.7.0 + resolution: "@backstage/plugin-org@npm:0.7.0" + dependencies: + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/ui": "npm:^0.13.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" @@ -3483,47 +4038,47 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/715dc4ace599f7de91541f02eede25e58300e0169394324aad85e9680a15f5108ed1bab9e441e7b1e557063b44aee496a92c719f164754274c4f8da3a4398775 + checksum: 10c0/d40e4532b78013f358a1e16fce056a7f9b38ecbe0152cdd120b9ef79775167fe03b5a645815741d6ef213670c87259d13f03786828639b34122a4e426a4b3003 languageName: node linkType: hard -"@backstage/plugin-permission-backend-module-allow-all-policy@npm:^0.2.15": - version: 0.2.15 - resolution: "@backstage/plugin-permission-backend-module-allow-all-policy@npm:0.2.15" +"@backstage/plugin-permission-backend-module-allow-all-policy@npm:^0.2.17": + version: 0.2.17 + resolution: "@backstage/plugin-permission-backend-module-allow-all-policy@npm:0.2.17" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/plugin-auth-node": "npm:^0.6.10" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-permission-node": "npm:^0.10.7" - checksum: 10c0/b2ca39b9f78c7c70372ff81abb0688929db0e2be4cdbda3eedc35279f626fe78a497ce636a40865ac19f487649543f15b552b5317e5d775dd21776a3228914d5 + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" + checksum: 10c0/7acf5460734731dad68c9525d453b8ceb6bf5e25a266459d787dd527bed1df4929d98862d9fd9886c5c29f1bc9bbdc09e27653ed6b74f7503ab7ece7f824c711 languageName: node linkType: hard -"@backstage/plugin-permission-backend@npm:^0.7.7": - version: 0.7.8 - resolution: "@backstage/plugin-permission-backend@npm:0.7.8" +"@backstage/plugin-permission-backend@npm:^0.7.10": + version: 0.7.10 + resolution: "@backstage/plugin-permission-backend@npm:0.7.10" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.12" - "@backstage/plugin-permission-common": "npm:^0.9.5" - "@backstage/plugin-permission-node": "npm:^0.10.9" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" dataloader: "npm:^2.0.0" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" lodash: "npm:^4.17.21" yn: "npm:^4.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/3ffd2af8bdec1e0d72f54b2bdce08b2fe299366627c5a10f78179ba32fe74731f8b97629de6523cc838a19fedaa9226d96718e17a3976e173f646c4e4cb979bb + zod: "npm:^3.25.76 || ^4.0.0" + checksum: 10c0/f01f610860e3ae46ce1b57a911639bf810bd298c82960c19431a8d5843feebde6f06231bc393fed9d928f1bd2b76b82b2dabafa8279ee9858673874345d8aa00 languageName: node linkType: hard -"@backstage/plugin-permission-common@npm:^0.9.3, @backstage/plugin-permission-common@npm:^0.9.4, @backstage/plugin-permission-common@npm:^0.9.5": +"@backstage/plugin-permission-common@npm:^0.9.3, @backstage/plugin-permission-common@npm:^0.9.5": version: 0.9.5 resolution: "@backstage/plugin-permission-common@npm:0.9.5" dependencies: @@ -3538,21 +4093,36 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-permission-node@npm:^0.10.7, @backstage/plugin-permission-node@npm:^0.10.9": - version: 0.10.9 - resolution: "@backstage/plugin-permission-node@npm:0.10.9" +"@backstage/plugin-permission-common@npm:^0.9.6, @backstage/plugin-permission-common@npm:^0.9.7": + version: 0.9.7 + resolution: "@backstage/plugin-permission-common@npm:0.9.7" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.2" + cross-fetch: "npm:^4.0.0" + uuid: "npm:^11.0.0" + zod: "npm:^3.25.76 || ^4.0.0" + zod-to-json-schema: "npm:^3.25.1" + checksum: 10c0/577c15e246fc46c7fe1a4868c3765a9c837b86a64d7a3ab75ac76ed443f2ac4e687c37bd11f2e787898ff8909c9206b9285446e956eb9c4313d3818e37fbf64d + languageName: node + linkType: hard + +"@backstage/plugin-permission-node@npm:^0.10.11": + version: 0.10.11 + resolution: "@backstage/plugin-permission-node@npm:0.10.11" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.12" - "@backstage/plugin-permission-common": "npm:^0.9.5" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-permission-common": "npm:^0.9.7" "@types/express": "npm:^4.17.6" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" - checksum: 10c0/c1c0b8245c8877b2720849676c0dfeb3452caeb52ec2f9b1ef122013cf36f58dbce7ea249bb68204aea83aa757435b5f6d730c434e737478b11b0d38191ed20f + checksum: 10c0/9aa5bec48bf768ea2252376a6fb4f408b5076d7534205cafe25233b104a2d1d625aed5cf173dc73616076e54bc7878065274859e6e81d5d039a424bcb487eea0 languageName: node linkType: hard @@ -3576,207 +4146,104 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-proxy-backend@npm:^0.6.9": - version: 0.6.9 - resolution: "@backstage/plugin-proxy-backend@npm:0.6.9" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/plugin-proxy-node": "npm:^0.1.11" - "@backstage/types": "npm:^1.2.2" - express-promise-router: "npm:^4.1.0" - http-proxy-middleware: "npm:^2.0.0" - checksum: 10c0/8c31d435caa55054f0a0c62e475ab11f66fe8d8f061c0d7e3a3920f3715df743035f002a04a9d1d972f66b10cc15b298c5a9f9e47d37807a693bb6f3ce405d2a - languageName: node - linkType: hard - -"@backstage/plugin-proxy-node@npm:^0.1.11": - version: 0.1.11 - resolution: "@backstage/plugin-proxy-node@npm:0.1.11" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - http-proxy-middleware: "npm:^2.0.0" - checksum: 10c0/4278d17a10e26701d117359d5bd27e6478cf946a0e61ffa071f5b724678dc404b4ae7519d1da967ac232f9eb73a704298e681845e22e0928ff3fb011bef0a456 - languageName: node - linkType: hard - -"@backstage/plugin-scaffolder-backend-module-azure@npm:^0.2.17": - version: 0.2.17 - resolution: "@backstage/plugin-scaffolder-backend-module-azure@npm:0.2.17" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-scaffolder-node": "npm:^0.12.3" - azure-devops-node-api: "npm:^14.0.0" - yaml: "npm:^2.0.0" - checksum: 10c0/bf4c6a39ad648776d5209a1732ccbfba948c5e55ebfe65f0ab08f0d023d8690c4f800bffda17d7c7d46a933c1e28c05ff2adcb0f1d19cae0d3c48c940770b559 - languageName: node - linkType: hard - -"@backstage/plugin-scaffolder-backend-module-bitbucket-cloud@npm:^0.3.1, @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@npm:^0.3.2": - version: 0.3.2 - resolution: "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud@npm:0.3.2" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-bitbucket-cloud-common": "npm:^0.3.6" - "@backstage/plugin-scaffolder-node": "npm:^0.12.4" - bitbucket: "npm:^2.12.0" - fs-extra: "npm:^11.2.0" - yaml: "npm:^2.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/43363baff16bf08e58ec47e8ff984387be9b3cd0a3cc1ac8b96ecf9b5d50e6f3ebd0ca75bc847eebae77d9b3943a5ad46def397679afc42f0a2d176a6296a338 - languageName: node - linkType: hard - -"@backstage/plugin-scaffolder-backend-module-bitbucket-server@npm:^0.2.17": - version: 0.2.17 - resolution: "@backstage/plugin-scaffolder-backend-module-bitbucket-server@npm:0.2.17" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-scaffolder-node": "npm:^0.12.3" - fs-extra: "npm:^11.2.0" - yaml: "npm:^2.0.0" - checksum: 10c0/80f0d82d6eafbcfb94bac04c9da928d119c7f8b57609c42cca8e1067e7fe885725e72fe31827dc18f91213e717107df53afe57293717dcab0db3fa168e2ba7dd - languageName: node - linkType: hard - -"@backstage/plugin-scaffolder-backend-module-bitbucket@npm:^0.3.18": - version: 0.3.18 - resolution: "@backstage/plugin-scaffolder-backend-module-bitbucket@npm:0.3.18" +"@backstage/plugin-permission-react@npm:^0.4.41": + version: 0.4.41 + resolution: "@backstage/plugin-permission-react@npm:0.4.41" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "npm:^0.3.1" - "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "npm:^0.2.17" - "@backstage/plugin-scaffolder-node": "npm:^0.12.3" - fs-extra: "npm:^11.2.0" - yaml: "npm:^2.0.0" - checksum: 10c0/cc58e80f31b3ba5f34b299be5477b18a9291eb4583cc89117ace85175faa9895a4633315c1bd4039ce2a59f97a156838d3a873948b30c1a041f23bb7ce1e610f + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/plugin-permission-common": "npm:^0.9.7" + dataloader: "npm:^2.0.0" + swr: "npm:^2.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/d04a28f02cd98a0415f0ccc21b755fb88190bf3b1cf61c6f48086f4823d674ac25152fa14a5b273ae4232617e514334198efab670daae06aaa07b5ff1be7f4a6 languageName: node linkType: hard -"@backstage/plugin-scaffolder-backend-module-gerrit@npm:^0.2.17": - version: 0.2.17 - resolution: "@backstage/plugin-scaffolder-backend-module-gerrit@npm:0.2.17" +"@backstage/plugin-proxy-backend@npm:^0.6.11": + version: 0.6.11 + resolution: "@backstage/plugin-proxy-backend@npm:0.6.11" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-scaffolder-node": "npm:^0.12.3" - yaml: "npm:^2.0.0" - checksum: 10c0/31e4654fb19011bc9e10c4f724fc6558825c9f9e1f84f29a5ba750619155addb98c86b2d2ca2f223c91707875e88b76ddb09810c952c62540c94b5a76881c584 + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/plugin-proxy-node": "npm:^0.1.13" + "@backstage/types": "npm:^1.2.2" + express-promise-router: "npm:^4.1.0" + http-proxy-middleware: "npm:^2.0.0" + checksum: 10c0/b4c82f005d3cfa702f2e479eddc50707cfe500891273b5f4cd13365436cd9245dde739e6e92c731439d9d62bcba49e5074db00a7ff7299812329e84f12424c7c languageName: node linkType: hard -"@backstage/plugin-scaffolder-backend-module-gitea@npm:^0.2.17": - version: 0.2.17 - resolution: "@backstage/plugin-scaffolder-backend-module-gitea@npm:0.2.17" +"@backstage/plugin-proxy-node@npm:^0.1.13": + version: 0.1.13 + resolution: "@backstage/plugin-proxy-node@npm:0.1.13" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-scaffolder-node": "npm:^0.12.3" - yaml: "npm:^2.0.0" - checksum: 10c0/690eca2f9373c38b218d0a3b69a74999b445477ff513f17fafbfa449b95881345a0fbad87ec064a3de12b29aec293ce2e993c7bf000b00cb175d1f69dbf4722f + "@backstage/backend-plugin-api": "npm:^1.8.0" + http-proxy-middleware: "npm:^2.0.0" + checksum: 10c0/587ad9d975048b5e483fb8eaa373661e84e890f2b113e728d477e712d8f892909637ed21d6eebe0369d3e27a56d1583f81d55c7667b99abe17f47097f6933e8f languageName: node linkType: hard -"@backstage/plugin-scaffolder-backend-module-github@npm:^0.9.3, @backstage/plugin-scaffolder-backend-module-github@npm:^0.9.5": - version: 0.9.5 - resolution: "@backstage/plugin-scaffolder-backend-module-github@npm:0.9.5" +"@backstage/plugin-scaffolder-backend-module-github@npm:^0.9.7": + version: 0.9.7 + resolution: "@backstage/plugin-scaffolder-backend-module-github@npm:0.9.7" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-scaffolder-node": "npm:^0.12.4" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-scaffolder-node": "npm:^0.13.0" "@backstage/types": "npm:^1.2.2" "@octokit/webhooks": "npm:^10.9.2" - libsodium-wrappers: "npm:^0.7.11" + libsodium-wrappers: "npm:^0.8.0" octokit: "npm:^3.0.0" octokit-plugin-create-pull-request: "npm:^5.0.0" yaml: "npm:^2.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/35abdba5bf6dc88f95f8fffe05ce8009e9ddd72afab80cfc426c5637e41710cb2c75fad7403edcf1b261000b0e7426da5a42712534a10cf762e659d7c61cb0df - languageName: node - linkType: hard - -"@backstage/plugin-scaffolder-backend-module-gitlab@npm:^0.11.2": - version: 0.11.2 - resolution: "@backstage/plugin-scaffolder-backend-module-gitlab@npm:0.11.2" - dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/config": "npm:^1.3.6" - "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-scaffolder-node": "npm:^0.12.4" - "@gitbeaker/requester-utils": "npm:^41.2.0" - "@gitbeaker/rest": "npm:^41.2.0" - luxon: "npm:^3.0.0" - yaml: "npm:^2.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/ddfbd0f8bc1db4b7e56726c108f8b0ec9c476a3c781e5c7d9ce121c8844b24363c51afc89415e0496000d86094145335a972d6f5aa6d88d02cc0b006d14341fb + zod: "npm:^3.25.76 || ^4.0.0" + checksum: 10c0/b69c3bad1b4a83345668c731c2c36fcf376bfee18c031e4aeec2cb767a2604491fc99d3ccdd5a0355119cbd711dca3800538dea71c879bdf59ed01466f009147 languageName: node linkType: hard -"@backstage/plugin-scaffolder-backend-module-notifications@npm:^0.1.17": - version: 0.1.18 - resolution: "@backstage/plugin-scaffolder-backend-module-notifications@npm:0.1.18" +"@backstage/plugin-scaffolder-backend-module-notifications@npm:^0.1.20": + version: 0.1.20 + resolution: "@backstage/plugin-scaffolder-backend-module-notifications@npm:0.1.20" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/plugin-notifications-common": "npm:^0.2.0" - "@backstage/plugin-notifications-node": "npm:^0.2.22" - "@backstage/plugin-scaffolder-node": "npm:^0.12.3" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/plugin-notifications-common": "npm:^0.2.1" + "@backstage/plugin-notifications-node": "npm:^0.2.24" + "@backstage/plugin-scaffolder-node": "npm:^0.13.0" yaml: "npm:^2.0.0" - checksum: 10c0/130b0f3de82f028ab4b4011472a3bfdd5f7128d2d6d99920932b58816a6ec2535924fcfec4b06b29a108996ad29a6eee4469ac6ee6ad2027f9d608e1707d9675 + checksum: 10c0/e2861a956fc91d4f1632d01eca01edee549ba0008912990832f62d59db10dda5923962c1ae7fa8cf034085629f5e2e53627044f9aea4172cba01ff84eb3845da languageName: node linkType: hard -"@backstage/plugin-scaffolder-backend@npm:^3.1.0": - version: 3.1.2 - resolution: "@backstage/plugin-scaffolder-backend@npm:3.1.2" +"@backstage/plugin-scaffolder-backend@npm:^3.2.0": + version: 3.2.0 + resolution: "@backstage/plugin-scaffolder-backend@npm:3.2.0" dependencies: - "@backstage/backend-defaults": "npm:^0.15.1" - "@backstage/backend-openapi-utils": "npm:^0.6.5" - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-openapi-utils": "npm:^0.6.7" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-auth-node": "npm:^0.6.12" - "@backstage/plugin-bitbucket-cloud-common": "npm:^0.3.6" - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.2.16" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-events-node": "npm:^0.4.18" - "@backstage/plugin-permission-common": "npm:^0.9.5" - "@backstage/plugin-permission-node": "npm:^0.10.9" - "@backstage/plugin-scaffolder-backend-module-azure": "npm:^0.2.17" - "@backstage/plugin-scaffolder-backend-module-bitbucket": "npm:^0.3.18" - "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "npm:^0.3.2" - "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "npm:^0.2.17" - "@backstage/plugin-scaffolder-backend-module-gerrit": "npm:^0.2.17" - "@backstage/plugin-scaffolder-backend-module-gitea": "npm:^0.2.17" - "@backstage/plugin-scaffolder-backend-module-github": "npm:^0.9.5" - "@backstage/plugin-scaffolder-backend-module-gitlab": "npm:^0.11.2" - "@backstage/plugin-scaffolder-common": "npm:^1.7.5" - "@backstage/plugin-scaffolder-node": "npm:^0.12.4" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-events-node": "npm:^0.4.20" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" + "@backstage/plugin-scaffolder-common": "npm:^2.0.0" + "@backstage/plugin-scaffolder-node": "npm:^0.13.0" "@backstage/types": "npm:^1.2.2" "@opentelemetry/api": "npm:^1.9.0" "@types/luxon": "npm:^3.0.0" - concat-stream: "npm:^2.0.0" express: "npm:^4.22.0" fs-extra: "npm:^11.2.0" globby: "npm:^11.0.0" @@ -3788,30 +4255,28 @@ __metadata: logform: "npm:^2.3.2" luxon: "npm:^3.0.0" nunjucks: "npm:^3.2.3" - p-limit: "npm:^3.1.0" p-queue: "npm:^6.6.2" prom-client: "npm:^15.0.0" - tar: "npm:^6.1.12" triple-beam: "npm:^1.4.1" uuid: "npm:^11.0.0" winston: "npm:^3.2.1" winston-transport: "npm:^4.7.0" yaml: "npm:^2.0.0" zen-observable: "npm:^0.10.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" - checksum: 10c0/f8d6da3a7e5e5fbbe1ffb3f168a57bd8cf11282ae0121405d4045d7af02156b017c0e5f6b746f190263469db99409f23c83329861b70416c111b71cd4e3a2047 + checksum: 10c0/c06fe90c40b1b4c669f321da7e7a6dd978aceec99c493d1d82c4dcefb0c2d0d181609b184debf1b60f5cec84e20a8e0f91bf0c3d533a226b0afab772583e5fe7 languageName: node linkType: hard -"@backstage/plugin-scaffolder-common@npm:^1.7.5": - version: 1.7.5 - resolution: "@backstage/plugin-scaffolder-common@npm:1.7.5" +"@backstage/plugin-scaffolder-common@npm:^2.0.0": + version: 2.0.0 + resolution: "@backstage/plugin-scaffolder-common@npm:2.0.0" dependencies: - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-permission-common": "npm:^0.9.4" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-permission-common": "npm:^0.9.7" "@backstage/types": "npm:^1.2.2" "@microsoft/fetch-event-source": "npm:^2.0.1" "@types/json-schema": "npm:^7.0.9" @@ -3819,20 +4284,20 @@ __metadata: json-schema: "npm:^0.4.0" uri-template: "npm:^2.0.0" zen-observable: "npm:^0.10.0" - checksum: 10c0/8d6c589aa93cc954b793aa70f27ebe14d566656704b201718008d9487d2e5657635b691e0bed68bc746054c457849aa905fd418daed94b058597f35a0af71742 + checksum: 10c0/441d16cc5cc1800bbee5fa791e8a72314a9df9e15f19ed58ffbff92d837f125192ba2c89953d4846ac9da8ac040cfef18f8cacb3fc4e9332232cd44df1d86963 languageName: node linkType: hard -"@backstage/plugin-scaffolder-node@npm:^0.12.3, @backstage/plugin-scaffolder-node@npm:^0.12.4": - version: 0.12.4 - resolution: "@backstage/plugin-scaffolder-node@npm:0.12.4" +"@backstage/plugin-scaffolder-node@npm:^0.13.0": + version: 0.13.0 + resolution: "@backstage/plugin-scaffolder-node@npm:0.13.0" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-permission-common": "npm:^0.9.5" - "@backstage/plugin-scaffolder-common": "npm:^1.7.5" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-scaffolder-common": "npm:^2.0.0" "@backstage/types": "npm:^1.2.2" "@isomorphic-git/pgp-plugin": "npm:^0.0.7" concat-stream: "npm:^2.0.0" @@ -3842,30 +4307,35 @@ __metadata: jsonschema: "npm:^1.5.0" lodash: "npm:^4.17.21" p-limit: "npm:^3.1.0" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" winston: "npm:^3.2.1" winston-transport: "npm:^4.7.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" - checksum: 10c0/98f96505b42413defdbce53d8c0b1cca2d5d867755d71f5584af3271f4fcf34b506d7e1bef421a6cc5d5d157de4fd9dffc740bec30e4a776b5a99244c678acbb + peerDependencies: + "@backstage/backend-test-utils": ^1.11.1 + peerDependenciesMeta: + "@backstage/backend-test-utils": + optional: true + checksum: 10c0/13432e89adbfe5efc82211643b66eda6e08075b5983abb8ad88d5901e8fc9f3825f690631953c3865ec5dd4f2ee3149cc10464c2b57436df337a2b1743df61f0 languageName: node linkType: hard -"@backstage/plugin-scaffolder-react@npm:^1.19.6": - version: 1.19.6 - resolution: "@backstage/plugin-scaffolder-react@npm:1.19.6" - dependencies: - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-components": "npm:^0.18.6" - "@backstage/core-plugin-api": "npm:^1.12.2" - "@backstage/frontend-plugin-api": "npm:^0.13.4" - "@backstage/plugin-catalog-react": "npm:^1.21.6" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@backstage/plugin-scaffolder-common": "npm:^1.7.5" - "@backstage/theme": "npm:^0.7.1" +"@backstage/plugin-scaffolder-react@npm:^1.20.0": + version: 1.20.0 + resolution: "@backstage/plugin-scaffolder-react@npm:1.20.0" + dependencies: + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/plugin-scaffolder-common": "npm:^2.0.0" + "@backstage/theme": "npm:^0.7.2" "@backstage/types": "npm:^1.2.2" - "@backstage/version-bridge": "npm:^1.0.11" + "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" @@ -3878,7 +4348,7 @@ __metadata: ajv: "npm:^8.0.1" ajv-errors: "npm:^3.0.0" classnames: "npm:^2.2.6" - flatted: "npm:3.3.3" + flatted: "npm:^3.3.4" humanize-duration: "npm:^3.25.1" immer: "npm:^9.0.6" json-schema: "npm:^0.4.0" @@ -3889,40 +4359,45 @@ __metadata: react-use: "npm:^17.2.4" use-immer: "npm:^0.11.0" zen-observable: "npm:^0.10.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" peerDependencies: + "@backstage/frontend-test-utils": ^0.5.1 "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router: ^6.30.3 + react-router-dom: ^6.30.2 peerDependenciesMeta: + "@backstage/frontend-test-utils": + optional: true "@types/react": optional: true - checksum: 10c0/292680def0de470f4c0740b7e84fb16fde607f480c5053fc37e33d0ac8e50c4ee1d4b898952e7bd7e94aff6b05c558bb9f0b44ce5b95e35ee19b5eb0317b9424 + checksum: 10c0/7e39ad2c77f4bd01c1b9451c28a229af8599d9d1ea523ffcc047f666f91c2dfb1465eb19603e6bed9ebfd9ad3b928dcab5199a74fa63cb49cb2440364aa0b592 languageName: node linkType: hard -"@backstage/plugin-scaffolder@npm:^1.35.0": - version: 1.35.2 - resolution: "@backstage/plugin-scaffolder@npm:1.35.2" +"@backstage/plugin-scaffolder@npm:^1.36.1": + version: 1.36.1 + resolution: "@backstage/plugin-scaffolder@npm:1.36.1" dependencies: - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-components": "npm:^0.18.6" - "@backstage/core-plugin-api": "npm:^1.12.2" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.4" - "@backstage/integration": "npm:^1.19.2" - "@backstage/integration-react": "npm:^1.2.14" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-react": "npm:^1.21.6" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@backstage/plugin-scaffolder-common": "npm:^1.7.5" - "@backstage/plugin-scaffolder-react": "npm:^1.19.6" + "@backstage/frontend-plugin-api": "npm:^0.15.1" + "@backstage/integration": "npm:^2.0.0" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/plugin-scaffolder-common": "npm:^2.0.0" + "@backstage/plugin-scaffolder-react": "npm:^1.20.0" "@backstage/plugin-techdocs-common": "npm:^0.1.1" - "@backstage/plugin-techdocs-react": "npm:^1.3.7" + "@backstage/plugin-techdocs-react": "npm:^1.3.9" "@backstage/types": "npm:^1.2.2" + "@backstage/ui": "npm:^0.13.1" "@codemirror/language": "npm:^6.0.0" "@codemirror/legacy-modes": "npm:^6.1.0" "@codemirror/view": "npm:^6.0.0" @@ -3930,6 +4405,7 @@ __metadata: "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" "@react-hookz/web": "npm:^24.0.0" + "@remixicon/react": "npm:^4.6.0" "@rjsf/core": "npm:5.24.13" "@rjsf/material-ui": "npm:5.24.13" "@rjsf/utils": "npm:5.24.13" @@ -3950,112 +4426,111 @@ __metadata: react-use: "npm:^17.2.4" react-window: "npm:^1.8.10" yaml: "npm:^2.0.0" - zod: "npm:^3.25.76" + zod: "npm:^3.25.76 || ^4.0.0" zod-to-json-schema: "npm:^3.25.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/ed2637dd20a7448b9a3841d6fd16b846f508217d97f6e0e21844a81ac1032d13f84afb7e1773068720ebad131683b7825875b9b50c787826949f075a98eabf00 + checksum: 10c0/fcafba902ffeddca74ac578c258d2089afb3e7eac2a8a32afc86852877f43aeb7f8458f8a01b7ec551af1cd1faf6ba1889db0209d1730612db7b79b5d9292338 languageName: node linkType: hard -"@backstage/plugin-search-backend-module-catalog@npm:^0.3.11": - version: 0.3.11 - resolution: "@backstage/plugin-search-backend-module-catalog@npm:0.3.11" +"@backstage/plugin-search-backend-module-catalog@npm:^0.3.13": + version: 0.3.13 + resolution: "@backstage/plugin-search-backend-module-catalog@npm:0.3.13" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-search-backend-node": "npm:^1.4.0" - "@backstage/plugin-search-common": "npm:^1.2.21" - checksum: 10c0/2725f51ccde449609f158a4c24bcd985c32f0c2811af1edde5361b573df52cbf2a9d13c7eb4ab949dce7ab0f00f42c3b68494e6eede55e91e279c067e49277c3 + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-search-backend-node": "npm:^1.4.2" + "@backstage/plugin-search-common": "npm:^1.2.22" + checksum: 10c0/0d06ef59603169272696d2028fcef5af7f9457c795477f1f5775c682a1970a8cbbfc3e26fe0309fb1903fd2fed46b34be45e5e0bbeac9f4038b8c1b902789214 languageName: node linkType: hard -"@backstage/plugin-search-backend-module-pg@npm:^0.5.51": - version: 0.5.51 - resolution: "@backstage/plugin-search-backend-module-pg@npm:0.5.51" +"@backstage/plugin-search-backend-module-pg@npm:^0.5.53": + version: 0.5.53 + resolution: "@backstage/plugin-search-backend-module-pg@npm:0.5.53" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" - "@backstage/plugin-search-backend-node": "npm:^1.4.0" - "@backstage/plugin-search-common": "npm:^1.2.21" + "@backstage/plugin-search-backend-node": "npm:^1.4.2" + "@backstage/plugin-search-common": "npm:^1.2.22" knex: "npm:^3.0.0" lodash: "npm:^4.17.21" uuid: "npm:^11.0.0" - checksum: 10c0/8871a014d81fc8f50bdac6294002e7ceec6189fb536fc98d6e9a50f34cc0563d1c5e89d16e2577c1b11e4fc9e696c7106d666321e54b5092c95c9b243ff2b93a + checksum: 10c0/0c3edb70d97b8f63ffeed87575adfe7f0cd42bdb0d6d0003f25bf4b5620ea6881b2f38380303ba214a3b7d268701db51d77809547864a668a3d41f34abacd7be languageName: node linkType: hard -"@backstage/plugin-search-backend-module-techdocs@npm:^0.4.9": - version: 0.4.10 - resolution: "@backstage/plugin-search-backend-module-techdocs@npm:0.4.10" +"@backstage/plugin-search-backend-module-techdocs@npm:^0.4.12": + version: 0.4.12 + resolution: "@backstage/plugin-search-backend-module-techdocs@npm:0.4.12" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-permission-common": "npm:^0.9.4" - "@backstage/plugin-search-backend-node": "npm:^1.4.0" - "@backstage/plugin-search-common": "npm:^1.2.21" - "@backstage/plugin-techdocs-node": "npm:^1.14.0" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-search-backend-node": "npm:^1.4.2" + "@backstage/plugin-search-common": "npm:^1.2.22" + "@backstage/plugin-techdocs-node": "npm:^1.14.4" lodash: "npm:^4.17.21" p-limit: "npm:^3.1.0" - checksum: 10c0/c978f8637fbdebe1df4df4246618fae684c036a30dc336f12813ab0d2ed017919a50d2b6b738f78f8d1c81af174d02691847b052a035e0d6797b285dc53448c4 + checksum: 10c0/61f52a5861a69a829712668389dddefce5128e1b9575704b1c592eb5b6528c3b3cc5243ae13e9f5e5f62939e2b58dc5ed5583b6907299a728e3f062ae2c40664 languageName: node linkType: hard -"@backstage/plugin-search-backend-node@npm:^1.4.0": - version: 1.4.0 - resolution: "@backstage/plugin-search-backend-node@npm:1.4.0" +"@backstage/plugin-search-backend-node@npm:^1.4.2": + version: 1.4.2 + resolution: "@backstage/plugin-search-backend-node@npm:1.4.2" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-search-common": "npm:^1.2.21" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-search-common": "npm:^1.2.22" "@types/lunr": "npm:^2.3.3" lodash: "npm:^4.17.21" lunr: "npm:^2.3.9" ndjson: "npm:^2.0.0" uuid: "npm:^11.0.0" - checksum: 10c0/ba00e80422a078dc13d69c32bd76616ebfacd64f2f50340650ef7d96ef8d3eb73a5c4097c00f5e66a184edf3bf957c74dc5786b4681ab2105c0ca32452bb5eb0 + checksum: 10c0/8971101f5ef2789328531e8a3c8f215a65e8d45f03d7229343d9c50df577b636105e933d511da44f470c77162b0d0838933f40f08e60cc036ce4a98213d53e96 languageName: node linkType: hard -"@backstage/plugin-search-backend@npm:^2.0.9": - version: 2.0.11 - resolution: "@backstage/plugin-search-backend@npm:2.0.11" +"@backstage/plugin-search-backend@npm:^2.1.0": + version: 2.1.0 + resolution: "@backstage/plugin-search-backend@npm:2.1.0" dependencies: - "@backstage/backend-defaults": "npm:^0.15.1" - "@backstage/backend-openapi-utils": "npm:^0.6.5" - "@backstage/backend-plugin-api": "npm:^1.6.2" + "@backstage/backend-openapi-utils": "npm:^0.6.7" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-permission-common": "npm:^0.9.5" - "@backstage/plugin-permission-node": "npm:^0.10.9" - "@backstage/plugin-search-backend-node": "npm:^1.4.0" - "@backstage/plugin-search-common": "npm:^1.2.21" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" + "@backstage/plugin-search-backend-node": "npm:^1.4.2" + "@backstage/plugin-search-common": "npm:^1.2.22" "@backstage/types": "npm:^1.2.2" dataloader: "npm:^2.0.0" express: "npm:^4.22.0" lodash: "npm:^4.17.21" qs: "npm:^6.10.1" yn: "npm:^4.0.0" - zod: "npm:^3.25.76" - checksum: 10c0/09aa1f7e8765535813de0797b63c41d728e5d19bf762a2a9d856352891dcac54a8e4b4d298f03a023a95efb646514733935f9aef6df1652d715e495c08dd08c5 + zod: "npm:^3.25.76 || ^4.0.0" + checksum: 10c0/be0ecb3fbda7418e3403d42f4060ed18b4968bc3ea69ee79844ece97bcd741f51cfd5b18247a47c4dbf67ce0d00793c8df977387c03038b13adeeb49d000c1db languageName: node linkType: hard @@ -4069,17 +4544,27 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-react@npm:^1.10.1, @backstage/plugin-search-react@npm:^1.10.2": - version: 1.10.2 - resolution: "@backstage/plugin-search-react@npm:1.10.2" +"@backstage/plugin-search-common@npm:^1.2.22": + version: 1.2.22 + resolution: "@backstage/plugin-search-common@npm:1.2.22" dependencies: - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-search-common": "npm:^1.2.21" - "@backstage/theme": "npm:^0.7.1" + "@backstage/plugin-permission-common": "npm:^0.9.6" "@backstage/types": "npm:^1.2.2" - "@backstage/version-bridge": "npm:^1.0.11" + checksum: 10c0/f8dfb561d7e1022d7bbcdf8e99bf18a3b214da3fc597f9cc56bdaca77b1c8667de7ce2b094bc10b5f40988924352c3dbf8f6b384fa3e78b2516b5685be83cba4 + languageName: node + linkType: hard + +"@backstage/plugin-search-react@npm:^1.11.0": + version: 1.11.0 + resolution: "@backstage/plugin-search-react@npm:1.11.0" + dependencies: + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-search-common": "npm:^1.2.22" + "@backstage/theme": "npm:^0.7.2" + "@backstage/types": "npm:^1.2.2" + "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" @@ -4091,27 +4576,28 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/eb1ae375567cb2a2d99bd1a8ad08c12fb76ea52abffe232b7720dfe0263ec395b9f11a405c2829c91cd9a859fd4cab9695d6c563f685bdcb54d6d411ea4f33e3 + checksum: 10c0/7157c599ac2f3f5456edbdb41b63ee7629c844c6ef7a20c44b366e7a8cb52e9404ec4488d1f9944ceb72a58f5c8713c7b35d119ae489399b6cb3f4101dc1008a languageName: node linkType: hard -"@backstage/plugin-search@npm:^1.5.1": - version: 1.5.3 - resolution: "@backstage/plugin-search@npm:1.5.3" +"@backstage/plugin-search@npm:^1.7.0": + version: 1.7.0 + resolution: "@backstage/plugin-search@npm:1.7.0" dependencies: - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-search-common": "npm:^1.2.21" - "@backstage/plugin-search-react": "npm:^1.10.2" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-search-common": "npm:^1.2.22" + "@backstage/plugin-search-react": "npm:^1.11.0" "@backstage/types": "npm:^1.2.2" - "@backstage/version-bridge": "npm:^1.0.11" + "@backstage/ui": "npm:^0.13.0" + "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" qs: "npm:^6.9.4" @@ -4120,75 +4606,75 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/fafad3e18e2bca5c2b00517e7a6cdc4cd23c3f55a50333a8e2b859b649857998a636f1645a07a983e89ddad063844cf0141677c002098f168dfee540591719de + checksum: 10c0/1d1ea688b6f5a7b45022cbbe3e5e8fd756bce24cba27c0c666455611dfd8f97d380be69fdc78dca73aca2c00da7cc0284ea85d86f01361f1b0aaba0e04379fbe languageName: node linkType: hard -"@backstage/plugin-signals-backend@npm:^0.3.11": - version: 0.3.11 - resolution: "@backstage/plugin-signals-backend@npm:0.3.11" +"@backstage/plugin-signals-backend@npm:^0.3.13": + version: 0.3.13 + resolution: "@backstage/plugin-signals-backend@npm:0.3.13" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" - "@backstage/plugin-events-node": "npm:^0.4.18" - "@backstage/plugin-signals-node": "npm:^0.1.27" + "@backstage/plugin-events-node": "npm:^0.4.20" + "@backstage/plugin-signals-node": "npm:^0.1.29" "@backstage/types": "npm:^1.2.2" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" uuid: "npm:^11.0.0" ws: "npm:^8.18.0" - checksum: 10c0/ce42385d9f338039285850152c287fafbb4550a5b907eae0679dd3207eb2ab49a7e7e03ce431fa13075c7ac1c214d4f25dfe584288628892b92b97c8c918e101 + checksum: 10c0/6f0f9d0864ebc8ee756519f2fd0ef44c30723ef0dc02048fdcba5bf7f30abd875f184930b7fb5382275aaea21b00a057e0743064847c7e29cd9ea4bc24734bfb languageName: node linkType: hard -"@backstage/plugin-signals-node@npm:^0.1.27": - version: 0.1.27 - resolution: "@backstage/plugin-signals-node@npm:0.1.27" +"@backstage/plugin-signals-node@npm:^0.1.29": + version: 0.1.29 + resolution: "@backstage/plugin-signals-node@npm:0.1.29" dependencies: - "@backstage/backend-plugin-api": "npm:^1.6.0" + "@backstage/backend-plugin-api": "npm:^1.8.0" "@backstage/config": "npm:^1.3.6" - "@backstage/plugin-auth-node": "npm:^0.6.10" - "@backstage/plugin-events-node": "npm:^0.4.18" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-events-node": "npm:^0.4.20" "@backstage/types": "npm:^1.2.2" express: "npm:^4.22.0" uuid: "npm:^11.0.0" ws: "npm:^8.18.0" - checksum: 10c0/92c4f88930eb70cb768febb37e957c3aabfe4119328088b561454b2a88336698348c0b6712bcd5b946f39762a54b51d269087ebde4f728c1e37fa51e6ac5eb88 + checksum: 10c0/4d32e70698e85f2dd653f120c3486ed169d26ad36db417607ef9c5b0b2824c4cb75dd812709dfb442e60f332a78f6765cab99999cd1f1abd73303da257454d4e languageName: node linkType: hard -"@backstage/plugin-signals-react@npm:^0.0.18": - version: 0.0.18 - resolution: "@backstage/plugin-signals-react@npm:0.0.18" +"@backstage/plugin-signals-react@npm:^0.0.20": + version: 0.0.20 + resolution: "@backstage/plugin-signals-react@npm:0.0.20" dependencies: - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/types": "npm:^1.2.2" "@material-ui/core": "npm:^4.12.4" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/cb5d8d9f41cf2111ee1869b271e02c9c70f8b41082af6b0874dbcc28c33cf4d3561b745aa45a39add95d4d2e01101bf902fbf581bd428b380940ccf438116ebb + checksum: 10c0/49b43501979ffb1f9f5c3dc0cc5606249bdde3c8952bfeca0257dc686b49a843022056613b401ff58f551437fc9b5a450aef04494b154a1ca0e29d72a33b0668 languageName: node linkType: hard -"@backstage/plugin-signals@npm:^0.0.26": - version: 0.0.26 - resolution: "@backstage/plugin-signals@npm:0.0.26" +"@backstage/plugin-signals@npm:^0.0.29": + version: 0.0.29 + resolution: "@backstage/plugin-signals@npm:0.0.29" dependencies: - "@backstage/core-components": "npm:^0.18.4" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.2" - "@backstage/plugin-signals-react": "npm:^0.0.18" - "@backstage/theme": "npm:^0.7.1" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-signals-react": "npm:^0.0.20" + "@backstage/theme": "npm:^0.7.2" "@backstage/types": "npm:^1.2.2" "@material-ui/core": "npm:^4.12.4" uuid: "npm:^11.0.0" @@ -4196,27 +4682,26 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/b2b628e9a52b8ae227f46ecd8ef669896223880697ca7d5fe13b89b41415885ea88e3a23e81eff4d20c2f9f2e589900c336dbedbf12eb29226592b0569e63b38 + checksum: 10c0/ad32af89aaef11c0b5fddf0478be680391a0eca7178f1103eb4a9dfab1fbff78fd0366765d7e180acf0e14b318daecf9eabb899a7dffbc651f764d47576925d5 languageName: node linkType: hard -"@backstage/plugin-techdocs-backend@npm:^2.1.3": - version: 2.1.4 - resolution: "@backstage/plugin-techdocs-backend@npm:2.1.4" +"@backstage/plugin-techdocs-backend@npm:^2.1.6": + version: 2.1.6 + resolution: "@backstage/plugin-techdocs-backend@npm:2.1.6" dependencies: - "@backstage/backend-defaults": "npm:^0.15.0" - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/plugin-catalog-node": "npm:^1.20.1" - "@backstage/plugin-techdocs-node": "npm:^1.14.0" + "@backstage/integration": "npm:^2.0.0" + "@backstage/plugin-catalog-node": "npm:^2.1.0" + "@backstage/plugin-techdocs-node": "npm:^1.14.4" "@backstage/types": "npm:^1.2.2" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" @@ -4224,7 +4709,7 @@ __metadata: knex: "npm:^3.0.0" p-limit: "npm:^3.1.0" winston: "npm:^3.2.1" - checksum: 10c0/3b11259793b18f78ed9affc05bf15520002fe0f4163f15ee7344912a218ea7adf389ff3fb3b08731e95dee979ebfa6ac68250c2ba311dfe18bf66d5a8bf01f38 + checksum: 10c0/81d515b3a6905d027deb6788aa0bc10a0cea50b702d463bcb150eb90db187445a601572b8eccd567a7959184db2dc78ff2ac1fab26b7c4e84a5198cbf1639537 languageName: node linkType: hard @@ -4235,16 +4720,16 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-techdocs-module-addons-contrib@npm:^1.1.31": - version: 1.1.32 - resolution: "@backstage/plugin-techdocs-module-addons-contrib@npm:1.1.32" +"@backstage/plugin-techdocs-module-addons-contrib@npm:^1.1.34": + version: 1.1.34 + resolution: "@backstage/plugin-techdocs-module-addons-contrib@npm:1.1.34" dependencies: - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/integration": "npm:^1.19.2" - "@backstage/integration-react": "npm:^1.2.14" - "@backstage/plugin-techdocs-react": "npm:^1.3.7" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/integration": "npm:^2.0.0" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-techdocs-react": "npm:^1.3.9" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@react-hookz/web": "npm:^24.0.0" @@ -4254,17 +4739,17 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/2d826700028280c90d3fc7d3f0d3fcfc9b9fbcb6a28bb8592438b40f0079b3c089efc2510f87561830bfe343d8309902e6294b0cac6b6f007bf252884b0acad7 + checksum: 10c0/3f59aaa4f8b99705eb823ca05523831811c400cd550853be7aa8a59d1cb3b57c8389c41a69b9fa285a8cd5f619cb8c5f9d2689e8f0f9eca6da642081465c2621 languageName: node linkType: hard -"@backstage/plugin-techdocs-node@npm:^1.14.0": - version: 1.14.1 - resolution: "@backstage/plugin-techdocs-node@npm:1.14.1" +"@backstage/plugin-techdocs-node@npm:^1.14.4": + version: 1.14.4 + resolution: "@backstage/plugin-techdocs-node@npm:1.14.4" dependencies: "@aws-sdk/client-s3": "npm:^3.350.0" "@aws-sdk/credential-providers": "npm:^3.350.0" @@ -4272,13 +4757,13 @@ __metadata: "@aws-sdk/types": "npm:^3.347.0" "@azure/identity": "npm:^4.0.0" "@azure/storage-blob": "npm:^12.5.0" - "@backstage/backend-plugin-api": "npm:^1.6.2" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" "@backstage/errors": "npm:^1.2.7" - "@backstage/integration": "npm:^1.19.2" - "@backstage/integration-aws-node": "npm:^0.1.19" - "@backstage/plugin-search-common": "npm:^1.2.21" + "@backstage/integration": "npm:^2.0.0" + "@backstage/integration-aws-node": "npm:^0.1.20" + "@backstage/plugin-search-common": "npm:^1.2.22" "@backstage/plugin-techdocs-common": "npm:^0.1.1" "@google-cloud/storage": "npm:^7.0.0" "@smithy/node-http-handler": "npm:^3.0.0" @@ -4295,21 +4780,21 @@ __metadata: p-limit: "npm:^3.1.0" recursive-readdir: "npm:^2.2.2" winston: "npm:^3.2.1" - checksum: 10c0/127eec83e71d85b5ff5f2a4aeaf4f2e68ede64fabc3aeaa15dc8d6efddbf64db0177e70860196e64807b220e4241ee61ef7389965040e8fc3e4bd231e123f509 + checksum: 10c0/c1511f0f8b3981405862bae6f68736b817a7acecfcb67b40a26bf8140177c2829eed55b210cd342195612e08517983cb623e0e5f33b9731bb884d316271c157b languageName: node linkType: hard -"@backstage/plugin-techdocs-react@npm:^1.3.6, @backstage/plugin-techdocs-react@npm:^1.3.7": - version: 1.3.7 - resolution: "@backstage/plugin-techdocs-react@npm:1.3.7" +"@backstage/plugin-techdocs-react@npm:^1.3.9": + version: 1.3.9 + resolution: "@backstage/plugin-techdocs-react@npm:1.3.9" dependencies: - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/frontend-plugin-api": "npm:^0.13.3" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/frontend-plugin-api": "npm:^0.15.0" "@backstage/plugin-techdocs-common": "npm:^0.1.1" - "@backstage/version-bridge": "npm:^1.0.11" + "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/styles": "npm:^4.11.0" jss: "npm:~10.10.0" @@ -4320,40 +4805,42 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/916236bd4b6e5ba832e044bbdc13bab8f1fbc8ff742347babf78fd484f6eb81d5d2caeef88af8a4479e97fe4c5e79fb4d79c6ed22f392ffe1b05aff658c47ac3 + checksum: 10c0/15faa3dac14c43a4793e9e2d942c7ba53db4c7b5249fb389ca986078a2dcb911f9a09b639744117105fff829861087d5c18dbf1d697b1e51207b5ac3fb55f648 languageName: node linkType: hard -"@backstage/plugin-techdocs@npm:^1.16.1": - version: 1.16.2 - resolution: "@backstage/plugin-techdocs@npm:1.16.2" +"@backstage/plugin-techdocs@npm:^1.17.2": + version: 1.17.2 + resolution: "@backstage/plugin-techdocs@npm:1.17.2" dependencies: - "@backstage/catalog-client": "npm:^1.12.1" - "@backstage/catalog-model": "npm:^1.7.6" + "@backstage/catalog-client": "npm:^1.14.0" + "@backstage/catalog-model": "npm:^1.7.7" "@backstage/config": "npm:^1.3.6" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/integration": "npm:^1.19.2" - "@backstage/integration-react": "npm:^1.2.14" - "@backstage/plugin-auth-react": "npm:^0.1.23" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-search-common": "npm:^1.2.21" - "@backstage/plugin-search-react": "npm:^1.10.2" + "@backstage/frontend-plugin-api": "npm:^0.15.1" + "@backstage/integration": "npm:^2.0.0" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-auth-react": "npm:^0.1.25" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-search-common": "npm:^1.2.22" + "@backstage/plugin-search-react": "npm:^1.11.0" "@backstage/plugin-techdocs-common": "npm:^0.1.1" - "@backstage/plugin-techdocs-react": "npm:^1.3.7" - "@backstage/theme": "npm:^0.7.1" + "@backstage/plugin-techdocs-react": "npm:^1.3.9" + "@backstage/theme": "npm:^0.7.2" + "@backstage/ui": "npm:^0.13.1" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" "@material-ui/styles": "npm:^4.10.0" "@microsoft/fetch-event-source": "npm:^2.0.1" - dompurify: "npm:^3.2.4" + "@remixicon/react": "npm:^4.6.0" + dompurify: "npm:^3.3.2" git-url-parse: "npm:^15.0.0" lodash: "npm:^4.17.21" react-helmet: "npm:6.1.0" @@ -4362,50 +4849,54 @@ __metadata: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/abb91a777497044768866775d6e625f58a5213d220c5e3349032e69b5403169ff3ab147d10edddd6aedf634fdca2ce37e788d579cc8f3aa57fa8054a72f53302 + checksum: 10c0/52900d961b9e19d306a3f48fa07701c23bf66282894c3cb86ffb515be847c499601c3d72fe4993b7d21ad3ce5142a729cedec950e7a193780d717600a60c42d3 languageName: node linkType: hard -"@backstage/plugin-user-settings-common@npm:^0.0.1": - version: 0.0.1 - resolution: "@backstage/plugin-user-settings-common@npm:0.0.1" - checksum: 10c0/b447a444f6feb0ec1dc7f3c2de9f6706138aec2d593b2f529295997bac47e33a7914321575b7d13774b9348289ab50ae23924b9f3efe65bf058e94889d07b6f8 +"@backstage/plugin-user-settings-common@npm:^0.1.0": + version: 0.1.0 + resolution: "@backstage/plugin-user-settings-common@npm:0.1.0" + dependencies: + "@backstage/types": "npm:^1.2.2" + checksum: 10c0/cb922137295ec2a2f7d282157f309b675651eca25c1dd94ca000e2983e2f758e21f5ca6725117adfe7142b3f1e9f77f5922b9b85ba6083e51cb4d9763bea2207 languageName: node linkType: hard -"@backstage/plugin-user-settings@npm:^0.8.30": - version: 0.8.31 - resolution: "@backstage/plugin-user-settings@npm:0.8.31" +"@backstage/plugin-user-settings@npm:^0.9.1": + version: 0.9.1 + resolution: "@backstage/plugin-user-settings@npm:0.9.1" dependencies: - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/core-app-api": "npm:^1.19.3" - "@backstage/core-components": "npm:^0.18.5" - "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/core-app-api": "npm:^1.19.6" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.13.3" - "@backstage/plugin-catalog-react": "npm:^1.21.5" - "@backstage/plugin-signals-react": "npm:^0.0.18" - "@backstage/plugin-user-settings-common": "npm:^0.0.1" - "@backstage/theme": "npm:^0.7.1" + "@backstage/frontend-plugin-api": "npm:^0.15.0" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-signals-react": "npm:^0.0.20" + "@backstage/plugin-user-settings-common": "npm:^0.1.0" + "@backstage/theme": "npm:^0.7.2" "@backstage/types": "npm:^1.2.2" + "@backstage/ui": "npm:^0.13.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:4.0.0-alpha.61" + dataloader: "npm:^2.0.0" react-use: "npm:^17.2.4" zen-observable: "npm:^0.10.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/dc1624a6c6fafd9fb67a7f6b11ab8aee7e7a41d03f76ef74965661016db653299337b07988e15ed8e7e8202d93a76779e3a10699c2755de976a1ab244fe7e660 + checksum: 10c0/323278a4632b8f1d9b41d0e02bc7def07163e970b3c44d752a6121f8786c1036db73adb89b80a2351d04a532ee279bd9202f582c4d03d1450fb389212a476ea5 languageName: node linkType: hard @@ -4416,16 +4907,45 @@ __metadata: languageName: node linkType: hard -"@backstage/test-utils@npm:^1.7.14": - version: 1.7.14 - resolution: "@backstage/test-utils@npm:1.7.14" +"@backstage/test-utils@npm:^1.7.14": + version: 1.7.14 + resolution: "@backstage/test-utils@npm:1.7.14" + dependencies: + "@backstage/config": "npm:^1.3.6" + "@backstage/core-app-api": "npm:^1.19.3" + "@backstage/core-plugin-api": "npm:^1.12.1" + "@backstage/plugin-permission-common": "npm:^0.9.3" + "@backstage/plugin-permission-react": "npm:^0.4.39" + "@backstage/theme": "npm:^0.7.1" + "@backstage/types": "npm:^1.2.2" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + cross-fetch: "npm:^4.0.0" + i18next: "npm:^22.4.15" + zen-observable: "npm:^0.10.0" + peerDependencies: + "@testing-library/react": ^16.0.0 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/8c2de7a9abe1dc2dbe5e010c362f8e601fbd37f4f5fd0a71c8949c2a1c35a12755a4d45d1a8c18d6652614803f91c275c17ec77ba42f46ac85b7b5ee35674b6a + languageName: node + linkType: hard + +"@backstage/test-utils@npm:^1.7.16": + version: 1.7.16 + resolution: "@backstage/test-utils@npm:1.7.16" dependencies: "@backstage/config": "npm:^1.3.6" - "@backstage/core-app-api": "npm:^1.19.3" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@backstage/theme": "npm:^0.7.1" + "@backstage/core-app-api": "npm:^1.19.6" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/theme": "npm:^0.7.2" "@backstage/types": "npm:^1.2.2" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" @@ -4434,14 +4954,17 @@ __metadata: zen-observable: "npm:^0.10.0" peerDependencies: "@testing-library/react": ^16.0.0 + "@types/jest": "*" "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: + "@types/jest": + optional: true "@types/react": optional: true - checksum: 10c0/8c2de7a9abe1dc2dbe5e010c362f8e601fbd37f4f5fd0a71c8949c2a1c35a12755a4d45d1a8c18d6652614803f91c275c17ec77ba42f46ac85b7b5ee35674b6a + checksum: 10c0/1e8e2b6f9942505846bd615297e3aa95f049b6bf658fc95dc2688007c6a83bb19135063f21f86d91a6e5d3978a59158967de5007c9b13d396e170eff515d5945 languageName: node linkType: hard @@ -4465,6 +4988,26 @@ __metadata: languageName: node linkType: hard +"@backstage/theme@npm:^0.7.2": + version: 0.7.2 + resolution: "@backstage/theme@npm:0.7.2" + dependencies: + "@emotion/react": "npm:^11.10.5" + "@emotion/styled": "npm:^11.10.5" + "@mui/material": "npm:^5.12.2" + peerDependencies: + "@material-ui/core": ^4.12.2 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/f3d10319727dde23b2f30813dd9e1981167670b04f5143dd0da222c9e5b8f8da43d9d8c42935b4a07d4ddd09e6fa18014cd8e5bd7dcb86c03a7593b3cee67d2a + languageName: node + linkType: hard + "@backstage/types@npm:^1.2.1, @backstage/types@npm:^1.2.2": version: 1.2.2 resolution: "@backstage/types@npm:1.2.2" @@ -4472,23 +5015,25 @@ __metadata: languageName: node linkType: hard -"@backstage/ui@npm:^0.10.0": - version: 0.10.0 - resolution: "@backstage/ui@npm:0.10.0" +"@backstage/ui@npm:^0.13.0, @backstage/ui@npm:^0.13.1": + version: 0.13.1 + resolution: "@backstage/ui@npm:0.13.1" dependencies: + "@backstage/version-bridge": "npm:^1.0.12" "@remixicon/react": "npm:^4.6.0" "@tanstack/react-table": "npm:^8.21.3" clsx: "npm:^2.1.1" - react-aria-components: "npm:^1.13.0" + react-aria-components: "npm:^1.14.0" + use-sync-external-store: "npm:^1.4.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 + react-router-dom: ^6.30.2 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/a3137a0005949019539b01c9c1b9ae06f2ccac76d384616cde73d8837010c9be093367f548f2b0c0e1484bdfcdb3b292afdc354d756ec8a439d1feb0b1430383 + checksum: 10c0/e581a2185a273aa97909e53d594a534fa1f8d9967593a09b20a47c0b149ab559b7ff7fe88ef02ec3b74243ac4a52b8086d24b9d7733bba4756fb1eb77c171836 languageName: node linkType: hard @@ -4507,6 +5052,21 @@ __metadata: languageName: node linkType: hard +"@backstage/version-bridge@npm:^1.0.12": + version: 1.0.12 + resolution: "@backstage/version-bridge@npm:1.0.12" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/1dc2d45ccef8287bd7bf0ed0f8f87ce742e207ab7fb2d17ec25defb4dd93240ba0a63365f627cc17b97423a0a0e043ab433cfe9e7772eb178dc84983d4a03104 + languageName: node + linkType: hard + "@balena/dockerignore@npm:^1.0.2": version: 1.0.2 resolution: "@balena/dockerignore@npm:1.0.2" @@ -4957,216 +5517,184 @@ __metadata: languageName: node linkType: hard -"@envelop/core@npm:^5.2.3, @envelop/core@npm:^5.3.0": - version: 5.5.0 - resolution: "@envelop/core@npm:5.5.0" - dependencies: - "@envelop/instrumentation": "npm:^1.0.0" - "@envelop/types": "npm:^5.2.1" - "@whatwg-node/promise-helpers": "npm:^1.2.4" - tslib: "npm:^2.5.0" - checksum: 10c0/7b824effa13d464837a92474514cbd1ea6404f7c5e20b1c4364671a2503eafd33b0d67f8e86fb2841ecb2130a3f3b528a83d501d5bcef2a4e6426056787f31f8 - languageName: node - linkType: hard - -"@envelop/instrumentation@npm:^1.0.0": - version: 1.0.0 - resolution: "@envelop/instrumentation@npm:1.0.0" - dependencies: - "@whatwg-node/promise-helpers": "npm:^1.2.1" - tslib: "npm:^2.5.0" - checksum: 10c0/134df1ac481fb392aafc4522a22bcdc6ef0701f2d15d51b16207f3c9a4c7d3760adfa5f5bcc84f0c0ec7b011d84bcd40fff671eb471bed54bd928c165994b4e3 - languageName: node - linkType: hard - -"@envelop/types@npm:^5.2.1": - version: 5.2.1 - resolution: "@envelop/types@npm:5.2.1" - dependencies: - "@whatwg-node/promise-helpers": "npm:^1.0.0" - tslib: "npm:^2.5.0" - checksum: 10c0/2cdbb29d98350d957e18aff38ddf95670c249df894afab7fc888e2a02b43ca029fde96ca2829e5350bf83b982bc0267a5c8f7ee3ed9d353d4f145ebc0dc0b1e0 - languageName: node - linkType: hard - -"@esbuild/aix-ppc64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/aix-ppc64@npm:0.27.2" +"@esbuild/aix-ppc64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/aix-ppc64@npm:0.27.4" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-arm64@npm:0.27.2" +"@esbuild/android-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/android-arm64@npm:0.27.4" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-arm@npm:0.27.2" +"@esbuild/android-arm@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/android-arm@npm:0.27.4" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-x64@npm:0.27.2" +"@esbuild/android-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/android-x64@npm:0.27.4" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/darwin-arm64@npm:0.27.2" +"@esbuild/darwin-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/darwin-arm64@npm:0.27.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/darwin-x64@npm:0.27.2" +"@esbuild/darwin-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/darwin-x64@npm:0.27.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/freebsd-arm64@npm:0.27.2" +"@esbuild/freebsd-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/freebsd-arm64@npm:0.27.4" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/freebsd-x64@npm:0.27.2" +"@esbuild/freebsd-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/freebsd-x64@npm:0.27.4" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-arm64@npm:0.27.2" +"@esbuild/linux-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-arm64@npm:0.27.4" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-arm@npm:0.27.2" +"@esbuild/linux-arm@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-arm@npm:0.27.4" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-ia32@npm:0.27.2" +"@esbuild/linux-ia32@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-ia32@npm:0.27.4" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-loong64@npm:0.27.2" +"@esbuild/linux-loong64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-loong64@npm:0.27.4" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-mips64el@npm:0.27.2" +"@esbuild/linux-mips64el@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-mips64el@npm:0.27.4" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-ppc64@npm:0.27.2" +"@esbuild/linux-ppc64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-ppc64@npm:0.27.4" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-riscv64@npm:0.27.2" +"@esbuild/linux-riscv64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-riscv64@npm:0.27.4" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-s390x@npm:0.27.2" +"@esbuild/linux-s390x@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-s390x@npm:0.27.4" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-x64@npm:0.27.2" +"@esbuild/linux-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-x64@npm:0.27.4" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/netbsd-arm64@npm:0.27.2" +"@esbuild/netbsd-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/netbsd-arm64@npm:0.27.4" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/netbsd-x64@npm:0.27.2" +"@esbuild/netbsd-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/netbsd-x64@npm:0.27.4" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openbsd-arm64@npm:0.27.2" +"@esbuild/openbsd-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/openbsd-arm64@npm:0.27.4" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openbsd-x64@npm:0.27.2" +"@esbuild/openbsd-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/openbsd-x64@npm:0.27.4" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openharmony-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openharmony-arm64@npm:0.27.2" +"@esbuild/openharmony-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/openharmony-arm64@npm:0.27.4" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/sunos-x64@npm:0.27.2" +"@esbuild/sunos-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/sunos-x64@npm:0.27.4" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-arm64@npm:0.27.2" +"@esbuild/win32-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/win32-arm64@npm:0.27.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-ia32@npm:0.27.2" +"@esbuild/win32-ia32@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/win32-ia32@npm:0.27.4" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-x64@npm:0.27.2" +"@esbuild/win32-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/win32-x64@npm:0.27.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -5232,13 +5760,6 @@ __metadata: languageName: node linkType: hard -"@fastify/busboy@npm:^3.1.1": - version: 3.2.0 - resolution: "@fastify/busboy@npm:3.2.0" - checksum: 10c0/3e4fb00a27e3149d1c68de8ff14007d2bbcbbc171a9d050d0a8772e836727329d4d3f130995ebaa19cf537d5d2f5ce2a88000366e6192e751457bfcc2125f351 - languageName: node - linkType: hard - "@floating-ui/core@npm:^1.7.4": version: 1.7.4 resolution: "@floating-ui/core@npm:1.7.4" @@ -5328,39 +5849,6 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/core@npm:^41.3.0": - version: 41.3.0 - resolution: "@gitbeaker/core@npm:41.3.0" - dependencies: - "@gitbeaker/requester-utils": "npm:^41.3.0" - qs: "npm:^6.12.2" - xcase: "npm:^2.0.1" - checksum: 10c0/80181536c0d2edfa86c81d436d3f571913c59e15f37008008bfb15dd3d9e11ce20d498a4b5216f447eb03a57e55f8bd00bbc320a933d70e50115c9942dce0477 - languageName: node - linkType: hard - -"@gitbeaker/requester-utils@npm:^41.2.0, @gitbeaker/requester-utils@npm:^41.3.0": - version: 41.3.0 - resolution: "@gitbeaker/requester-utils@npm:41.3.0" - dependencies: - picomatch-browser: "npm:^2.2.6" - qs: "npm:^6.12.2" - rate-limiter-flexible: "npm:^4.0.1" - xcase: "npm:^2.0.1" - checksum: 10c0/12fec1a67fbe3e18d1a4af1cd8b915c0b07cd73c9ad9072e4b2210d099486ada393e39ab31148256feb56ed73f55c12c5ed97d54d5ffb181e46ac8d70110dba0 - languageName: node - linkType: hard - -"@gitbeaker/rest@npm:^41.2.0": - version: 41.3.0 - resolution: "@gitbeaker/rest@npm:41.3.0" - dependencies: - "@gitbeaker/core": "npm:^41.3.0" - "@gitbeaker/requester-utils": "npm:^41.3.0" - checksum: 10c0/253e29e7b968c6b195da470d4cfd5b1d982d896e58506cb56f2186989a7cacb9fc98d98c6f7bde0e13ac73c9393b754849838539223c7db11b5ba6c7b4718be2 - languageName: node - linkType: hard - "@google-cloud/container@npm:^5.0.0": version: 5.19.0 resolution: "@google-cloud/container@npm:5.19.0" @@ -5399,292 +5887,79 @@ __metadata: checksum: 10c0/0d0a6ceca76a138973fcb3ad577f209acdbd9d9aed1c645b09f98d5e5a258053dbbe6c1f13e6f85310cc0d9308f5f3a84f8fa4f1a132549a68d86174fb21067f languageName: node linkType: hard - -"@google-cloud/promisify@npm:<4.1.0": - version: 4.0.0 - resolution: "@google-cloud/promisify@npm:4.0.0" - checksum: 10c0/4332cbd923d7c6943ecdf46f187f1417c84bb9c801525cd74d719c766bfaad650f7964fb74576345f6537b6d6273a4f2992c8d79ebec6c8b8401b23d626b8dd3 - languageName: node - linkType: hard - -"@google-cloud/storage@npm:^7.0.0": - version: 7.18.0 - resolution: "@google-cloud/storage@npm:7.18.0" - dependencies: - "@google-cloud/paginator": "npm:^5.0.0" - "@google-cloud/projectify": "npm:^4.0.0" - "@google-cloud/promisify": "npm:<4.1.0" - abort-controller: "npm:^3.0.0" - async-retry: "npm:^1.3.3" - duplexify: "npm:^4.1.3" - fast-xml-parser: "npm:^4.4.1" - gaxios: "npm:^6.0.2" - google-auth-library: "npm:^9.6.3" - html-entities: "npm:^2.5.2" - mime: "npm:^3.0.0" - p-limit: "npm:^3.0.1" - retry-request: "npm:^7.0.0" - teeny-request: "npm:^9.0.0" - uuid: "npm:^8.0.0" - checksum: 10c0/1879a7c60a0a23890067d0b17359da701d0504e46b8e4c0b3cdfd29dcd54fcaaddada68206d1d14fafadea86eb0a885bd8cc725c453def845f9bd9aae2cc3a85 - languageName: node - linkType: hard - -"@graphiql/react@npm:^0.20.3": - version: 0.20.4 - resolution: "@graphiql/react@npm:0.20.4" - dependencies: - "@graphiql/toolkit": "npm:^0.9.1" - "@headlessui/react": "npm:^1.7.15" - "@radix-ui/react-dialog": "npm:^1.0.4" - "@radix-ui/react-dropdown-menu": "npm:^2.0.5" - "@radix-ui/react-tooltip": "npm:^1.0.6" - "@radix-ui/react-visually-hidden": "npm:^1.0.3" - "@types/codemirror": "npm:^5.60.8" - clsx: "npm:^1.2.1" - codemirror: "npm:^5.65.3" - codemirror-graphql: "npm:^2.0.11" - copy-to-clipboard: "npm:^3.2.0" - framer-motion: "npm:^6.5.1" - graphql-language-service: "npm:^5.2.0" - markdown-it: "npm:^12.2.0" - set-value: "npm:^4.1.0" - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 - react: ^16.8.0 || ^17 || ^18 - react-dom: ^16.8.0 || ^17 || ^18 - checksum: 10c0/245a3f99dee0a0952973cc9a205f8ab560bb823ce9493941bb1a7501044e772bd57edc0cb5538622ebfabc2f4f6353729186929abaa8147f9dd91431725b4acb - languageName: node - linkType: hard - -"@graphiql/react@npm:^0.23.0": - version: 0.23.1 - resolution: "@graphiql/react@npm:0.23.1" - dependencies: - "@graphiql/toolkit": "npm:^0.9.2" - "@headlessui/react": "npm:^1.7.15" - "@radix-ui/react-dialog": "npm:^1.0.4" - "@radix-ui/react-dropdown-menu": "npm:^2.0.5" - "@radix-ui/react-tooltip": "npm:^1.0.6" - "@radix-ui/react-visually-hidden": "npm:^1.0.3" - "@types/codemirror": "npm:^5.60.8" - clsx: "npm:^1.2.1" - codemirror: "npm:^5.65.3" - codemirror-graphql: "npm:^2.0.13" - copy-to-clipboard: "npm:^3.2.0" - framer-motion: "npm:^6.5.1" - graphql-language-service: "npm:^5.2.2" - markdown-it: "npm:^14.1.0" - set-value: "npm:^4.1.0" - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 - react: ^16.8.0 || ^17 || ^18 - react-dom: ^16.8.0 || ^17 || ^18 - checksum: 10c0/9c973266017c08b929f9e6adc65cc0fe8405b239f230e5cd4106c1a0b368adc27f110c13ba2a8391ea8437b915c6ba81d565ca3200e117a4109a067e1418928c - languageName: node - linkType: hard - -"@graphiql/toolkit@npm:^0.9.1, @graphiql/toolkit@npm:^0.9.2": - version: 0.9.2 - resolution: "@graphiql/toolkit@npm:0.9.2" - dependencies: - "@n1ru4l/push-pull-async-iterable-iterator": "npm:^3.1.0" - meros: "npm:^1.1.4" - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 - graphql-ws: ">= 4.5.0" - peerDependenciesMeta: - graphql-ws: - optional: true - checksum: 10c0/c4224b48aabe7756760d1ed06199431ee993b7d36d180cc7965147a8da218a6c71b7ee1dab24924e702e9a5417dd1539780f99d7ef489e801e3ff4806a9a92c3 - languageName: node - linkType: hard - -"@graphql-hive/signal@npm:^1.0.0": - version: 1.0.0 - resolution: "@graphql-hive/signal@npm:1.0.0" - checksum: 10c0/5c771417b29fa793b93d5060753ff9470425dbafe186d2a652b464e9a2a58e5e885a0cdf84d8316acc30bd6c05608b778686bb482bfe311ca410349dcaa7731f - languageName: node - linkType: hard - -"@graphql-tools/batch-execute@npm:^9.0.19": - version: 9.0.19 - resolution: "@graphql-tools/batch-execute@npm:9.0.19" - dependencies: - "@graphql-tools/utils": "npm:^10.9.1" - "@whatwg-node/promise-helpers": "npm:^1.3.0" - dataloader: "npm:^2.2.3" - tslib: "npm:^2.8.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/1e3598896492113572d6e586f89bb8856ccc21e3a915e75bd67b1d02e7deaf33e011a21b9ceaacb3abbe63c83ea3d982e106e71e5c7c84f965c114dfe46d10bd - languageName: node - linkType: hard - -"@graphql-tools/delegate@npm:^10.2.23": - version: 10.2.23 - resolution: "@graphql-tools/delegate@npm:10.2.23" - dependencies: - "@graphql-tools/batch-execute": "npm:^9.0.19" - "@graphql-tools/executor": "npm:^1.4.9" - "@graphql-tools/schema": "npm:^10.0.25" - "@graphql-tools/utils": "npm:^10.9.1" - "@repeaterjs/repeater": "npm:^3.0.6" - "@whatwg-node/promise-helpers": "npm:^1.3.0" - dataloader: "npm:^2.2.3" - dset: "npm:^3.1.2" - tslib: "npm:^2.8.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/bcc917cad5f8d21c593ca3382e1808b19c75ed3161ab42b2b69cb43eb53183408d27d1c77d9bf9c6f71971cd9c48109eb78183d2451ab28834a63f7fe855e788 - languageName: node - linkType: hard - -"@graphql-tools/executor-common@npm:^0.0.4": - version: 0.0.4 - resolution: "@graphql-tools/executor-common@npm:0.0.4" - dependencies: - "@envelop/core": "npm:^5.2.3" - "@graphql-tools/utils": "npm:^10.8.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/4cda40687e3c42f0fefc9950f5e3d89021007101c3d6aa5dba2a07e6a0ef14cc0d04424aa8777e74476d5165fb22219de175dff8a4826e520fdbee8be0d4a81d - languageName: node - linkType: hard - -"@graphql-tools/executor-common@npm:^0.0.6": - version: 0.0.6 - resolution: "@graphql-tools/executor-common@npm:0.0.6" - dependencies: - "@envelop/core": "npm:^5.3.0" - "@graphql-tools/utils": "npm:^10.9.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/2f7dda600983a69f0f1524965f2c665195c46d986fb88e4e2fe4653c70e95c59b09a263a2fee3f8ebc4b8107180c9fa16d472454119f5e10f7745b9c1eb589d2 - languageName: node - linkType: hard - -"@graphql-tools/executor-graphql-ws@npm:^2.0.1": - version: 2.0.7 - resolution: "@graphql-tools/executor-graphql-ws@npm:2.0.7" - dependencies: - "@graphql-tools/executor-common": "npm:^0.0.6" - "@graphql-tools/utils": "npm:^10.9.1" - "@whatwg-node/disposablestack": "npm:^0.0.6" - graphql-ws: "npm:^6.0.6" - isomorphic-ws: "npm:^5.0.0" - tslib: "npm:^2.8.1" - ws: "npm:^8.18.3" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/6f94c2ec9ca5866bdfc6c8a95acba69e24991960323a8b5066e19cd6aa38142db56cf4324a077bbc02b6518a50c2ca8eba9b8b018d4655c520c7806c4736ad35 - languageName: node - linkType: hard - -"@graphql-tools/executor-http@npm:^1.1.9": - version: 1.3.3 - resolution: "@graphql-tools/executor-http@npm:1.3.3" - dependencies: - "@graphql-hive/signal": "npm:^1.0.0" - "@graphql-tools/executor-common": "npm:^0.0.4" - "@graphql-tools/utils": "npm:^10.8.1" - "@repeaterjs/repeater": "npm:^3.0.4" - "@whatwg-node/disposablestack": "npm:^0.0.6" - "@whatwg-node/fetch": "npm:^0.10.4" - "@whatwg-node/promise-helpers": "npm:^1.3.0" - meros: "npm:^1.2.1" - tslib: "npm:^2.8.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/317259d5e4e08baea728aecdfcce56cac77fd4f68b34975befca94d74d9d989d168230a735e51426a94740335ece9a93ab6fce280196ecf993a4ea1c0097c083 - languageName: node - linkType: hard - -"@graphql-tools/executor-legacy-ws@npm:^1.1.19": - version: 1.1.25 - resolution: "@graphql-tools/executor-legacy-ws@npm:1.1.25" - dependencies: - "@graphql-tools/utils": "npm:^11.0.0" - "@types/ws": "npm:^8.0.0" - isomorphic-ws: "npm:^5.0.0" - tslib: "npm:^2.4.0" - ws: "npm:^8.19.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/42184c80aa52ce10af503658f606ada1871c2881f808c3a76bf3e48cd16ea89e6edd4e97a843b6249e4b7ac66566e2349914df1bfeadfc0e0ec3f3a84f9fb1f0 - languageName: node - linkType: hard - -"@graphql-tools/executor@npm:^1.4.9": - version: 1.5.1 - resolution: "@graphql-tools/executor@npm:1.5.1" - dependencies: - "@graphql-tools/utils": "npm:^11.0.0" - "@graphql-typed-document-node/core": "npm:^3.2.0" - "@repeaterjs/repeater": "npm:^3.0.4" - "@whatwg-node/disposablestack": "npm:^0.0.6" - "@whatwg-node/promise-helpers": "npm:^1.0.0" - tslib: "npm:^2.4.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/6d8d4dd9c005bd280da2bf61c8606f0dc1f0b0d6f518c5020b1d24854fd3cf8627843e21cd46ae5a505e3271cb88db0204fad8aaf5949020ebfa2cf26971c7b3 - languageName: node - linkType: hard - -"@graphql-tools/graphql-file-loader@npm:^8.0.0": - version: 8.1.10 - resolution: "@graphql-tools/graphql-file-loader@npm:8.1.10" - dependencies: - "@graphql-tools/import": "npm:7.1.10" - "@graphql-tools/utils": "npm:^11.0.0" - globby: "npm:^11.0.3" - tslib: "npm:^2.4.0" - unixify: "npm:^1.0.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/696d512098d262cecc6d123ee7e6f757ffb8b4ba2093f5049a910d8beb06737ffbd68562d954d48921535b15b4eabd122d7e29df6816a83b4844f9fdbd7925be + +"@google-cloud/promisify@npm:<4.1.0": + version: 4.0.0 + resolution: "@google-cloud/promisify@npm:4.0.0" + checksum: 10c0/4332cbd923d7c6943ecdf46f187f1417c84bb9c801525cd74d719c766bfaad650f7964fb74576345f6537b6d6273a4f2992c8d79ebec6c8b8401b23d626b8dd3 languageName: node linkType: hard -"@graphql-tools/import@npm:7.1.10": - version: 7.1.10 - resolution: "@graphql-tools/import@npm:7.1.10" +"@google-cloud/storage@npm:^7.0.0": + version: 7.18.0 + resolution: "@google-cloud/storage@npm:7.18.0" dependencies: - "@graphql-tools/utils": "npm:^11.0.0" - "@theguild/federation-composition": "npm:^0.21.3" - resolve-from: "npm:5.0.0" - tslib: "npm:^2.4.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/b8b64cfc11d20dc5439d12aa58965ec270dba879344338f8ec50873b9bc4e17a0db6472e45435626912aa04709497fc4d9e41c38842562bc5cb2546b674a57eb + "@google-cloud/paginator": "npm:^5.0.0" + "@google-cloud/projectify": "npm:^4.0.0" + "@google-cloud/promisify": "npm:<4.1.0" + abort-controller: "npm:^3.0.0" + async-retry: "npm:^1.3.3" + duplexify: "npm:^4.1.3" + fast-xml-parser: "npm:^4.4.1" + gaxios: "npm:^6.0.2" + google-auth-library: "npm:^9.6.3" + html-entities: "npm:^2.5.2" + mime: "npm:^3.0.0" + p-limit: "npm:^3.0.1" + retry-request: "npm:^7.0.0" + teeny-request: "npm:^9.0.0" + uuid: "npm:^8.0.0" + checksum: 10c0/1879a7c60a0a23890067d0b17359da701d0504e46b8e4c0b3cdfd29dcd54fcaaddada68206d1d14fafadea86eb0a885bd8cc725c453def845f9bd9aae2cc3a85 languageName: node linkType: hard -"@graphql-tools/json-file-loader@npm:^8.0.0": - version: 8.0.26 - resolution: "@graphql-tools/json-file-loader@npm:8.0.26" +"@graphiql/react@npm:0.29.0, @graphiql/react@npm:^0.29.0": + version: 0.29.0 + resolution: "@graphiql/react@npm:0.29.0" dependencies: - "@graphql-tools/utils": "npm:^11.0.0" - globby: "npm:^11.0.3" - tslib: "npm:^2.4.0" - unixify: "npm:^1.0.0" + "@graphiql/toolkit": "npm:^0.11.2" + "@headlessui/react": "npm:^1.7.15" + "@radix-ui/react-dialog": "npm:^1.0.4" + "@radix-ui/react-dropdown-menu": "npm:^2.0.5" + "@radix-ui/react-tooltip": "npm:^1.0.6" + "@radix-ui/react-visually-hidden": "npm:^1.0.3" + "@types/codemirror": "npm:^5.60.8" + clsx: "npm:^1.2.1" + codemirror: "npm:^5.65.3" + codemirror-graphql: "npm:^2.2.1" + copy-to-clipboard: "npm:^3.2.0" + framer-motion: "npm:^6.5.1" + get-value: "npm:^3.0.1" + graphql-language-service: "npm:^5.3.1" + markdown-it: "npm:^14.1.0" + react-compiler-runtime: "npm:19.1.0-rc.1" + set-value: "npm:^4.1.0" peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/899f388d639c2ab4881b0e6892c01c4dafe82954b14ad37ab7ce32daf12424f3bdbefb2dc299ce771b5af0d6d1b4f48c99affa961ccf4e68396be3aed20822ad + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: 10c0/514879b5a884213e9b30f77ec60ee131a9ed8ee1043430f689705925db7175579ebf20be69380ec8fbcc97a27271944b755012adac2f5cea8fb5fa14339547e9 languageName: node linkType: hard -"@graphql-tools/load@npm:^8.1.0": - version: 8.1.8 - resolution: "@graphql-tools/load@npm:8.1.8" +"@graphiql/toolkit@npm:^0.11.2": + version: 0.11.3 + resolution: "@graphiql/toolkit@npm:0.11.3" dependencies: - "@graphql-tools/schema": "npm:^10.0.31" - "@graphql-tools/utils": "npm:^11.0.0" - p-limit: "npm:3.1.0" - tslib: "npm:^2.4.0" + "@n1ru4l/push-pull-async-iterable-iterator": "npm:^3.1.0" + meros: "npm:^1.1.4" peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/87d26ee4363223e0f187dd6d4fd41c2dee80ec87ce1f01faf7174ac1ca0b85ac537f466a084812024cd2857a0df17c71afae3567998b84cc7a65b65bb84c0663 + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + graphql-ws: ">= 4.5.0" + peerDependenciesMeta: + graphql-ws: + optional: true + checksum: 10c0/dda0801680076586d41344bea5d4d42f0a0fe68913ed1c0916331004552e9aa7090986d73d29e358b814441425895c48fe20a448c775102e883c2ed5a5b904e2 languageName: node linkType: hard @@ -5700,31 +5975,6 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/merge@npm:^9.0.0, @graphql-tools/merge@npm:^9.1.7": - version: 9.1.7 - resolution: "@graphql-tools/merge@npm:9.1.7" - dependencies: - "@graphql-tools/utils": "npm:^11.0.0" - tslib: "npm:^2.4.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/b1dbebb12419e9d1d2f2ae6a92bbbe67dca4def052b6225259c3fee5d98a178668614f7faf68cfb2097e6598a7ef69309e1e9e93db6a5e219731b7613ab9805a - languageName: node - linkType: hard - -"@graphql-tools/schema@npm:^10.0.25, @graphql-tools/schema@npm:^10.0.31": - version: 10.0.31 - resolution: "@graphql-tools/schema@npm:10.0.31" - dependencies: - "@graphql-tools/merge": "npm:^9.1.7" - "@graphql-tools/utils": "npm:^11.0.0" - tslib: "npm:^2.4.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/d95b969d8f118e642a963b4a947314e475affba0acc0470132e52164d7fd483eccf13c8dbf70b33be535309af17b480fd7061c947ce6d76092d7d2a3527a499f - languageName: node - linkType: hard - "@graphql-tools/schema@npm:^8.5.0": version: 8.5.1 resolution: "@graphql-tools/schema@npm:8.5.1" @@ -5739,28 +5989,6 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/url-loader@npm:^8.0.0": - version: 8.0.33 - resolution: "@graphql-tools/url-loader@npm:8.0.33" - dependencies: - "@graphql-tools/executor-graphql-ws": "npm:^2.0.1" - "@graphql-tools/executor-http": "npm:^1.1.9" - "@graphql-tools/executor-legacy-ws": "npm:^1.1.19" - "@graphql-tools/utils": "npm:^10.9.1" - "@graphql-tools/wrap": "npm:^10.0.16" - "@types/ws": "npm:^8.0.0" - "@whatwg-node/fetch": "npm:^0.10.0" - "@whatwg-node/promise-helpers": "npm:^1.0.0" - isomorphic-ws: "npm:^5.0.0" - sync-fetch: "npm:0.6.0-2" - tslib: "npm:^2.4.0" - ws: "npm:^8.17.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/d63dda8c10d0505ea28d95648ca86aefd6328082fc46a2afbc3bfc173f712b50c8aa56d67d1ad50c0b0d4621c9d64453eec3cb36e7f811a731bbf8cf6a8662be - languageName: node - linkType: hard - "@graphql-tools/utils@npm:8.9.0": version: 8.9.0 resolution: "@graphql-tools/utils@npm:8.9.0" @@ -5772,34 +6000,6 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.8.1, @graphql-tools/utils@npm:^10.9.1": - version: 10.11.0 - resolution: "@graphql-tools/utils@npm:10.11.0" - dependencies: - "@graphql-typed-document-node/core": "npm:^3.1.1" - "@whatwg-node/promise-helpers": "npm:^1.0.0" - cross-inspect: "npm:1.0.1" - tslib: "npm:^2.4.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/73459332c199d8f3aa698bdee4ac6ce802274dba95cc7eff1f0219b6fe6e3a8f314d2e824168e296df8f5ce18f6dbd23ca14406b71890a41ce80b7548e8ccd6d - languageName: node - linkType: hard - -"@graphql-tools/utils@npm:^11.0.0": - version: 11.0.0 - resolution: "@graphql-tools/utils@npm:11.0.0" - dependencies: - "@graphql-typed-document-node/core": "npm:^3.1.1" - "@whatwg-node/promise-helpers": "npm:^1.0.0" - cross-inspect: "npm:1.0.1" - tslib: "npm:^2.4.0" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/008f7d033b900bab7daf073c34d2b5502f52005a3dfa9761dfd550d64734c7cd47c2cc92f130f24c5911ceed91315494b54e6849ed345004e59e84377ab1f6c7 - languageName: node - linkType: hard - "@graphql-tools/utils@npm:^8.8.0": version: 8.13.1 resolution: "@graphql-tools/utils@npm:8.13.1" @@ -5811,30 +6011,6 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/wrap@npm:^10.0.16": - version: 10.1.4 - resolution: "@graphql-tools/wrap@npm:10.1.4" - dependencies: - "@graphql-tools/delegate": "npm:^10.2.23" - "@graphql-tools/schema": "npm:^10.0.25" - "@graphql-tools/utils": "npm:^10.9.1" - "@whatwg-node/promise-helpers": "npm:^1.3.0" - tslib: "npm:^2.8.1" - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/a89d92930b243683b4a7ae5a0104d75ae9b9e3e4cf030f9a25daa04760c7ff05861c108d1e50199091ce036e96cd85bf253a891aa2a87ef737a946b6bb99fd63 - languageName: node - linkType: hard - -"@graphql-typed-document-node/core@npm:^3.1.1, @graphql-typed-document-node/core@npm:^3.2.0": - version: 3.2.0 - resolution: "@graphql-typed-document-node/core@npm:3.2.0" - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c - languageName: node - linkType: hard - "@grpc/grpc-js@npm:^1.10.9, @grpc/grpc-js@npm:^1.11.1": version: 1.14.3 resolution: "@grpc/grpc-js@npm:1.14.3" @@ -5966,12 +6142,12 @@ __metadata: languageName: node linkType: hard -"@internationalized/date@npm:^3.10.1": - version: 3.10.1 - resolution: "@internationalized/date@npm:3.10.1" +"@internationalized/date@npm:^3.12.0": + version: 3.12.0 + resolution: "@internationalized/date@npm:3.12.0" dependencies: "@swc/helpers": "npm:^0.5.0" - checksum: 10c0/2b7a8144a97baf0c8bd9f3ef28fe86238e2cfde3b837c943aa03bd07354a04753bab3fd7162e5865c284f5b2616e832c9eee395dec92c0fed4eff57615d9d940 + checksum: 10c0/6a26495d32f010b227a1f506da02cdf8438506014b41cfb81576c707a3dfe3d0fd207f80bcf28acd9eef8248a2c2da115cf9016515d513653ea1b22a796d0246 languageName: node linkType: hard @@ -7166,46 +7342,61 @@ __metadata: languageName: node linkType: hard -"@module-federation/bridge-react-webpack-plugin@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.9.1" +"@module-federation/bridge-react-webpack-plugin@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.21.6" dependencies: - "@module-federation/sdk": "npm:0.9.1" + "@module-federation/sdk": "npm:0.21.6" "@types/semver": "npm:7.5.8" semver: "npm:7.6.3" - checksum: 10c0/c930bb23b04c42de45d1973200e0e2133f4c234fad6ffa36e7cc48dcb52070b522bac202ff9e6bbe85e572b8077395b0526c4ce58681a7d2caf2c2ca98115a3d + checksum: 10c0/d7dbf7615568037def06fe709bafe5f165679b9c914795f304f9bc60805ecc5ed35a436f26c34ed51a870cb2cf688076deb44818a2a400486559989ac90b38d3 languageName: node linkType: hard -"@module-federation/data-prefetch@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/data-prefetch@npm:0.9.1" +"@module-federation/cli@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/cli@npm:0.21.6" + dependencies: + "@module-federation/dts-plugin": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" + chalk: "npm:3.0.0" + commander: "npm:11.1.0" + jiti: "npm:2.4.2" + bin: + mf: bin/mf.js + checksum: 10c0/d4267309535924c8f897768adacbe8bff6082124d3dc63a2f7f7102cae139082c96aaf611e96bce079dc7b8eaac091d9a1dc953b1840d89fdaed88f3203473b9 + languageName: node + linkType: hard + +"@module-federation/data-prefetch@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/data-prefetch@npm:0.21.6" dependencies: - "@module-federation/runtime": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" + "@module-federation/runtime": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" fs-extra: "npm:9.1.0" peerDependencies: react: ">=16.9.0" react-dom: ">=16.9.0" - checksum: 10c0/5242b8583c4f5278c71f138e40695d0d54e0e6437f6b9fcb83e531bb26d6367ed814bb4ae734f3563a805948904d1e1b7aed037caf8ab65bcbcfd7aa9375a9b8 + checksum: 10c0/f3cbdc431b8e5dbaa6a4046172cc5efce4437d5bddcce2b64b5bd27858ea9847dd520c9af30331d7a9d6c70e5369cad85cc1b33a2654529bcb01f5988ada1c7a languageName: node linkType: hard -"@module-federation/dts-plugin@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/dts-plugin@npm:0.9.1" +"@module-federation/dts-plugin@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/dts-plugin@npm:0.21.6" dependencies: - "@module-federation/error-codes": "npm:0.9.1" - "@module-federation/managers": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" - "@module-federation/third-party-dts-extractor": "npm:0.9.1" + "@module-federation/error-codes": "npm:0.21.6" + "@module-federation/managers": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" + "@module-federation/third-party-dts-extractor": "npm:0.21.6" adm-zip: "npm:^0.5.10" ansi-colors: "npm:^4.1.3" - axios: "npm:^1.7.4" + axios: "npm:^1.12.0" chalk: "npm:3.0.0" fs-extra: "npm:9.1.0" isomorphic-ws: "npm:5.0.0" - koa: "npm:2.15.4" + koa: "npm:3.0.3" lodash.clonedeepwith: "npm:4.5.0" log4js: "npm:6.9.1" node-schedule: "npm:2.1.1" @@ -7217,25 +7408,27 @@ __metadata: peerDependenciesMeta: vue-tsc: optional: true - checksum: 10c0/208d8e7176d486d7b146027b26b706eeb6d231ad0e4806adf2aa5293c9d5dfbace5c98eb52f6c151e29ff9cb789eb0344445239f1739c23a4d0102393be741c4 + checksum: 10c0/d4d6477de9fb76f57feafd5bfd2b9bd35f15a35620504a425f0c6abd65c1ff7d6957e6abc85ea17c4f4201a6be7178f71d737e8badc57e77806bc5426990ce92 languageName: node linkType: hard -"@module-federation/enhanced@npm:^0.9.0": - version: 0.9.1 - resolution: "@module-federation/enhanced@npm:0.9.1" - dependencies: - "@module-federation/bridge-react-webpack-plugin": "npm:0.9.1" - "@module-federation/data-prefetch": "npm:0.9.1" - "@module-federation/dts-plugin": "npm:0.9.1" - "@module-federation/error-codes": "npm:0.9.1" - "@module-federation/inject-external-runtime-core-plugin": "npm:0.9.1" - "@module-federation/managers": "npm:0.9.1" - "@module-federation/manifest": "npm:0.9.1" - "@module-federation/rspack": "npm:0.9.1" - "@module-federation/runtime-tools": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" +"@module-federation/enhanced@npm:^0.21.6": + version: 0.21.6 + resolution: "@module-federation/enhanced@npm:0.21.6" + dependencies: + "@module-federation/bridge-react-webpack-plugin": "npm:0.21.6" + "@module-federation/cli": "npm:0.21.6" + "@module-federation/data-prefetch": "npm:0.21.6" + "@module-federation/dts-plugin": "npm:0.21.6" + "@module-federation/error-codes": "npm:0.21.6" + "@module-federation/inject-external-runtime-core-plugin": "npm:0.21.6" + "@module-federation/managers": "npm:0.21.6" + "@module-federation/manifest": "npm:0.21.6" + "@module-federation/rspack": "npm:0.21.6" + "@module-federation/runtime-tools": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" btoa: "npm:^1.2.1" + schema-utils: "npm:^4.3.0" upath: "npm:2.0.1" peerDependencies: typescript: ^4.9.0 || ^5.0.0 @@ -7248,7 +7441,16 @@ __metadata: optional: true webpack: optional: true - checksum: 10c0/60f091b022a15f00796ffe32f8e771cfdd4e7381ab55056676ba71b2930365f3b3fe7c02f1cacf1df53b1693e122e6061f74c413485da1936aca53617c1a4072 + bin: + mf: bin/mf.js + checksum: 10c0/1df3cc856543cbf79c24fc2678e3c8acbaa8c6aa65a23ac5f61b165c7e765fd9255f9ab0408a6869f2a3ced345f137e298e74b3e5dc000a3b5e1692062a368c9 + languageName: node + linkType: hard + +"@module-federation/error-codes@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/error-codes@npm:0.21.6" + checksum: 10c0/365ca6350fac7882e86730dec34bd62871161638850727604a8a7b30ac3479a62d95a9e6ee39faa6f8dfc57a960fd91274df325fbda12bbbdd554a4258d8ed7d languageName: node linkType: hard @@ -7259,57 +7461,51 @@ __metadata: languageName: node linkType: hard -"@module-federation/error-codes@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/error-codes@npm:0.9.1" - checksum: 10c0/4134944357fafcf6cda301089b2cb97144bd3b5ddc06dbbdfe939a0290c002902a1094e7aab571d5439e9fe6d564766457948924e60c0d161d8f517318c0fa77 - languageName: node - linkType: hard - -"@module-federation/inject-external-runtime-core-plugin@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/inject-external-runtime-core-plugin@npm:0.9.1" +"@module-federation/inject-external-runtime-core-plugin@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/inject-external-runtime-core-plugin@npm:0.21.6" peerDependencies: - "@module-federation/runtime-tools": 0.9.1 - checksum: 10c0/c16129a7294ca9e0a62bd98784f5561ae8f7bceab5a1ba1c4ba912437849a2fe41b2d2af8631c675e688d854ce4d3155daab9752ac446e31ad453b522982ec95 + "@module-federation/runtime-tools": 0.21.6 + checksum: 10c0/c05aa3445a4f2f885b5c7cfadc86b75e4031f20e0760efcc4922f86268038cc9fdd883efb3cd7828c393ba494ab61d73b544b2c331b850be1012905c8d349359 languageName: node linkType: hard -"@module-federation/managers@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/managers@npm:0.9.1" +"@module-federation/managers@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/managers@npm:0.21.6" dependencies: - "@module-federation/sdk": "npm:0.9.1" + "@module-federation/sdk": "npm:0.21.6" find-pkg: "npm:2.0.0" fs-extra: "npm:9.1.0" - checksum: 10c0/c13447fc1266245d52b73018fbf7d09b986b06b227019da4fbcb3304829314883688d114dd47e2cc97254be4ed361272134e76bfeaabc3d9220400d2b24050f3 + checksum: 10c0/5dd26a4236f853525753c5868df77be5943793d6ea3f477dff45788931ddb422b067f520cfb75018e9d5148f4e5d9e799673ea530da9bc5369f43015f8494b95 languageName: node linkType: hard -"@module-federation/manifest@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/manifest@npm:0.9.1" +"@module-federation/manifest@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/manifest@npm:0.21.6" dependencies: - "@module-federation/dts-plugin": "npm:0.9.1" - "@module-federation/managers": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" + "@module-federation/dts-plugin": "npm:0.21.6" + "@module-federation/managers": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" chalk: "npm:3.0.0" find-pkg: "npm:2.0.0" - checksum: 10c0/436905bcacc0d18448f667e55b18f59856c67970dc551055df715d9263e2e4a1a754449115e6c10f61dd9e2a687cd3cf4677390ac49574fce17f4f647302dbac + checksum: 10c0/cc39d88068aaeb10497839428fbcf991b37fd1d72ed9250c2b0c4f0cd0716ff394081a854986ae107ad9c2b56915edf1078319f4aec8a7b8ffd65218873882ea languageName: node linkType: hard -"@module-federation/rspack@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/rspack@npm:0.9.1" +"@module-federation/rspack@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/rspack@npm:0.21.6" dependencies: - "@module-federation/bridge-react-webpack-plugin": "npm:0.9.1" - "@module-federation/dts-plugin": "npm:0.9.1" - "@module-federation/inject-external-runtime-core-plugin": "npm:0.9.1" - "@module-federation/managers": "npm:0.9.1" - "@module-federation/manifest": "npm:0.9.1" - "@module-federation/runtime-tools": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" + "@module-federation/bridge-react-webpack-plugin": "npm:0.21.6" + "@module-federation/dts-plugin": "npm:0.21.6" + "@module-federation/inject-external-runtime-core-plugin": "npm:0.21.6" + "@module-federation/managers": "npm:0.21.6" + "@module-federation/manifest": "npm:0.21.6" + "@module-federation/runtime-tools": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" + btoa: "npm:1.2.1" peerDependencies: "@rspack/core": ">=0.7" typescript: ^4.9.0 || ^5.0.0 @@ -7319,7 +7515,17 @@ __metadata: optional: true vue-tsc: optional: true - checksum: 10c0/e4db2534d5ce5823b64aeda15fbca1ca3dd268966167e60b51bd326c80b902c38fb2ba4b05500b5b602a4eca3736e5b1b38174649d94f01953d8858a02d71fd3 + checksum: 10c0/2b3b62b81ce01c2c0e0c4dc42dba4d2280a44facc049e7e98c24685f02015e05ddfdcddda9bb7041c27a33c89bb12e2c8fe1b92613603894e4b62bca2fc61c0e + languageName: node + linkType: hard + +"@module-federation/runtime-core@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/runtime-core@npm:0.21.6" + dependencies: + "@module-federation/error-codes": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" + checksum: 10c0/df986606a9f6b0f56cc9c261d497c852a1dba0e6817be5b9150db3a3d205242a6a0fedb0ad247aaa9bfb489aaeb3d58adcc7db44f4a3205cf20c5a8035e974f8 languageName: node linkType: hard @@ -7333,13 +7539,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-core@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/runtime-core@npm:0.9.1" +"@module-federation/runtime-tools@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/runtime-tools@npm:0.21.6" dependencies: - "@module-federation/error-codes": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" - checksum: 10c0/1b4174a74536c22757fb0ac0e6adb2d86c45857a36ec42bd4342fcb2fe124f59c5d323e0a69c78f2b50b3115390cf2ff4d3c8a8b21c610aa4ca40e8b2b28e5bf + "@module-federation/runtime": "npm:0.21.6" + "@module-federation/webpack-bundler-runtime": "npm:0.21.6" + checksum: 10c0/4cb9fa9dcde2101359ade4bdeb2e80c19c65f0ae265ad3877edfae2117f4b1e908e8d76d44ce5ae61d32ce73e7bf4238e24e4ad67115d64c994dbe169dc96b05 languageName: node linkType: hard @@ -7353,13 +7559,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/runtime-tools@npm:0.9.1" +"@module-federation/runtime@npm:0.21.6, @module-federation/runtime@npm:^0.21.6": + version: 0.21.6 + resolution: "@module-federation/runtime@npm:0.21.6" dependencies: - "@module-federation/runtime": "npm:0.9.1" - "@module-federation/webpack-bundler-runtime": "npm:0.9.1" - checksum: 10c0/41ca39964b27eda61d2db58b904d15f63c2e29fb83f06138f3628c055e5d7511015552b46cbce2a92a0ad9ecc8c0103243aaccc54c3bf620736e23dfe85b8689 + "@module-federation/error-codes": "npm:0.21.6" + "@module-federation/runtime-core": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" + checksum: 10c0/76596433cd914021cdeacefd461303f08c8daeb002d6c13659ce4e3d10f36873ce3a89edca432074b7484e2f5597e53d619ba425b11656b212e3d3ce775b8fb8 languageName: node linkType: hard @@ -7374,14 +7581,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/runtime@npm:0.9.1" - dependencies: - "@module-federation/error-codes": "npm:0.9.1" - "@module-federation/runtime-core": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" - checksum: 10c0/c63f3f9ef23d14f3b1a84c9e04cb266fcfa041446841bfa8aff7170e84a40139d642532bd6c535389e1db0b29c7b0bfd2f7d0a0a65c9977d796b54dd90381884 +"@module-federation/sdk@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/sdk@npm:0.21.6" + checksum: 10c0/54f33fb48e1f3db09e03b529af9f28fcd8007c4dbd8b197cb0691c392f3eb143961c526e1ecd400f1d652451976a378da962edbb961f5859f33f56edda527f88 languageName: node linkType: hard @@ -7392,21 +7595,24 @@ __metadata: languageName: node linkType: hard -"@module-federation/sdk@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/sdk@npm:0.9.1" - checksum: 10c0/2475c57386f2ecd0d9a9772861fdc946ce4eef6c112bef0526a2aacc38c3d48524c5fdb24dd6322d12845432abaef450cf5ba7e8138a9e152e7cae741e3692d3 - languageName: node - linkType: hard - -"@module-federation/third-party-dts-extractor@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/third-party-dts-extractor@npm:0.9.1" +"@module-federation/third-party-dts-extractor@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/third-party-dts-extractor@npm:0.21.6" dependencies: find-pkg: "npm:2.0.0" fs-extra: "npm:9.1.0" resolve: "npm:1.22.8" - checksum: 10c0/907bf3ab96c8f767669388668992cbb2dfee331ec30234ddf068f13fdb238547c223d1cb0d64326333d111698a589f86dc932159aadba7ff439039f4b839fce6 + checksum: 10c0/41643d616b2dd45455e8a53fb9f4aee6145b396abb667a59bd0ae8e3eabf331bbafc32c411e8eb8345a3c797c7deabe71d042b9ab2b30f8e571563cb48e8f5f8 + languageName: node + linkType: hard + +"@module-federation/webpack-bundler-runtime@npm:0.21.6": + version: 0.21.6 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.21.6" + dependencies: + "@module-federation/runtime": "npm:0.21.6" + "@module-federation/sdk": "npm:0.21.6" + checksum: 10c0/0767ace8f5002d2bcc4ace9fca25440b95f980980fcba3ffd9da1c8452ec608dc2cc8c79a6a49d259b1fa548134ad2e2e6e89d0ae42e41b4e7be047755996daf languageName: node linkType: hard @@ -7420,16 +7626,6 @@ __metadata: languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.9.1": - version: 0.9.1 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.9.1" - dependencies: - "@module-federation/runtime": "npm:0.9.1" - "@module-federation/sdk": "npm:0.9.1" - checksum: 10c0/9250ebb8721a64043ecc244acc308680b8a52cb95cebe92cd2d1099dd3e7ab0c3f53d893a5c7d3fbe5a86f133283bed97226c2232c316f281258de12e2239743 - languageName: node - linkType: hard - "@motionone/animation@npm:^10.12.0": version: 10.18.0 resolution: "@motionone/animation@npm:10.18.0" @@ -7679,6 +7875,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.4.0": + version: 1.4.0 + resolution: "@noble/hashes@npm:1.4.0" + checksum: 10c0/8c3f005ee72e7b8f9cff756dfae1241485187254e3f743873e22073d63906863df5d4f13d441b7530ea614b7a093f0d889309f28b59850f33b66cb26a779a4a5 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -7799,6 +8002,13 @@ __metadata: languageName: node linkType: hard +"@octokit/auth-callback@npm:^5.0.0": + version: 5.0.1 + resolution: "@octokit/auth-callback@npm:5.0.1" + checksum: 10c0/851d0930aeecddc626655469ef57727654af30755264d5139d9fdf61ba9fc88c1a8685f5721f853c806c570b8529d25d262f4efe76da90c08c779d9cc1c37edf + languageName: node + linkType: hard + "@octokit/auth-oauth-app@npm:^5.0.0": version: 5.0.6 resolution: "@octokit/auth-oauth-app@npm:5.0.6" @@ -8294,7 +8504,7 @@ __metadata: languageName: node linkType: hard -"@octokit/webhooks-types@npm:7.6.1": +"@octokit/webhooks-types@npm:7.6.1, @octokit/webhooks-types@npm:^7.6.1": version: 7.6.1 resolution: "@octokit/webhooks-types@npm:7.6.1" checksum: 10c0/7c2cb40f9ccd2bd392cf35c23f995ae0719ef35fd3bce0264ced5518cbf0a7087bd069bf5e5963fc33d0232726968db526912df3cb017c1bd1761c8849c31a30 @@ -8365,68 +8575,213 @@ __metadata: languageName: node linkType: hard -"@patternfly/react-icons@npm:^6.0.0, @patternfly/react-icons@npm:^6.4.0": - version: 6.4.0 - resolution: "@patternfly/react-icons@npm:6.4.0" - peerDependencies: - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - checksum: 10c0/2072babe83bad4de05e71b27c1180886912c99f24de8e5358cad06e9410a9643171b5dcda69d7e5c36f1010a2457ec9fdbb89cc9d54d92e63ede82de12c543cb +"@patternfly/react-icons@npm:^6.0.0, @patternfly/react-icons@npm:^6.4.0": + version: 6.4.0 + resolution: "@patternfly/react-icons@npm:6.4.0" + peerDependencies: + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + checksum: 10c0/2072babe83bad4de05e71b27c1180886912c99f24de8e5358cad06e9410a9643171b5dcda69d7e5c36f1010a2457ec9fdbb89cc9d54d92e63ede82de12c543cb + languageName: node + linkType: hard + +"@patternfly/react-styles@npm:^6.0.0, @patternfly/react-styles@npm:^6.4.0": + version: 6.4.0 + resolution: "@patternfly/react-styles@npm:6.4.0" + checksum: 10c0/ed94ec58ddeb21eeecb24a6bbaf2e64d2fb4e641d1e8e49107fa162e7b115a23f9e59242d390937cc2d48a1ee5b2e1133c133e9dd47a5b9b08bf9a157bf8b437 + languageName: node + linkType: hard + +"@patternfly/react-table@npm:^6.0.0, @patternfly/react-table@npm:^6.3.1": + version: 6.4.1 + resolution: "@patternfly/react-table@npm:6.4.1" + dependencies: + "@patternfly/react-core": "npm:^6.4.1" + "@patternfly/react-icons": "npm:^6.4.0" + "@patternfly/react-styles": "npm:^6.4.0" + "@patternfly/react-tokens": "npm:^6.4.0" + lodash: "npm:^4.17.23" + tslib: "npm:^2.8.1" + peerDependencies: + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + checksum: 10c0/9808a71211a70d7b5e6aead5eff49e06021bf621798e752e4c8490c1bc40a99292be877c8403fc89266aadd7299dcd3731b1354b806793b5d2e5a5db5e7d4706 + languageName: node + linkType: hard + +"@patternfly/react-tokens@npm:^6.0.0, @patternfly/react-tokens@npm:^6.4.0": + version: 6.4.0 + resolution: "@patternfly/react-tokens@npm:6.4.0" + checksum: 10c0/9b49ac8f1703de0e5b2b6d1154dbf83dbb40eea2850ce1f50ac173cd3d2cc9d4a1ca085c7c3db2a538aa6c137db186cbe783f1ab5985e843dfd455e9095f1a93 + languageName: node + linkType: hard + +"@patternfly/react-topology@npm:^6.0.0": + version: 6.4.0 + resolution: "@patternfly/react-topology@npm:6.4.0" + dependencies: + "@dagrejs/dagre": "npm:1.1.2" + "@patternfly/react-core": "npm:^6.0.0" + "@patternfly/react-icons": "npm:^6.0.0" + "@patternfly/react-styles": "npm:^6.0.0" + "@types/d3": "npm:^7.4.0" + "@types/d3-force": "npm:^1.2.1" + d3: "npm:^7.8.0" + mobx: "npm:^6.9.0" + mobx-react: "npm:^7.6.0" + point-in-svg-path: "npm:^1.0.1" + popper.js: "npm:^1.16.1" + tslib: "npm:^2.0.0" + webcola: "npm:3.4.0" + peerDependencies: + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + checksum: 10c0/c4db811821d8b9be67b394a69401ecdd61fb576cda4d84e481ac56997535941e50d7a8f276f778e48a1861b55703deb031859e0c63777814833d08db6115e6e5 + languageName: node + linkType: hard + +"@peculiar/asn1-cms@npm:^2.6.0, @peculiar/asn1-cms@npm:^2.6.1": + version: 2.6.1 + resolution: "@peculiar/asn1-cms@npm:2.6.1" + dependencies: + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + "@peculiar/asn1-x509-attr": "npm:^2.6.1" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/682e952fb35dec229bf54ecaff58bdf56281c1d718b5bcc2da103d67b5be302452c6275300c9f9fce1ed02f03792dab3ebe98c903117e0a5b0d9e5d861356280 + languageName: node + linkType: hard + +"@peculiar/asn1-csr@npm:^2.6.0": + version: 2.6.1 + resolution: "@peculiar/asn1-csr@npm:2.6.1" + dependencies: + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/5ea1ef27bf3879c793acb0b370b9fc1cb45df244b4706cecf075e45b58d19d65e612f4777eb12aa37f2037c1c725e96543ab9caf41d5a92378c5069071deae1f + languageName: node + linkType: hard + +"@peculiar/asn1-ecc@npm:^2.6.0": + version: 2.6.1 + resolution: "@peculiar/asn1-ecc@npm:2.6.1" + dependencies: + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/7804600f12a8993085232839ea020f51a329a195ce991ebbce077668d9ee1e57301bf97d5ef9657bd81717888f36f51f7aed3a9eee59fe4345c55d04eff89927 + languageName: node + linkType: hard + +"@peculiar/asn1-pfx@npm:^2.6.1": + version: 2.6.1 + resolution: "@peculiar/asn1-pfx@npm:2.6.1" + dependencies: + "@peculiar/asn1-cms": "npm:^2.6.1" + "@peculiar/asn1-pkcs8": "npm:^2.6.1" + "@peculiar/asn1-rsa": "npm:^2.6.1" + "@peculiar/asn1-schema": "npm:^2.6.0" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/69c86ed339b945f7c77173da06207af71553a5b033cc1f2bde262085e7b5870543f358a29efd8981ca7247ec7f1c5d722a014cc0979679045909cb13e2ca527e + languageName: node + linkType: hard + +"@peculiar/asn1-pkcs8@npm:^2.6.1": + version: 2.6.1 + resolution: "@peculiar/asn1-pkcs8@npm:2.6.1" + dependencies: + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/d712dc79ab877152f20c1772cbe065f5beb2a20e3dcae7892cc72f3227a1d3f7ae8eecba8bc29cf2b77cfdd8a01b0660f5390a416ca78ca7147f0e3c13d4d755 + languageName: node + linkType: hard + +"@peculiar/asn1-pkcs9@npm:^2.6.0": + version: 2.6.1 + resolution: "@peculiar/asn1-pkcs9@npm:2.6.1" + dependencies: + "@peculiar/asn1-cms": "npm:^2.6.1" + "@peculiar/asn1-pfx": "npm:^2.6.1" + "@peculiar/asn1-pkcs8": "npm:^2.6.1" + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + "@peculiar/asn1-x509-attr": "npm:^2.6.1" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/4a2f815bbeee3f65aea391d5e2287a19701d757d2781b3ecfd908a67028f2752796bd22f8ba3eb486911fcc34b52b0f7c1ff3e3b7d7f04ef58767be9ddbc851d + languageName: node + linkType: hard + +"@peculiar/asn1-rsa@npm:^2.6.0, @peculiar/asn1-rsa@npm:^2.6.1": + version: 2.6.1 + resolution: "@peculiar/asn1-rsa@npm:2.6.1" + dependencies: + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + asn1js: "npm:^3.0.6" + tslib: "npm:^2.8.1" + checksum: 10c0/4d7c71c5bddf7be3b0270c4d95b8274a392185cad4939a7a837d9c4c612601fee1a1ccabe414383b26629fb2013608e60a58ecd665c371617c1f177431a88ff2 languageName: node linkType: hard -"@patternfly/react-styles@npm:^6.0.0, @patternfly/react-styles@npm:^6.4.0": - version: 6.4.0 - resolution: "@patternfly/react-styles@npm:6.4.0" - checksum: 10c0/ed94ec58ddeb21eeecb24a6bbaf2e64d2fb4e641d1e8e49107fa162e7b115a23f9e59242d390937cc2d48a1ee5b2e1133c133e9dd47a5b9b08bf9a157bf8b437 +"@peculiar/asn1-schema@npm:^2.6.0": + version: 2.6.0 + resolution: "@peculiar/asn1-schema@npm:2.6.0" + dependencies: + asn1js: "npm:^3.0.6" + pvtsutils: "npm:^1.3.6" + tslib: "npm:^2.8.1" + checksum: 10c0/8c283b10a2e4aca4cb20d242cde773c9a798ea15a6c221d1474ef483e182d48195aeb5dde3f7b518f236eceb7808ae4438539d41a3aa9ed6d20aa4d36a21a0c2 languageName: node linkType: hard -"@patternfly/react-table@npm:^6.0.0, @patternfly/react-table@npm:^6.3.1": - version: 6.4.1 - resolution: "@patternfly/react-table@npm:6.4.1" +"@peculiar/asn1-x509-attr@npm:^2.6.1": + version: 2.6.1 + resolution: "@peculiar/asn1-x509-attr@npm:2.6.1" dependencies: - "@patternfly/react-core": "npm:^6.4.1" - "@patternfly/react-icons": "npm:^6.4.0" - "@patternfly/react-styles": "npm:^6.4.0" - "@patternfly/react-tokens": "npm:^6.4.0" - lodash: "npm:^4.17.23" + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.1" + asn1js: "npm:^3.0.6" tslib: "npm:^2.8.1" - peerDependencies: - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - checksum: 10c0/9808a71211a70d7b5e6aead5eff49e06021bf621798e752e4c8490c1bc40a99292be877c8403fc89266aadd7299dcd3731b1354b806793b5d2e5a5db5e7d4706 + checksum: 10c0/de8634ec12ef34b430e5a458151e856f954e15fe9e08d056dca51db6962e849a951820ab66d291e2452799576c44221b40087b9350dc3728d3770a46fcdeffc5 languageName: node linkType: hard -"@patternfly/react-tokens@npm:^6.0.0, @patternfly/react-tokens@npm:^6.4.0": - version: 6.4.0 - resolution: "@patternfly/react-tokens@npm:6.4.0" - checksum: 10c0/9b49ac8f1703de0e5b2b6d1154dbf83dbb40eea2850ce1f50ac173cd3d2cc9d4a1ca085c7c3db2a538aa6c137db186cbe783f1ab5985e843dfd455e9095f1a93 +"@peculiar/asn1-x509@npm:^2.6.0, @peculiar/asn1-x509@npm:^2.6.1": + version: 2.6.1 + resolution: "@peculiar/asn1-x509@npm:2.6.1" + dependencies: + "@peculiar/asn1-schema": "npm:^2.6.0" + asn1js: "npm:^3.0.6" + pvtsutils: "npm:^1.3.6" + tslib: "npm:^2.8.1" + checksum: 10c0/2e73a0ce6521eeb2d876e0b52e9fae2de4e2d183be5fba77d5fae9b7724de446d02c0b4e5fb04d4fedb50eed0de842f29f4d7cf2e998eaed6a2d2952f5c52d2c languageName: node linkType: hard -"@patternfly/react-topology@npm:^6.0.0": - version: 6.4.0 - resolution: "@patternfly/react-topology@npm:6.4.0" - dependencies: - "@dagrejs/dagre": "npm:1.1.2" - "@patternfly/react-core": "npm:^6.0.0" - "@patternfly/react-icons": "npm:^6.0.0" - "@patternfly/react-styles": "npm:^6.0.0" - "@types/d3": "npm:^7.4.0" - "@types/d3-force": "npm:^1.2.1" - d3: "npm:^7.8.0" - mobx: "npm:^6.9.0" - mobx-react: "npm:^7.6.0" - point-in-svg-path: "npm:^1.0.1" - popper.js: "npm:^1.16.1" - tslib: "npm:^2.0.0" - webcola: "npm:3.4.0" - peerDependencies: - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - checksum: 10c0/c4db811821d8b9be67b394a69401ecdd61fb576cda4d84e481ac56997535941e50d7a8f276f778e48a1861b55703deb031859e0c63777814833d08db6115e6e5 +"@peculiar/x509@npm:^1.14.2": + version: 1.14.3 + resolution: "@peculiar/x509@npm:1.14.3" + dependencies: + "@peculiar/asn1-cms": "npm:^2.6.0" + "@peculiar/asn1-csr": "npm:^2.6.0" + "@peculiar/asn1-ecc": "npm:^2.6.0" + "@peculiar/asn1-pkcs9": "npm:^2.6.0" + "@peculiar/asn1-rsa": "npm:^2.6.0" + "@peculiar/asn1-schema": "npm:^2.6.0" + "@peculiar/asn1-x509": "npm:^2.6.0" + pvtsutils: "npm:^1.3.6" + reflect-metadata: "npm:^0.2.2" + tslib: "npm:^2.8.1" + tsyringe: "npm:^4.10.0" + checksum: 10c0/949231ca9daf84534bfe255f28a856df497302fed294d227c6a28e50f5cfb67ed1d91afe6db787b88294ce042295243dbcb44455fe2efa5ed07428a74392eec9 languageName: node linkType: hard @@ -8455,6 +8810,42 @@ __metadata: languageName: node linkType: hard +"@pmmmwh/react-refresh-webpack-plugin@npm:^0.6.0": + version: 0.6.2 + resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.6.2" + dependencies: + anser: "npm:^2.1.1" + core-js-pure: "npm:^3.23.3" + error-stack-parser: "npm:^2.0.6" + html-entities: "npm:^2.1.0" + schema-utils: "npm:^4.2.0" + source-map: "npm:^0.7.3" + peerDependencies: + "@types/webpack": 5.x + react-refresh: ">=0.10.0 <1.0.0" + sockjs-client: ^1.4.0 + type-fest: ">=0.17.0 <6.0.0" + webpack: ^5.0.0 + webpack-dev-server: ^4.8.0 || 5.x + webpack-hot-middleware: 2.x + webpack-plugin-serve: 1.x + peerDependenciesMeta: + "@types/webpack": + optional: true + sockjs-client: + optional: true + type-fest: + optional: true + webpack-dev-server: + optional: true + webpack-hot-middleware: + optional: true + webpack-plugin-serve: + optional: true + checksum: 10c0/8559c40ae1a7cc2bd6d1cf512fbeb61f94db5e965199998dd5d5d58294176ebf06309d223ad898e2ff4d4e6b053d5a25c928ef4c43398116a82f3294d227a6e1 + languageName: node + linkType: hard + "@popperjs/core@npm:^2.11.8": version: 2.11.8 resolution: "@popperjs/core@npm:2.11.8" @@ -9127,435 +9518,436 @@ __metadata: languageName: node linkType: hard -"@react-aria/autocomplete@npm:3.0.0-rc.4": - version: 3.0.0-rc.4 - resolution: "@react-aria/autocomplete@npm:3.0.0-rc.4" +"@react-aria/autocomplete@npm:3.0.0-rc.6": + version: 3.0.0-rc.6 + resolution: "@react-aria/autocomplete@npm:3.0.0-rc.6" dependencies: - "@react-aria/combobox": "npm:^3.14.1" - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/listbox": "npm:^3.15.1" - "@react-aria/searchfield": "npm:^3.8.10" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/utils": "npm:^3.32.0" + "@react-aria/combobox": "npm:^3.15.0" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/listbox": "npm:^3.15.3" + "@react-aria/searchfield": "npm:^3.8.12" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/utils": "npm:^3.33.1" "@react-stately/autocomplete": "npm:3.0.0-beta.4" - "@react-stately/combobox": "npm:^3.12.1" - "@react-types/autocomplete": "npm:3.0.0-alpha.36" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" + "@react-stately/combobox": "npm:^3.13.0" + "@react-types/autocomplete": "npm:3.0.0-alpha.38" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/20fc809f5814aa3f7fb54ac83b5a45ad8e1df9c3f7963495d94ea63c161b7249e915f5ecabfd83c0412c9a636140330cb663078be531e3a63cd70a72a7fab8aa + checksum: 10c0/a3ea55078915b221dd2132b87d7362e5ad316c6313fc4590730635b3aa6df476d59978176fc626e52cc8e78c6494ec8df6ef419db3219a577abbd55b2d28b466 languageName: node linkType: hard -"@react-aria/breadcrumbs@npm:^3.5.30": - version: 3.5.30 - resolution: "@react-aria/breadcrumbs@npm:3.5.30" +"@react-aria/breadcrumbs@npm:^3.5.32": + version: 3.5.32 + resolution: "@react-aria/breadcrumbs@npm:3.5.32" dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/link": "npm:^3.8.7" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/breadcrumbs": "npm:^3.7.17" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/link": "npm:^3.8.9" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/breadcrumbs": "npm:^3.7.19" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/b33793650f99f331866ccf92947c8d52fe065d61a945a671c1e2e40503ca936bfdabbcb4571138002afc66316fc364acdabc57e3c6d4cfc257eab92ce55ad00c + checksum: 10c0/97b43e37343dbe20d12454c0e5bd010e9d271590e2c894bf831ff30113b73d373643534e1d32e5536267d7fe608071f7f77cc8cff367630c49103610e6b1412f languageName: node linkType: hard -"@react-aria/button@npm:^3.14.3": - version: 3.14.3 - resolution: "@react-aria/button@npm:3.14.3" +"@react-aria/button@npm:^3.14.5": + version: 3.14.5 + resolution: "@react-aria/button@npm:3.14.5" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/toolbar": "npm:3.0.0-beta.22" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/toggle": "npm:^3.9.3" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/toolbar": "npm:3.0.0-beta.24" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/toggle": "npm:^3.9.5" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/072c2e3b298eb8eccbb334933e70ad4248180c09c8a628ed94cba62cfcb90060ea99ca83bffcf88877ce4a35590614f686051a81f95ff4aac7bb5cef1c5f2086 + checksum: 10c0/df10c1f7d796ca7022770cf0f4695b5de5d14021f1edb9f1a1ace7e67c5534a97a1a052d161a84f9a8da1d4b8cccc98c033df623cd779450fae53911581c1d8f languageName: node linkType: hard -"@react-aria/calendar@npm:^3.9.3": - version: 3.9.3 - resolution: "@react-aria/calendar@npm:3.9.3" +"@react-aria/calendar@npm:^3.9.5": + version: 3.9.5 + resolution: "@react-aria/calendar@npm:3.9.5" dependencies: - "@internationalized/date": "npm:^3.10.1" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" + "@internationalized/date": "npm:^3.12.0" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/calendar": "npm:^3.9.1" - "@react-types/button": "npm:^3.14.1" - "@react-types/calendar": "npm:^3.8.1" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/calendar": "npm:^3.9.3" + "@react-types/button": "npm:^3.15.1" + "@react-types/calendar": "npm:^3.8.3" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/15eb86b6da788690ac664f2372d8c7c224250f0fd86836caed25e58011561fb5c038d2d12e023c0a2e73549c3db7a30b79c8d1c988b0eb40a5dc0dd2ca8e6bd0 + checksum: 10c0/fa2b5a041ea3c919f7ecf839abb45c7565f1d934be2683d84f991fa1936f419a4c370c02d576da3fcb0143c3d46eac15c04239b30f986e98b01004fed85a2c7a languageName: node linkType: hard -"@react-aria/checkbox@npm:^3.16.3": - version: 3.16.3 - resolution: "@react-aria/checkbox@npm:3.16.3" +"@react-aria/checkbox@npm:^3.16.5": + version: 3.16.5 + resolution: "@react-aria/checkbox@npm:3.16.5" dependencies: - "@react-aria/form": "npm:^3.1.3" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/toggle": "npm:^3.12.3" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/checkbox": "npm:^3.7.3" - "@react-stately/form": "npm:^3.2.2" - "@react-stately/toggle": "npm:^3.9.3" - "@react-types/checkbox": "npm:^3.10.2" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/form": "npm:^3.1.5" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/toggle": "npm:^3.12.5" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/checkbox": "npm:^3.7.5" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/toggle": "npm:^3.9.5" + "@react-types/checkbox": "npm:^3.10.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/b83406f233bc47df718e4304dcea2137678f45af00eb306f67217055ea636b2319715059ae2818b4b872b9263cb892d6e1f0a72a87284ef51050603c38562e1a + checksum: 10c0/d2f71668945a2c4faa8188f1a0448fae4393114f3d555d401f4f8ba09fab0c3382e31a082b8d45fb3b35599d1685037c90a0a6e8545d51f871e0f56f621faede languageName: node linkType: hard -"@react-aria/collections@npm:^3.0.1": - version: 3.0.1 - resolution: "@react-aria/collections@npm:3.0.1" +"@react-aria/collections@npm:^3.0.3": + version: 3.0.3 + resolution: "@react-aria/collections@npm:3.0.3" dependencies: - "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" - use-sync-external-store: "npm:^1.4.0" + use-sync-external-store: "npm:^1.6.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/e8b54c00ad24e1941e1104f4322a6def142297f17aa2ad2f05e7730a7eee4a7868c474042b1c4775b5f4906a27f00072f9689cb2af94faabd7f60c70899bdb3f + checksum: 10c0/b96a5f0923d9bdaf055d5c55e1f256c0f1d5e5d0dd69892980cc78a6a76bcfe5d7470fb1adba36c008806a6c77de0feb84bc950bf31d8124aa9928878350fff2 languageName: node linkType: hard -"@react-aria/color@npm:^3.1.3": - version: 3.1.3 - resolution: "@react-aria/color@npm:3.1.3" - dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/numberfield": "npm:^3.12.3" - "@react-aria/slider": "npm:^3.8.3" - "@react-aria/spinbutton": "npm:^3.7.0" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/utils": "npm:^3.32.0" - "@react-aria/visually-hidden": "npm:^3.8.29" - "@react-stately/color": "npm:^3.9.3" - "@react-stately/form": "npm:^3.2.2" - "@react-types/color": "npm:^3.1.2" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/color@npm:^3.1.5": + version: 3.1.5 + resolution: "@react-aria/color@npm:3.1.5" + dependencies: + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/numberfield": "npm:^3.12.5" + "@react-aria/slider": "npm:^3.8.5" + "@react-aria/spinbutton": "npm:^3.7.2" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/utils": "npm:^3.33.1" + "@react-aria/visually-hidden": "npm:^3.8.31" + "@react-stately/color": "npm:^3.9.5" + "@react-stately/form": "npm:^3.2.4" + "@react-types/color": "npm:^3.1.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/802bfa2ab5bda1a279a230590bfdb8a8fe5f7f4c84660b4dbec05ba551a0a59ad90bd4d6574ff4c6c8ec4c94d7f72e806711f7616f85bb1974d584a73f6ea473 + checksum: 10c0/d97fe78edec86a5f75d94469911f19d9ad8968df2d6e920b15f2b28c398ae6985cfeddbcbcfd68d19ab36a02d8d557c72b926483d8b7cd3f207f29daf7df2674 languageName: node linkType: hard -"@react-aria/combobox@npm:^3.14.1": - version: 3.14.1 - resolution: "@react-aria/combobox@npm:3.14.1" +"@react-aria/combobox@npm:^3.15.0": + version: 3.15.0 + resolution: "@react-aria/combobox@npm:3.15.0" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/listbox": "npm:^3.15.1" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/listbox": "npm:^3.15.3" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/menu": "npm:^3.19.4" - "@react-aria/overlays": "npm:^3.31.0" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/combobox": "npm:^3.12.1" - "@react-stately/form": "npm:^3.2.2" - "@react-types/button": "npm:^3.14.1" - "@react-types/combobox": "npm:^3.13.10" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/menu": "npm:^3.21.0" + "@react-aria/overlays": "npm:^3.31.2" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/combobox": "npm:^3.13.0" + "@react-stately/form": "npm:^3.2.4" + "@react-types/button": "npm:^3.15.1" + "@react-types/combobox": "npm:^3.14.0" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/425fd484faaf52625263583de5bad0799a75f3f4db09bad3315c1580a245069e7247751390927789f1683540f4d3a720a6dfb7587f7bbd4c18528bbf5f93badf + checksum: 10c0/39dabc979125bdf96e25236070c3cdb7aa6e381a4c823e6f021ccaf431e7412a0b397226696ec27c5b9c890f021d004df9a9880709346de930e0815e46102e21 languageName: node linkType: hard -"@react-aria/datepicker@npm:^3.15.3": - version: 3.15.3 - resolution: "@react-aria/datepicker@npm:3.15.3" +"@react-aria/datepicker@npm:^3.16.1": + version: 3.16.1 + resolution: "@react-aria/datepicker@npm:3.16.1" dependencies: - "@internationalized/date": "npm:^3.10.1" + "@internationalized/date": "npm:^3.12.0" "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/form": "npm:^3.1.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/spinbutton": "npm:^3.7.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/datepicker": "npm:^3.15.3" - "@react-stately/form": "npm:^3.2.2" - "@react-types/button": "npm:^3.14.1" - "@react-types/calendar": "npm:^3.8.1" - "@react-types/datepicker": "npm:^3.13.3" - "@react-types/dialog": "npm:^3.5.22" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/form": "npm:^3.1.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/spinbutton": "npm:^3.7.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/datepicker": "npm:^3.16.1" + "@react-stately/form": "npm:^3.2.4" + "@react-types/button": "npm:^3.15.1" + "@react-types/calendar": "npm:^3.8.3" + "@react-types/datepicker": "npm:^3.13.5" + "@react-types/dialog": "npm:^3.5.24" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/ce18df7cd40241718628839c7995b955951577578ec276ce961ed86e35d66f224aae4465958cc812bea919edee52aefba3db7dccd1e6c21ade5b6bad31651c9e + checksum: 10c0/5390bea90bf0d4df0b25b2ac34e78052a77da68d833af23abac843708ea46fd67079251b84cb4e8d9b3e3ad450850c232711f4cc7dd47e0d5f099bdff83514a4 languageName: node linkType: hard -"@react-aria/dialog@npm:^3.5.32": - version: 3.5.32 - resolution: "@react-aria/dialog@npm:3.5.32" +"@react-aria/dialog@npm:^3.5.34": + version: 3.5.34 + resolution: "@react-aria/dialog@npm:3.5.34" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/overlays": "npm:^3.31.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/dialog": "npm:^3.5.22" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/overlays": "npm:^3.31.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/dialog": "npm:^3.5.24" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c5472dd70d8079993b84eb30c42c076dab6eb14adf21db6f63f1b94fbd4cf7c4b90391c5e842484a180e3123f4bacb000fcaf8434247ad5be9870f106eb87ba1 + checksum: 10c0/cc1f50a40ac20a27528e8aa17cd7ff47f26471e1c87a8f0ee445fe909a00f58a4abee4a857be114d229e5b6e15832572709d1cf2d42f1eb8e0fe209f0c45a97a languageName: node linkType: hard -"@react-aria/disclosure@npm:^3.1.1": - version: 3.1.1 - resolution: "@react-aria/disclosure@npm:3.1.1" +"@react-aria/disclosure@npm:^3.1.3": + version: 3.1.3 + resolution: "@react-aria/disclosure@npm:3.1.3" dependencies: "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/disclosure": "npm:^3.0.9" - "@react-types/button": "npm:^3.14.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/disclosure": "npm:^3.0.11" + "@react-types/button": "npm:^3.15.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/d96e8122f7a4a03741af171c9928b6a897eb040ead995cd10cef74a332a9bf7910b36a83cfb088abbe68c13ece38b2ac1d43f50349a831f23aa6b181c2ef1389 + checksum: 10c0/f081af1189f5efd72d59aa7cd74e10bf03f534d458e156276f281d3ba633543657cbc76535e4c43cd071ec63bfb97f04ae8d2683dc7fdea845e576abd0cf23a3 languageName: node linkType: hard -"@react-aria/dnd@npm:^3.11.4": - version: 3.11.4 - resolution: "@react-aria/dnd@npm:3.11.4" +"@react-aria/dnd@npm:^3.11.6": + version: 3.11.6 + resolution: "@react-aria/dnd@npm:3.11.6" dependencies: "@internationalized/string": "npm:^3.2.7" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/overlays": "npm:^3.31.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/dnd": "npm:^3.7.2" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/overlays": "npm:^3.31.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/dnd": "npm:^3.7.4" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/7e056f59273eb5e725490fd546b42a3977677227c4071b1407441c8fb9bd11bc8b96241a756b6a4882c362145010326f171be2140fe8bd79fc328a92e0e8b91a + checksum: 10c0/47d4a45c1c56f7e37ae22043c0b1d5ad67359b0cb2cfb46b26a3f587d551e99564ae3331dd31ada699f04cde51a9cec384a1beed5f566cbd6e15b2884dac98d6 languageName: node linkType: hard -"@react-aria/focus@npm:^3.21.3": - version: 3.21.3 - resolution: "@react-aria/focus@npm:3.21.3" +"@react-aria/focus@npm:^3.21.5": + version: 3.21.5 + resolution: "@react-aria/focus@npm:3.21.5" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c1169f2047908dd2641439ed49b51d1482df00514f5adc569d73727bc6375150198dd1b6e345a79fc31f3571d7d09549743ba2e6b3168ed8d6a554708d48fa9b + checksum: 10c0/f14f3d2d14b11d1e70028c4040cae46f132a24594e9217d7041d63ebff4cf8bc5d7f07b029c97481f267a440cfd6e1521181484b665f2a410b76ee77ceb641bf languageName: node linkType: hard -"@react-aria/form@npm:^3.1.3": - version: 3.1.3 - resolution: "@react-aria/form@npm:3.1.3" +"@react-aria/form@npm:^3.1.5": + version: 3.1.5 + resolution: "@react-aria/form@npm:3.1.5" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/form": "npm:^3.2.2" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/form": "npm:^3.2.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/8469b0ee653deedae1ef0c0ab64773a204e337f4919d3ed9caf67c6df54f5b2dd33935a484eb49d79621976ef1a2fde69cc0ca6af2f9361ab32c3b909f41431b + checksum: 10c0/fe3127151f95c8cde971384ff6f0cc9ebaf826e9ec818efbb75ac7d72ffa6a6445ba3431502ca77099d0281ea8fae2c6c53ce5f299e92a725bf0d45fb7f018af languageName: node linkType: hard -"@react-aria/grid@npm:^3.14.6": - version: 3.14.6 - resolution: "@react-aria/grid@npm:3.14.6" +"@react-aria/grid@npm:^3.14.8": + version: 3.14.8 + resolution: "@react-aria/grid@npm:3.14.8" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/grid": "npm:^3.11.7" - "@react-stately/selection": "npm:^3.20.7" - "@react-types/checkbox": "npm:^3.10.2" - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/grid": "npm:^3.11.9" + "@react-stately/selection": "npm:^3.20.9" + "@react-types/checkbox": "npm:^3.10.4" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/012544af3ef3192de3fd5e4e01e03abb7f3ff0e3266e25bf52137fdc79c4214aaa936d9142975b8a95dd608cecf711704887bd499312e041fa20a6048d7e3f3e + checksum: 10c0/6e824d536aec986ed11121076044199f96cfca21ec3ca69759a76f8ab524d8c4ef2b02fc98bdc075155818daa19e9fb82b565b8641ed08e50fcb36c09ab0860e languageName: node linkType: hard -"@react-aria/gridlist@npm:^3.14.2": - version: 3.14.2 - resolution: "@react-aria/gridlist@npm:3.14.2" - dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/grid": "npm:^3.14.6" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/list": "npm:^3.13.2" - "@react-stately/tree": "npm:^3.9.4" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/gridlist@npm:^3.14.4": + version: 3.14.4 + resolution: "@react-aria/gridlist@npm:3.14.4" + dependencies: + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/grid": "npm:^3.14.8" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/list": "npm:^3.13.4" + "@react-stately/tree": "npm:^3.9.6" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/49168c61d2dd28760ef7395b82d36fba8a6809aa71aca4266bd34486068f36fd9da092fe913395dfd7ea1854f13f307f42a9992da7acf22ebdec13947ed26a58 + checksum: 10c0/c94350a6c17d1343eaa9cbae028f33f7487d712e95a0a096edf95d726f1100c12214015dc8c7512e77475cf446a9a54a9a0b89264727903b9e1b272b61683ab2 languageName: node linkType: hard -"@react-aria/i18n@npm:^3.12.14": - version: 3.12.14 - resolution: "@react-aria/i18n@npm:3.12.14" +"@react-aria/i18n@npm:^3.12.16": + version: 3.12.16 + resolution: "@react-aria/i18n@npm:3.12.16" dependencies: - "@internationalized/date": "npm:^3.10.1" + "@internationalized/date": "npm:^3.12.0" "@internationalized/message": "npm:^3.1.8" "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c25095a268b30b715713a7f2af8e4023cb9b6993118f824ceafcaa7af65200ccaa4ff8b100a670f58821a007cb57f2571a7a6823b492a116b38a43ca880ebd8b + checksum: 10c0/dfe20390526ee74b0eaec66ae990c2e76d7712afe95be5e38e2e0400a95e91cbf43e73c0e5ac3d2d25c1cb3b5ee6e55f02e272dd24ee9e9d6648a5317e68e674 languageName: node linkType: hard -"@react-aria/interactions@npm:^3.26.0": - version: 3.26.0 - resolution: "@react-aria/interactions@npm:3.26.0" +"@react-aria/interactions@npm:^3.27.1": + version: 3.27.1 + resolution: "@react-aria/interactions@npm:3.27.1" dependencies: "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.32.0" + "@react-aria/utils": "npm:^3.33.1" "@react-stately/flags": "npm:^3.1.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/542044d08c02aec337ceda1ed55e5b01f6fa3e76c930b0063bc4a2146102d39659df81570912b7bef4782e268c08bbfdca82a44df413ec8ce8f1bdf930e97051 + checksum: 10c0/3fe2bf9b6d58554c21bacbc9d6af4a547040b803a8eaed5b5fa3ea99045fe777a038ef1f4b13919833b60744ba46635aaf0849d04a6de1eb545728c339a8b510 languageName: node linkType: hard -"@react-aria/label@npm:^3.7.23": - version: 3.7.23 - resolution: "@react-aria/label@npm:3.7.23" +"@react-aria/label@npm:^3.7.25": + version: 3.7.25 + resolution: "@react-aria/label@npm:3.7.25" dependencies: - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/202871efd3c04435219ad58cd05fdc80829d95d848ac50f22eedd7e04e04ae1bb45ff1d9de0721f6f4ce84f5df135f8248942ae740ff27dc8511f9dcfbbaff7f + checksum: 10c0/06474430cd3574aab2eba10823bbaa4ee28bb4e5cdae58b9de38600dcf73bb6ace1af520f507fb8e507f0acd9eea20002af471b57119a71f2da8ea8d0c03d58f languageName: node linkType: hard -"@react-aria/landmark@npm:^3.0.8": - version: 3.0.8 - resolution: "@react-aria/landmark@npm:3.0.8" +"@react-aria/landmark@npm:^3.0.10": + version: 3.0.10 + resolution: "@react-aria/landmark@npm:3.0.10" dependencies: - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" - use-sync-external-store: "npm:^1.4.0" + use-sync-external-store: "npm:^1.6.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/13a81bbc37121eb1a1019608a20e97c72e5d7fa338550011052ba2230310002bf6d87aec31ae74e44ac622b473b8830108571787ca1edffd4f0df84f17b002ca + checksum: 10c0/5f3cf583ef8f473e6f2733d3090ac58ee7493b9d8e7c4beffb9a5ea9dc1f0a91f5308d9782de4bb229f32421e1e73a10df0dac724b3c722c043557117a2313db languageName: node linkType: hard -"@react-aria/link@npm:^3.8.7": - version: 3.8.7 - resolution: "@react-aria/link@npm:3.8.7" +"@react-aria/link@npm:^3.8.9": + version: 3.8.9 + resolution: "@react-aria/link@npm:3.8.9" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/link": "npm:^3.6.5" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/link": "npm:^3.6.7" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/0fb7e17fae5f07bfea3b26a507453d6987f9bfc7009330778b242099a045cb3a4f910621cab22e41e40193ace21db67acc73a64ef6b0bf942154b2cd630674db + checksum: 10c0/4724cf10762a6bda9d4c17adae5bf95a27512152f7f5f1d798d3cb835dd2321860f143e66f98f3653b3f2d776909a8f269fb8476395965d8ee0fe3d040ec3977 languageName: node linkType: hard -"@react-aria/listbox@npm:^3.15.1": - version: 3.15.1 - resolution: "@react-aria/listbox@npm:3.15.1" - dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/list": "npm:^3.13.2" - "@react-types/listbox": "npm:^3.7.4" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/listbox@npm:^3.15.3": + version: 3.15.3 + resolution: "@react-aria/listbox@npm:3.15.3" + dependencies: + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/list": "npm:^3.13.4" + "@react-types/listbox": "npm:^3.7.6" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/2499e19d780ae09563bb4dd1d00fc36f2dd7bc110e573a2727d6290c4c61d2604939e36697e9edf92c3c3587db49c2680ec06e0932fcddf8ad59059d56bb2d65 + checksum: 10c0/c0f08d32971ede39dc5941e451dd8aac22e0655abca3840d547e99a6dc6e9b3a3787cf501c284367df642584dbbc8440246ac6e94fbdf5598f5d181952e8b7e1 languageName: node linkType: hard @@ -9568,237 +9960,239 @@ __metadata: languageName: node linkType: hard -"@react-aria/menu@npm:^3.19.4": - version: 3.19.4 - resolution: "@react-aria/menu@npm:3.19.4" - dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/overlays": "npm:^3.31.0" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/menu": "npm:^3.9.9" - "@react-stately/selection": "npm:^3.20.7" - "@react-stately/tree": "npm:^3.9.4" - "@react-types/button": "npm:^3.14.1" - "@react-types/menu": "npm:^3.10.5" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/menu@npm:^3.21.0": + version: 3.21.0 + resolution: "@react-aria/menu@npm:3.21.0" + dependencies: + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/overlays": "npm:^3.31.2" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/menu": "npm:^3.9.11" + "@react-stately/selection": "npm:^3.20.9" + "@react-stately/tree": "npm:^3.9.6" + "@react-types/button": "npm:^3.15.1" + "@react-types/menu": "npm:^3.10.7" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c16e4a283ef4275c21e795617046d2d3ac5106bf61fce62244691e7edb64f2ddd3c407e0e1481283484a9e007d42b70449ce53297e30b5547a00d375b6f2a81b + checksum: 10c0/7f20522dc3afa4a927c71e41b62777a9096a22b61c45ce73eccc163f7b14628f45f00c7dfe603b0a511018e9f4eddbaf094c5bfd7a6acc59701ecc11d3b1bafb languageName: node linkType: hard -"@react-aria/meter@npm:^3.4.28": - version: 3.4.28 - resolution: "@react-aria/meter@npm:3.4.28" +"@react-aria/meter@npm:^3.4.30": + version: 3.4.30 + resolution: "@react-aria/meter@npm:3.4.30" dependencies: - "@react-aria/progress": "npm:^3.4.28" - "@react-types/meter": "npm:^3.4.13" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/progress": "npm:^3.4.30" + "@react-types/meter": "npm:^3.4.15" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/a19976aff4157d29da2c07312bea8ef7e5f25fcc1662be655e323b2e07023211daa6c292a3eb176854ab8f501e2a8f967e545d0788df436f9b631c67992b90db + checksum: 10c0/8aed01a68b0dfce61c1ae59eb4c95d9feeb0bcabf8ed6492a95133c45c1553f7ea03865dca45ba0341b621827fa8cd06a931d8602dd86cfc84828e5c8dc1fb58 languageName: node linkType: hard -"@react-aria/numberfield@npm:^3.12.3": - version: 3.12.3 - resolution: "@react-aria/numberfield@npm:3.12.3" +"@react-aria/numberfield@npm:^3.12.5": + version: 3.12.5 + resolution: "@react-aria/numberfield@npm:3.12.5" dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/spinbutton": "npm:^3.7.0" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/form": "npm:^3.2.2" - "@react-stately/numberfield": "npm:^3.10.3" - "@react-types/button": "npm:^3.14.1" - "@react-types/numberfield": "npm:^3.8.16" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/live-announcer": "npm:^3.4.4" + "@react-aria/spinbutton": "npm:^3.7.2" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/numberfield": "npm:^3.11.0" + "@react-types/button": "npm:^3.15.1" + "@react-types/numberfield": "npm:^3.8.18" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/4f53d5696eb77eb739d9083c2eba6bbd4d780879170c29b92877b4ee1e0780160b900a1914f6affe3623b53ec90f2f661be5055ad4ea00bfc4d3f5a330d4f80b + checksum: 10c0/54f204d4a92295f24d5f7a18290891baff95f355c17980fa028acdd875d0e41ff1e18aced9f879e1bbbbe34acd48237efd623772bbf240f603b8e11f60e2e2c5 languageName: node linkType: hard -"@react-aria/overlays@npm:^3.31.0": - version: 3.31.0 - resolution: "@react-aria/overlays@npm:3.31.0" +"@react-aria/overlays@npm:^3.31.2": + version: 3.31.2 + resolution: "@react-aria/overlays@npm:3.31.2" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.32.0" - "@react-aria/visually-hidden": "npm:^3.8.29" - "@react-stately/overlays": "npm:^3.6.21" - "@react-types/button": "npm:^3.14.1" - "@react-types/overlays": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-aria/visually-hidden": "npm:^3.8.31" + "@react-stately/flags": "npm:^3.1.2" + "@react-stately/overlays": "npm:^3.6.23" + "@react-types/button": "npm:^3.15.1" + "@react-types/overlays": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/6cdc30d669e42c1a69af33c5c6da37fecae6139be923b4bddb17ac79392a40d62c3f3d67a0437201bddd1cc3b07b2e6d4ffeb5fb285db319427a5552ac4383f0 + checksum: 10c0/c53b87a608cbc13b49e26061604314e4d0b8048716934884e6a34b1dc0250859b641e6ed654d9aa3df85f7a88d093644c8cc607a092b54f62c7044a1ca458a79 languageName: node linkType: hard -"@react-aria/progress@npm:^3.4.28": - version: 3.4.28 - resolution: "@react-aria/progress@npm:3.4.28" +"@react-aria/progress@npm:^3.4.30": + version: 3.4.30 + resolution: "@react-aria/progress@npm:3.4.30" dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/progress": "npm:^3.5.16" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/progress": "npm:^3.5.18" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/e70e65d204036d379f21aaac760cf9b820d672655b722b4863b2e6bfca4a6431b3bf8a602433448b4dc738e202d2378fc46efeb06585d3e2e97ea229ba54f013 + checksum: 10c0/8db32d5a8dd12f511ab48e9b2707e7c305b777103bdbb828ac190dd3ebcad77400df88525e97f020388d647efd72766376f6b4b3574df51ff981dde6dfc554c0 languageName: node linkType: hard -"@react-aria/radio@npm:^3.12.3": - version: 3.12.3 - resolution: "@react-aria/radio@npm:3.12.3" +"@react-aria/radio@npm:^3.12.5": + version: 3.12.5 + resolution: "@react-aria/radio@npm:3.12.5" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/form": "npm:^3.1.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/radio": "npm:^3.11.3" - "@react-types/radio": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/form": "npm:^3.1.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/radio": "npm:^3.11.5" + "@react-types/radio": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/bf45c98d034e551b0611b1688e12b47655b5f6806f69d948e1cf523761395a41c51c49f01a7b0af74e0c3d86fc3c66f189cd0fdd3f1c0c670193a50eab2f128d + checksum: 10c0/d9637d50aa01c6e7fc9f997241794b231b1b46e5beea2f998a041c316235cc28d79e9acf8b6e27cfb4a6f3060e5a069c3ec9fee74eb51f7fd0a20805bb22e2cb languageName: node linkType: hard -"@react-aria/searchfield@npm:^3.8.10": - version: 3.8.10 - resolution: "@react-aria/searchfield@npm:3.8.10" +"@react-aria/searchfield@npm:^3.8.12": + version: 3.8.12 + resolution: "@react-aria/searchfield@npm:3.8.12" dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/searchfield": "npm:^3.5.17" - "@react-types/button": "npm:^3.14.1" - "@react-types/searchfield": "npm:^3.6.6" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/searchfield": "npm:^3.5.19" + "@react-types/button": "npm:^3.15.1" + "@react-types/searchfield": "npm:^3.6.8" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/8592ffb85499cc46aab562cc7ff4733e0200a1f17aa5e88bdb69150a52e3abc63b8404adf85ad06dfb85b45b01525c33b75554e5139e639e4bd0d38d4e1c9548 - languageName: node - linkType: hard - -"@react-aria/select@npm:^3.17.1": - version: 3.17.1 - resolution: "@react-aria/select@npm:3.17.1" - dependencies: - "@react-aria/form": "npm:^3.1.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/listbox": "npm:^3.15.1" - "@react-aria/menu": "npm:^3.19.4" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-aria/visually-hidden": "npm:^3.8.29" - "@react-stately/select": "npm:^3.9.0" - "@react-types/button": "npm:^3.14.1" - "@react-types/select": "npm:^3.12.0" - "@react-types/shared": "npm:^3.32.1" + checksum: 10c0/a75e6f8984da2529eed3d301deff350eb199a6a82f0d466b7ea81e029e70c24e254ee4aea374760f6f3f52746e60c9b7f6f0a212cd50378f4359478d206561b0 + languageName: node + linkType: hard + +"@react-aria/select@npm:^3.17.3": + version: 3.17.3 + resolution: "@react-aria/select@npm:3.17.3" + dependencies: + "@react-aria/form": "npm:^3.1.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/listbox": "npm:^3.15.3" + "@react-aria/menu": "npm:^3.21.0" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-aria/visually-hidden": "npm:^3.8.31" + "@react-stately/select": "npm:^3.9.2" + "@react-types/button": "npm:^3.15.1" + "@react-types/select": "npm:^3.12.2" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c0b81118d7c98c7ec05478b0d9d10b32334e8a3068319ba3bf9c574e9a723142a82caabeba78fea4ff46f62bdf95bf2816f6ed02ec176f09fd7fc7db38c75f59 + checksum: 10c0/6084e71341952388a1b2780fae13b2628a58e10ba8f486caecb75f3ee4a6fb98d5bf16edf449566487b9620e8a12e3ba04ed12344b17b22362112fe07d6310dd languageName: node linkType: hard -"@react-aria/selection@npm:^3.27.0": - version: 3.27.0 - resolution: "@react-aria/selection@npm:3.27.0" +"@react-aria/selection@npm:^3.27.2": + version: 3.27.2 + resolution: "@react-aria/selection@npm:3.27.2" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/selection": "npm:^3.20.7" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/selection": "npm:^3.20.9" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/287713832368162f49217aefaa9a292cfc194d556e37d6212df58087aebbdf56be661c89d98eb3a3a4604e057a263c3cdcff343afa57115b751fdd6f787fc4d8 + checksum: 10c0/bc283b7fd3169f1287a6f90bfa73a48f1b5f51ba38e1b32d785ba8c761f616927b0b649d410a793ff461d16a5dfb5c066b4ecc0100db8b5556ccf0d2bdfe42c4 languageName: node linkType: hard -"@react-aria/separator@npm:^3.4.14": - version: 3.4.14 - resolution: "@react-aria/separator@npm:3.4.14" +"@react-aria/separator@npm:^3.4.16": + version: 3.4.16 + resolution: "@react-aria/separator@npm:3.4.16" dependencies: - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c38d750049d12bd203dfd00f6d908bfd944f56d34883375219cac4f0b0a60f4ca9df6238367fb90d9395b8b09bea2334b3f1990b7e409d4a1b57af95e816a1e5 + checksum: 10c0/bf32279f3c94da22c9f3eda7c1fe6dd9206cf670819c94be0099c158d0b5e2401fcfd22e5f2f9b748079fa53c69130e089f89cd3333947a4ae76d0ff18932482 languageName: node linkType: hard -"@react-aria/slider@npm:^3.8.3": - version: 3.8.3 - resolution: "@react-aria/slider@npm:3.8.3" - dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/slider": "npm:^3.7.3" - "@react-types/shared": "npm:^3.32.1" - "@react-types/slider": "npm:^3.8.2" +"@react-aria/slider@npm:^3.8.5": + version: 3.8.5 + resolution: "@react-aria/slider@npm:3.8.5" + dependencies: + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/slider": "npm:^3.7.5" + "@react-types/shared": "npm:^3.33.1" + "@react-types/slider": "npm:^3.8.4" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/8e0b1e8d5307d9c874ddd6391d2ddc79e4895addf7dd86d971fd375502bd5e866fb9183202b13929bea927995270e7d766cb6f8d2558a6192325a62efa63e4fd + checksum: 10c0/4b44a91f066fe0b31ab17efa64e23e2f6ffcf416eed219d319b96f1b3ff9850b73e8f08e6ef5a00fd0f6109924495f39995304222bf83e7e46276513357e11df languageName: node linkType: hard -"@react-aria/spinbutton@npm:^3.7.0": - version: 3.7.0 - resolution: "@react-aria/spinbutton@npm:3.7.0" +"@react-aria/spinbutton@npm:^3.7.2": + version: 3.7.2 + resolution: "@react-aria/spinbutton@npm:3.7.2" dependencies: - "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/i18n": "npm:^3.12.16" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/1247ac3610468aa81f2ed862624904412254485623ea0140200e2ef89f8bbaf4fea94b997ee5b7095058b44dea5e2d72c1c94208b02ad62783d6cc4bf9135da8 + checksum: 10c0/97c2f504bb07484d78045cf5b36301b0eea6226928a72ffc80c03212657e6c9e18dbb7f1bd5caa91235008e0d6ad0672dd37144967dd1b2deb56b025d27c9baa languageName: node linkType: hard @@ -9813,242 +10207,242 @@ __metadata: languageName: node linkType: hard -"@react-aria/switch@npm:^3.7.9": - version: 3.7.9 - resolution: "@react-aria/switch@npm:3.7.9" +"@react-aria/switch@npm:^3.7.11": + version: 3.7.11 + resolution: "@react-aria/switch@npm:3.7.11" dependencies: - "@react-aria/toggle": "npm:^3.12.3" - "@react-stately/toggle": "npm:^3.9.3" - "@react-types/shared": "npm:^3.32.1" - "@react-types/switch": "npm:^3.5.15" + "@react-aria/toggle": "npm:^3.12.5" + "@react-stately/toggle": "npm:^3.9.5" + "@react-types/shared": "npm:^3.33.1" + "@react-types/switch": "npm:^3.5.17" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/6475862dea50ef7246940bc0dd28846971061e5d1c580335a43db075fa2c4e0fda98836b2c6689524f8b108f225b704a722224d7605f1fe68b40561a17dcd46d + checksum: 10c0/b3561b707be211e6ce4b6c58f4463be285b88be082d9d449561513465450730e67e87f951d137d0f6f7260124588c571fbdcd6524fd42f9f144c267f199f5a76 languageName: node linkType: hard -"@react-aria/table@npm:^3.17.9": - version: 3.17.9 - resolution: "@react-aria/table@npm:3.17.9" +"@react-aria/table@npm:^3.17.11": + version: 3.17.11 + resolution: "@react-aria/table@npm:3.17.11" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/grid": "npm:^3.14.6" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/grid": "npm:^3.14.8" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/utils": "npm:^3.32.0" - "@react-aria/visually-hidden": "npm:^3.8.29" - "@react-stately/collections": "npm:^3.12.8" + "@react-aria/utils": "npm:^3.33.1" + "@react-aria/visually-hidden": "npm:^3.8.31" + "@react-stately/collections": "npm:^3.12.10" "@react-stately/flags": "npm:^3.1.2" - "@react-stately/table": "npm:^3.15.2" - "@react-types/checkbox": "npm:^3.10.2" - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" - "@react-types/table": "npm:^3.13.4" + "@react-stately/table": "npm:^3.15.4" + "@react-types/checkbox": "npm:^3.10.4" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" + "@react-types/table": "npm:^3.13.6" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/7101b289cd17813f8091da4a064166407c99d5960a0188284b098da4a28f59d02bf51afdc5fdcd4377ac99fd4e9ed10ada59de883a7d465f7f2358e8b8985981 + checksum: 10c0/8959b2f4fa42e56019b0e6a8586011a5220cd59f4b69efe344e2fb16769642b7fdbe02eca7c96fbd41bdeb873c3e46f55663c31ab82b9ac4fadaf2cd6f179857 languageName: node linkType: hard -"@react-aria/tabs@npm:^3.10.9": - version: 3.10.9 - resolution: "@react-aria/tabs@npm:3.10.9" +"@react-aria/tabs@npm:^3.11.1": + version: 3.11.1 + resolution: "@react-aria/tabs@npm:3.11.1" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/tabs": "npm:^3.8.7" - "@react-types/shared": "npm:^3.32.1" - "@react-types/tabs": "npm:^3.3.20" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/tabs": "npm:^3.8.9" + "@react-types/shared": "npm:^3.33.1" + "@react-types/tabs": "npm:^3.3.22" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/ad71d9bcb06a150f0686956535aeecc7478d00ef7dd199960750933f8dfb9dc3b9de663f20e535b872839a8a375b91a2633cfb927f5bc8b225f97fecd2c1fb1c + checksum: 10c0/24c26ff9a0f5a4d7adf2a2fa4d887cc9bd9a40c1a88013ed5d7d71c68bf69497bc28480cadca018fb3e597be0215f75cf7028a114b0add76c12f23043e37737d languageName: node linkType: hard -"@react-aria/tag@npm:^3.7.3": - version: 3.7.3 - resolution: "@react-aria/tag@npm:3.7.3" - dependencies: - "@react-aria/gridlist": "npm:^3.14.2" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/list": "npm:^3.13.2" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/tag@npm:^3.8.1": + version: 3.8.1 + resolution: "@react-aria/tag@npm:3.8.1" + dependencies: + "@react-aria/gridlist": "npm:^3.14.4" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/list": "npm:^3.13.4" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/44b7fe31387e61782aa6f3f25d92979369d46c8049b83db290dbd4a4a5516981b7e4cf429fad47bb5c33bbf69252d64a60db845c122b9ebe067ed9723f4cf180 + checksum: 10c0/3969ef97889543e7f3a599198a6724d27e76f61da20d01e4ff39916534e9f4a4be92913c190381db432f918399fb3b08e5e7d8529e01625c39c4ed4b5a1bd4e8 languageName: node linkType: hard -"@react-aria/textfield@npm:^3.18.3": - version: 3.18.3 - resolution: "@react-aria/textfield@npm:3.18.3" +"@react-aria/textfield@npm:^3.18.5": + version: 3.18.5 + resolution: "@react-aria/textfield@npm:3.18.5" dependencies: - "@react-aria/form": "npm:^3.1.3" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/form": "npm:^3.2.2" + "@react-aria/form": "npm:^3.1.5" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/form": "npm:^3.2.4" "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" - "@react-types/textfield": "npm:^3.12.6" + "@react-types/shared": "npm:^3.33.1" + "@react-types/textfield": "npm:^3.12.8" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/a6096702af45e005870ac615d9c02a45114a84449ff9475c0e1735b2ba8e1e33e2ce3c99aa7c0764f2b3ba7eb694fbcf6999339a28e135b57aeeb4dea5ce4627 + checksum: 10c0/36e58a23fb29f2167cb404932c6339bbd8a8d33b68145df575fbeafadd5d50b6e130b578e10b109b93e8b08328a79afbdf3af3b50d0a07812b85b8b7d35e42ca languageName: node linkType: hard -"@react-aria/toast@npm:^3.0.9": - version: 3.0.9 - resolution: "@react-aria/toast@npm:3.0.9" - dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/landmark": "npm:^3.0.8" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/toast": "npm:^3.1.2" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/toast@npm:^3.0.11": + version: 3.0.11 + resolution: "@react-aria/toast@npm:3.0.11" + dependencies: + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/landmark": "npm:^3.0.10" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/toast": "npm:^3.1.3" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/56a276f012f2deaac5193bffba9e8293f989953475610361b5b718786783dd9950e14eb9b18248af98358526d5ae32fdbb40e7bd499715613bee7c74ff44e1e2 + checksum: 10c0/235044b30738582c7838facff395db85d9de2a48691b8fe01020e825520d6662c1e6aef61a9c515f3301d4402203614c3a9e1460b89bf60c54853f66b4cf62f3 languageName: node linkType: hard -"@react-aria/toggle@npm:^3.12.3": - version: 3.12.3 - resolution: "@react-aria/toggle@npm:3.12.3" +"@react-aria/toggle@npm:^3.12.5": + version: 3.12.5 + resolution: "@react-aria/toggle@npm:3.12.5" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/toggle": "npm:^3.9.3" - "@react-types/checkbox": "npm:^3.10.2" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/toggle": "npm:^3.9.5" + "@react-types/checkbox": "npm:^3.10.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/ec2f6670120f9f1585fa035db67215d4fde948a58f83ef03153798304da152cab2fbffd7a722f153d2cb60411fc5ba5d01527b12c967767d7a2e0bc64c0739ee + checksum: 10c0/8afb40f99c9e57efb2e18ac5fb8a67a88cd45b36ece218667fe67adec84dd4b1a45bd97412e6ee396dab0d763fa3d71f1451f5f54c20366aac8212c7020ed6d3 languageName: node linkType: hard -"@react-aria/toolbar@npm:3.0.0-beta.22": - version: 3.0.0-beta.22 - resolution: "@react-aria/toolbar@npm:3.0.0-beta.22" +"@react-aria/toolbar@npm:3.0.0-beta.24": + version: 3.0.0-beta.24 + resolution: "@react-aria/toolbar@npm:3.0.0-beta.24" dependencies: - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/aebc2e72f2ed9853f81468a8695e4d9688b6b021a940a2af552fee30515cbd261fc1d200c75e8b4ad92a02aa613b26705f6882952b4380fe779449a8fe93811b + checksum: 10c0/ee47c3fc2eac0b4fcc4ebcd6d746fa3d50087d76273381f5b23b4274a9d4d129bc7057b8775f5b5081e431077ffdd6b51f1b1ccdcea86bf1a20eb8e44459e912 languageName: node linkType: hard -"@react-aria/tooltip@npm:^3.9.0": - version: 3.9.0 - resolution: "@react-aria/tooltip@npm:3.9.0" +"@react-aria/tooltip@npm:^3.9.2": + version: 3.9.2 + resolution: "@react-aria/tooltip@npm:3.9.2" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/tooltip": "npm:^3.5.9" - "@react-types/shared": "npm:^3.32.1" - "@react-types/tooltip": "npm:^3.5.0" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/tooltip": "npm:^3.5.11" + "@react-types/shared": "npm:^3.33.1" + "@react-types/tooltip": "npm:^3.5.2" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/374a09a97cd81d75e1326517ce94cb289524b8d6f2734ce9012c13c5506786ad62d31cb2b15d0a2d41928797f813882c5dca382971783c062cd4652903f1e318 + checksum: 10c0/d52e3d8bb4bb481a7bf7a2e9b286d226f7f7c0a83c18620ed9f2318f5be1ad7411ca479e4ff8efdb13dfec12277ece895338251a06b283b4bc39d443545bd4f2 languageName: node linkType: hard -"@react-aria/tree@npm:^3.1.5": - version: 3.1.5 - resolution: "@react-aria/tree@npm:3.1.5" - dependencies: - "@react-aria/gridlist": "npm:^3.14.2" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/tree": "npm:^3.9.4" - "@react-types/button": "npm:^3.14.1" - "@react-types/shared": "npm:^3.32.1" +"@react-aria/tree@npm:^3.1.7": + version: 3.1.7 + resolution: "@react-aria/tree@npm:3.1.7" + dependencies: + "@react-aria/gridlist": "npm:^3.14.4" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/tree": "npm:^3.9.6" + "@react-types/button": "npm:^3.15.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/24571074016fd4a8e0ab0c2cda2d6cfdfe50b3e0eb1beb0523a72938a32be525849a0129c8094588b0a012cde9d492283d872e2b9e92022430098b4a4062540a + checksum: 10c0/a0ca8cc14dac2cac8b294fbc8696f0895cfab1b82dedf17a165d971f8ba03044fec935063e256759c870e88d7858fc8ac151446eaa516e173c19dd94d927f06c languageName: node linkType: hard -"@react-aria/utils@npm:^3.32.0": - version: 3.32.0 - resolution: "@react-aria/utils@npm:3.32.0" +"@react-aria/utils@npm:^3.33.1": + version: 3.33.1 + resolution: "@react-aria/utils@npm:3.33.1" dependencies: "@react-aria/ssr": "npm:^3.9.10" "@react-stately/flags": "npm:^3.1.2" "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/10fd9b162f8c752bf70070f5e091eaf3bd2c163b0a86e1f29c306c766b6b1acbbefa85c1ed6c28973b858afeafd638faa783361440c679890698c3d78bb50121 + checksum: 10c0/8b59b6e4f5f2358aecc291c929b621e68d121ebd15a8377da0a8b34ee706c9caea928dc34a0cc70650527dc9538619f4a9dcbcacf92bee31a73e69f59afbc772 languageName: node linkType: hard -"@react-aria/virtualizer@npm:^4.1.11": - version: 4.1.11 - resolution: "@react-aria/virtualizer@npm:4.1.11" +"@react-aria/virtualizer@npm:^4.1.13": + version: 4.1.13 + resolution: "@react-aria/virtualizer@npm:4.1.13" dependencies: - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-stately/virtualizer": "npm:^4.4.4" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-stately/virtualizer": "npm:^4.4.6" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/8a6cee687b29e771de873a31ce56a75ca3217f78ead97081af3a684ef56acc141d681e3bab78eefc6b5452dfdb1abcde4267d1ac25c5a4d10819e1fcaf14cfc8 + checksum: 10c0/483e03235d3066dd0d1a24884e9fde115e21b472bae087cad69b56edbdbb0e38a4fb54d15c4eb6df6f6d8f1f3dca9282fc5da070151e8719e7a9024b939d2836 languageName: node linkType: hard -"@react-aria/visually-hidden@npm:^3.8.29": - version: 3.8.29 - resolution: "@react-aria/visually-hidden@npm:3.8.29" +"@react-aria/visually-hidden@npm:^3.8.31": + version: 3.8.31 + resolution: "@react-aria/visually-hidden@npm:3.8.31" dependencies: - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.32.0" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/utils": "npm:^3.33.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/6a5a6cf115bdf2e96cd54210b2fc28884ee70875982ebfc3ff8423e8d887968a1f5d6cf66af27799b3727f9e49c85fb9eb84ec0f81c8ed530eb2b5e5ac617776 + checksum: 10c0/bb33ba7ec2b274241140c8219e243c0c059bb11d01304881c883b0f6e8b708fdebda8729bc682b5468a43d74d7a576b722e6ffbbdf9cb0370d0ab6833f161c50 languageName: node linkType: hard @@ -10087,138 +10481,139 @@ __metadata: languageName: node linkType: hard -"@react-stately/calendar@npm:^3.9.1": - version: 3.9.1 - resolution: "@react-stately/calendar@npm:3.9.1" +"@react-stately/calendar@npm:^3.9.3": + version: 3.9.3 + resolution: "@react-stately/calendar@npm:3.9.3" dependencies: - "@internationalized/date": "npm:^3.10.1" + "@internationalized/date": "npm:^3.12.0" "@react-stately/utils": "npm:^3.11.0" - "@react-types/calendar": "npm:^3.8.1" - "@react-types/shared": "npm:^3.32.1" + "@react-types/calendar": "npm:^3.8.3" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/5dde2f2643ca239d356bdffc5a63fd0c3a1b52c9a3eeff9079d4351bbef04b5e199a58df32f4257a21de3645a5036903bdfe9641abe9c0e7bbed78223aaf9367 + checksum: 10c0/24231a0b0c270b9936a043b4e17091005fce0749804f1b0ea7e0f101f945081255d08439186d0229430f2ade745c9d2eaa131e0b154d236e0dc3b0912a93864e languageName: node linkType: hard -"@react-stately/checkbox@npm:^3.7.3": - version: 3.7.3 - resolution: "@react-stately/checkbox@npm:3.7.3" +"@react-stately/checkbox@npm:^3.7.5": + version: 3.7.5 + resolution: "@react-stately/checkbox@npm:3.7.5" dependencies: - "@react-stately/form": "npm:^3.2.2" + "@react-stately/form": "npm:^3.2.4" "@react-stately/utils": "npm:^3.11.0" - "@react-types/checkbox": "npm:^3.10.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/checkbox": "npm:^3.10.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/20b510f50a0be5c70dd1e291498c48b5de10062ee01dc06860003dac378d0445bf3f73cd332591cec57b86e1370355ec1018ec04d00a82f90a6248d44e92c594 + checksum: 10c0/c60a931853af7882e7fa498f3e02c0ef13fbf824696f504869e22f1f1fc65967d3f05f9dd66a4b466bb8a8533113bddf713c712abcbcae07f50fb11c952db4c5 languageName: node linkType: hard -"@react-stately/collections@npm:^3.12.8": - version: 3.12.8 - resolution: "@react-stately/collections@npm:3.12.8" +"@react-stately/collections@npm:^3.12.10": + version: 3.12.10 + resolution: "@react-stately/collections@npm:3.12.10" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/3fd0ebd2e1c4bfe77f1dac79933c6301b39b3345de191d307af2daf764663b83d4a9a7ae7ca669245e140868850912182c78983057b588fe2d6a407b4520ae52 + checksum: 10c0/6c033e1e4f345bcad9cc725f63a63c2908d45ee96c44a27bf67d41f1c6065f9bce3177de2168cff53ca33ebe93028a8806e3f45659f600efcac739fae02876c1 languageName: node linkType: hard -"@react-stately/color@npm:^3.9.3": - version: 3.9.3 - resolution: "@react-stately/color@npm:3.9.3" +"@react-stately/color@npm:^3.9.5": + version: 3.9.5 + resolution: "@react-stately/color@npm:3.9.5" dependencies: "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" - "@react-stately/form": "npm:^3.2.2" - "@react-stately/numberfield": "npm:^3.10.3" - "@react-stately/slider": "npm:^3.7.3" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/numberfield": "npm:^3.11.0" + "@react-stately/slider": "npm:^3.7.5" "@react-stately/utils": "npm:^3.11.0" - "@react-types/color": "npm:^3.1.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/color": "npm:^3.1.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/d836086a741f6b4491c48646d7b1bcfa29b09bb2550bd151d462feccd6a5f895f1fb049e557a071665d5bd2906402d3858553c0b71ff39e04142a4c76fb53f34 + checksum: 10c0/7fa5b21ef06ee3aab466e9caf1a4c6c9c4591828d433d0600777b1f00324abe4918572dd6ec6640d9a7ed41e631ef7ae19f7fa277c3232ae7fb20b1057b0938b languageName: node linkType: hard -"@react-stately/combobox@npm:^3.12.1": - version: 3.12.1 - resolution: "@react-stately/combobox@npm:3.12.1" +"@react-stately/combobox@npm:^3.13.0": + version: 3.13.0 + resolution: "@react-stately/combobox@npm:3.13.0" dependencies: - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/form": "npm:^3.2.2" - "@react-stately/list": "npm:^3.13.2" - "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/list": "npm:^3.13.4" + "@react-stately/overlays": "npm:^3.6.23" "@react-stately/utils": "npm:^3.11.0" - "@react-types/combobox": "npm:^3.13.10" - "@react-types/shared": "npm:^3.32.1" + "@react-types/combobox": "npm:^3.14.0" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/abdb03341614614218a121c24bb4dcf44594c482ac54e589833ac0b94e2178f3689aa36a188fa1467b1fbe1e1e23a993bb373088fa3f7eb01638eed15588f850 + checksum: 10c0/b538778862fa5107e8c8b54f630a5d0a6ec2c1d35ae9111b813baf0bdfd55f79cedd8b3dcf0a8ad118edfb9f300506a382b09c8a13b706f183d6d61156e4533d languageName: node linkType: hard -"@react-stately/data@npm:^3.15.0": - version: 3.15.0 - resolution: "@react-stately/data@npm:3.15.0" +"@react-stately/data@npm:^3.15.2": + version: 3.15.2 + resolution: "@react-stately/data@npm:3.15.2" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/3f5fb59cabe92c0750a6c86e04c8469d53b48ab53e90a3db00eb9e9097e85029b3ff28800b49273a2c0ad19fbe02101ca0d160c026eae36630ea52f8906df238 + checksum: 10c0/b8765059eac1e19dac9e0e4407f1a36e4204b62dd7efc87a1fe157574869df17f9852a7f6c94a8813b3844c38023f25317fe83eff1cd752552da59235b9c5f14 languageName: node linkType: hard -"@react-stately/datepicker@npm:^3.15.3": - version: 3.15.3 - resolution: "@react-stately/datepicker@npm:3.15.3" +"@react-stately/datepicker@npm:^3.16.1": + version: 3.16.1 + resolution: "@react-stately/datepicker@npm:3.16.1" dependencies: - "@internationalized/date": "npm:^3.10.1" + "@internationalized/date": "npm:^3.12.0" + "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" - "@react-stately/form": "npm:^3.2.2" - "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/overlays": "npm:^3.6.23" "@react-stately/utils": "npm:^3.11.0" - "@react-types/datepicker": "npm:^3.13.3" - "@react-types/shared": "npm:^3.32.1" + "@react-types/datepicker": "npm:^3.13.5" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/41a23ec44259fd3e1ba2848532301d0f6420f7fb4635da0e76a55f2a7075ec70b10665ff10bfa9bd3ad6a9c70d085ce0cd65a5d81d3457bdc4e1062282148b38 + checksum: 10c0/7b4d044e9f5feb30ee5d4ac66fce9294a97464471d659d6fc35d6d0d4f8280283b1cfcbf5f232ac38ed16f4b765b15ac6d67399efb3f78488cf4e46d015e4c56 languageName: node linkType: hard -"@react-stately/disclosure@npm:^3.0.9": - version: 3.0.9 - resolution: "@react-stately/disclosure@npm:3.0.9" +"@react-stately/disclosure@npm:^3.0.11": + version: 3.0.11 + resolution: "@react-stately/disclosure@npm:3.0.11" dependencies: "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/4880d57688b33253afde5637984cbdcdbe32943cb27f3034d480dcaa41ffafc3240834cc88d01489320ec8b8b7f592b4823bff1e89a90cc0d7b603d8f18969f4 + checksum: 10c0/1b588e777fe01a527897cfce0fbc07525df02ee602e2a3a2415e5ef28596af965cbefc0a7f603aaea413f3ca1cb46ee236d0544550cf4906cb1a2d2ea6eb20fc languageName: node linkType: hard -"@react-stately/dnd@npm:^3.7.2": - version: 3.7.2 - resolution: "@react-stately/dnd@npm:3.7.2" +"@react-stately/dnd@npm:^3.7.4": + version: 3.7.4 + resolution: "@react-stately/dnd@npm:3.7.4" dependencies: - "@react-stately/selection": "npm:^3.20.7" - "@react-types/shared": "npm:^3.32.1" + "@react-stately/selection": "npm:^3.20.9" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/bcf4fc361cf7c8e759baaec4a3dc8aaca3ea13d9be81a8a9454f85f62f9526c35b5b4e8cc5683461ce94d8e874f6ee17b5b49113f8a595dfdf82099a534cd96b + checksum: 10c0/9cb1292c04364e631ace5d5e86f0b89423c958a7b80dcd22ef2f36edca20a3cd9315af327a79d337e60d349ffcc770c9b59e0ab9dc206af5e42e6aad34229467 languageName: node linkType: hard @@ -10231,265 +10626,265 @@ __metadata: languageName: node linkType: hard -"@react-stately/form@npm:^3.2.2": - version: 3.2.2 - resolution: "@react-stately/form@npm:3.2.2" +"@react-stately/form@npm:^3.2.4": + version: 3.2.4 + resolution: "@react-stately/form@npm:3.2.4" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c7950b8f8bf073ccff12fe9cdc839527cf3e403852d3bbd9a9ec921224c81c94e6f1e9aa29106d12fc38296385e513384177e5efe78cdaf8d1acdec2b59af583 + checksum: 10c0/e940fd5a8fa0fcd3f09acdc3eb97b12fa84690d6a4fc01a52da22673e4a2580b76626901296cad7ee7ed9ea43deb9a48e6af248bf4d59a4fe4c599f30941ecb3 languageName: node linkType: hard -"@react-stately/grid@npm:^3.11.7": - version: 3.11.7 - resolution: "@react-stately/grid@npm:3.11.7" +"@react-stately/grid@npm:^3.11.9": + version: 3.11.9 + resolution: "@react-stately/grid@npm:3.11.9" dependencies: - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/selection": "npm:^3.20.7" - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/selection": "npm:^3.20.9" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/b00bc984a52cdbf077473780fac0c711d99c4c308ac8500ff35b7e7bbd20f20a995af4b810756868e305e35dac53513f0d908cb0637699c33cc7c932953b69ff + checksum: 10c0/6fbd5be08268a893254f19a26495f7b578e78fd51373f92711603295c0335cfaf36a4478db77cc48fa585e22963370b232b3d394037c74125b90349e14bb0631 languageName: node linkType: hard -"@react-stately/layout@npm:^4.5.2": - version: 4.5.2 - resolution: "@react-stately/layout@npm:4.5.2" - dependencies: - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/table": "npm:^3.15.2" - "@react-stately/virtualizer": "npm:^4.4.4" - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" - "@react-types/table": "npm:^3.13.4" +"@react-stately/layout@npm:^4.6.0": + version: 4.6.0 + resolution: "@react-stately/layout@npm:4.6.0" + dependencies: + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/table": "npm:^3.15.4" + "@react-stately/virtualizer": "npm:^4.4.6" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" + "@react-types/table": "npm:^3.13.6" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/800a064d425deb9d9d0ca1f40cd29a6160a11640c7c42d72ab5a8f62b054daf51daa78bec98c2c2ddd6da57b8952abdbd64733d3f6e223df85da54711a52d5fc + checksum: 10c0/cb50d63cf725776ccade53baa7256ce9708591ac63c6849997acf74774a2ee0dca1f2cc16164ff2eb8c302b0f30bdb6bab38bc75e882a079887d4c6d6c1ff23a languageName: node linkType: hard -"@react-stately/list@npm:^3.13.2": - version: 3.13.2 - resolution: "@react-stately/list@npm:3.13.2" +"@react-stately/list@npm:^3.13.4": + version: 3.13.4 + resolution: "@react-stately/list@npm:3.13.4" dependencies: - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/selection": "npm:^3.20.7" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/selection": "npm:^3.20.9" "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/1611c786cc722af12e6a70dc24bb07c2c09e112a9423c3377aafac63e01f23532c74072cd246793148d858dc36d3a7402c15231aac2ff7616021e7e031fb8e1a + checksum: 10c0/df5c4b07bd81fc2a3db13fa618aec99f91b94467d107f3ab123de6c336f757faf120c4fb3e04aa68a635026240668f78902ea9c4d3f3da6b4238506ecbbd5721 languageName: node linkType: hard -"@react-stately/menu@npm:^3.9.9": - version: 3.9.9 - resolution: "@react-stately/menu@npm:3.9.9" +"@react-stately/menu@npm:^3.9.11": + version: 3.9.11 + resolution: "@react-stately/menu@npm:3.9.11" dependencies: - "@react-stately/overlays": "npm:^3.6.21" - "@react-types/menu": "npm:^3.10.5" - "@react-types/shared": "npm:^3.32.1" + "@react-stately/overlays": "npm:^3.6.23" + "@react-types/menu": "npm:^3.10.7" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/a9a6e9f13849a2ce7192103b9d4c46fb2ef69d6767a0e0e29ac9adcba9dc061aa233e429425c1499fdf60ab7eb378bf24719c3f5b7ff76a8094af7bc77f5baab + checksum: 10c0/6006e7fd865eee1b4accdb22a13fbbd5c11de5f5b613a562b280eefbd2b1c3adb273348047d6214a5f53f6b4486202f46114969235110e02c1e208bf9e6eb635 languageName: node linkType: hard -"@react-stately/numberfield@npm:^3.10.3": - version: 3.10.3 - resolution: "@react-stately/numberfield@npm:3.10.3" +"@react-stately/numberfield@npm:^3.11.0": + version: 3.11.0 + resolution: "@react-stately/numberfield@npm:3.11.0" dependencies: "@internationalized/number": "npm:^3.6.5" - "@react-stately/form": "npm:^3.2.2" + "@react-stately/form": "npm:^3.2.4" "@react-stately/utils": "npm:^3.11.0" - "@react-types/numberfield": "npm:^3.8.16" + "@react-types/numberfield": "npm:^3.8.18" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/da8f14a3938eaa8025a2a56279fdbe23151cb1e3b0de2d1c817ab50882d3492ab3c056bda9ad806a4b8f2461be0359f183a12944c57b6fe3becf70399b0e454a + checksum: 10c0/5e82aed4dadb5a0ef38385ef07c961c88d27f96e4d1a7fa8ebb79ebb825d235f919e31cf97cb09e369d412682742acbef631c7b876cbedd040d65e1df0d8af72 languageName: node linkType: hard -"@react-stately/overlays@npm:^3.6.21": - version: 3.6.21 - resolution: "@react-stately/overlays@npm:3.6.21" +"@react-stately/overlays@npm:^3.6.23": + version: 3.6.23 + resolution: "@react-stately/overlays@npm:3.6.23" dependencies: "@react-stately/utils": "npm:^3.11.0" - "@react-types/overlays": "npm:^3.9.2" + "@react-types/overlays": "npm:^3.9.4" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/1f52664e8a21f2841b3818e35588188ac86f03b783c8caa47290abc7582a778ff06416071fd504429e6c808ea13fdf004681f5d2e001955da57c5310daf959bc + checksum: 10c0/7c62160e11bbeb58780a629e09f0c8b16716a06b5c6114909fc45bb0118d55913b50aab3bbee2d6f60bbdd1075fed234dd361b3595b6cc49115a370c66b01ff3 languageName: node linkType: hard -"@react-stately/radio@npm:^3.11.3": - version: 3.11.3 - resolution: "@react-stately/radio@npm:3.11.3" +"@react-stately/radio@npm:^3.11.5": + version: 3.11.5 + resolution: "@react-stately/radio@npm:3.11.5" dependencies: - "@react-stately/form": "npm:^3.2.2" + "@react-stately/form": "npm:^3.2.4" "@react-stately/utils": "npm:^3.11.0" - "@react-types/radio": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/radio": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/77d8acf82b07f778c6c6655168543945ea842d365c99734f1c60ddf968169fa06ddbcc932a7733c336a679a53a175ac3a23620175f087a4619a6028bd543a91c + checksum: 10c0/85a3cdd263e91445b4c739a8cf817a44032aab4c0863b385ebda49009d5c737fa653d087fc992771304037972d4d31fd065c00a46a36f11f688cb2e9e5da215e languageName: node linkType: hard -"@react-stately/searchfield@npm:^3.5.17": - version: 3.5.17 - resolution: "@react-stately/searchfield@npm:3.5.17" +"@react-stately/searchfield@npm:^3.5.19": + version: 3.5.19 + resolution: "@react-stately/searchfield@npm:3.5.19" dependencies: "@react-stately/utils": "npm:^3.11.0" - "@react-types/searchfield": "npm:^3.6.6" + "@react-types/searchfield": "npm:^3.6.8" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/73e159f0f34b8569b378d44983600566a0374b75ad4fc17eade961c10c63f053eb9e4e5c16dfcc8e590fb4120e058a0260918d2a658eb9e6d3cde3afaaa808eb + checksum: 10c0/f0b0cb7bbfdc7415049739984b6670baff4e45e62d26026050bd916a0c45e1f740fa53f4bc5d08326ae6b1b41ea8db219e34b3e6c053d8d8398536eeeee396ed languageName: node linkType: hard -"@react-stately/select@npm:^3.9.0": - version: 3.9.0 - resolution: "@react-stately/select@npm:3.9.0" +"@react-stately/select@npm:^3.9.2": + version: 3.9.2 + resolution: "@react-stately/select@npm:3.9.2" dependencies: - "@react-stately/form": "npm:^3.2.2" - "@react-stately/list": "npm:^3.13.2" - "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/list": "npm:^3.13.4" + "@react-stately/overlays": "npm:^3.6.23" "@react-stately/utils": "npm:^3.11.0" - "@react-types/select": "npm:^3.12.0" - "@react-types/shared": "npm:^3.32.1" + "@react-types/select": "npm:^3.12.2" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/18b486c1b3c15d513f9c8e322986a6fedcc395bdfbabe9d8b3120b777613ded8652beaffc6641d856609f2692a0d70245daa9bab39ca2e9e0a375214f0da0a17 + checksum: 10c0/613ec7668802085f5b281d651ec0e2229b45dc17b2b750342bfbba928965af1da1e64f0214f89eafc836710ee440406863b8ac26b80e8d63c2fe9945a587f3f5 languageName: node linkType: hard -"@react-stately/selection@npm:^3.20.7": - version: 3.20.7 - resolution: "@react-stately/selection@npm:3.20.7" +"@react-stately/selection@npm:^3.20.9": + version: 3.20.9 + resolution: "@react-stately/selection@npm:3.20.9" dependencies: - "@react-stately/collections": "npm:^3.12.8" + "@react-stately/collections": "npm:^3.12.10" "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/5cee2680b35632868d8049b9a80440c0367795d6a6bcd183fe10e2e889185c1eaadf1538014eff8a1f39a8553c6c43198940c66a5a357a3e074f550346cf1f82 + checksum: 10c0/c006938877d900a885e39d066f5f5bc00fe346c197b4514b7cd93256e402d88f3f133e84f0ca22385d975936d641e6535ba1560cf73333c2314d6621d08b2cd3 languageName: node linkType: hard -"@react-stately/slider@npm:^3.7.3": - version: 3.7.3 - resolution: "@react-stately/slider@npm:3.7.3" +"@react-stately/slider@npm:^3.7.5": + version: 3.7.5 + resolution: "@react-stately/slider@npm:3.7.5" dependencies: "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" - "@react-types/slider": "npm:^3.8.2" + "@react-types/shared": "npm:^3.33.1" + "@react-types/slider": "npm:^3.8.4" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/725d7ff07b48086b0c0d1dddb61c6ea3a7f4effd6845a13a4e45c81126e368825f4d1586d067b82f0a85b4bbb5aa9d0aaf20622721d1dd69802361c221f6f572 + checksum: 10c0/e1c14a4354f910bf967b4e450e02bb447baa3a268460a00b19944ce457785ec19a3fb386a82c7d1d7ff6a110a368193a008418ecfbaab9844681d7391d4568a0 languageName: node linkType: hard -"@react-stately/table@npm:^3.15.2": - version: 3.15.2 - resolution: "@react-stately/table@npm:3.15.2" +"@react-stately/table@npm:^3.15.4": + version: 3.15.4 + resolution: "@react-stately/table@npm:3.15.4" dependencies: - "@react-stately/collections": "npm:^3.12.8" + "@react-stately/collections": "npm:^3.12.10" "@react-stately/flags": "npm:^3.1.2" - "@react-stately/grid": "npm:^3.11.7" - "@react-stately/selection": "npm:^3.20.7" + "@react-stately/grid": "npm:^3.11.9" + "@react-stately/selection": "npm:^3.20.9" "@react-stately/utils": "npm:^3.11.0" - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" - "@react-types/table": "npm:^3.13.4" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" + "@react-types/table": "npm:^3.13.6" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/e7ae6e2d9606cf45c7c1c870e12fe13ac66e0321aa905adf1b1fba2e10caccbc9b93cd70890ba54363a602fcc38a2e429f8464b86e9ad3de17448a55c51e6dd3 + checksum: 10c0/47fef5047d1e0aeb7e34db1f575d9f92b4b6bb5264dcf59862fed32d227863b6fc0b0b4357cb542fa25cc2f4d271e719e3a04bf1875960d01b6fa9860956ff07 languageName: node linkType: hard -"@react-stately/tabs@npm:^3.8.7": - version: 3.8.7 - resolution: "@react-stately/tabs@npm:3.8.7" +"@react-stately/tabs@npm:^3.8.9": + version: 3.8.9 + resolution: "@react-stately/tabs@npm:3.8.9" dependencies: - "@react-stately/list": "npm:^3.13.2" - "@react-types/shared": "npm:^3.32.1" - "@react-types/tabs": "npm:^3.3.20" + "@react-stately/list": "npm:^3.13.4" + "@react-types/shared": "npm:^3.33.1" + "@react-types/tabs": "npm:^3.3.22" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/6cf902c193d0557909a107ecc1283841c64f56519f98ff0c20b83bcc94c19d53ecbb00a5132346988f950fe5afef4ac0ae14d63d9f272ed1c330a47c31990fbc + checksum: 10c0/9c61603a3c928b5caa40b02fe0fb82e0c9085d3f3dc624bc0e911981097fe3db4a03c13e794178f3fc47365a4a71b46b1098346da00b5464ad19277b936b4236 languageName: node linkType: hard -"@react-stately/toast@npm:^3.1.2": - version: 3.1.2 - resolution: "@react-stately/toast@npm:3.1.2" +"@react-stately/toast@npm:^3.1.3": + version: 3.1.3 + resolution: "@react-stately/toast@npm:3.1.3" dependencies: "@swc/helpers": "npm:^0.5.0" - use-sync-external-store: "npm:^1.4.0" + use-sync-external-store: "npm:^1.6.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/5de06a2ca5830824a236f809e44a5084ae58a4f463c86aa2e72ec84c8ca632dfe1f5054248a9a1f6ee2aa213e22bfc186e0f4d5ef9a552eb369ee906686f8fec + checksum: 10c0/b6965d260625a837899edb0dc771ef3019cbdf79a80d9b43f4a69ed8194f49a2bb0f8dac0e63aa25dd400cac0d9b6ee7d6ab097a65ef4a1a77dd84dfcea1d472 languageName: node linkType: hard -"@react-stately/toggle@npm:^3.9.3": - version: 3.9.3 - resolution: "@react-stately/toggle@npm:3.9.3" +"@react-stately/toggle@npm:^3.9.5": + version: 3.9.5 + resolution: "@react-stately/toggle@npm:3.9.5" dependencies: "@react-stately/utils": "npm:^3.11.0" - "@react-types/checkbox": "npm:^3.10.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/checkbox": "npm:^3.10.4" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/d7b7b4ec1ade79d5c600e62454aefc552a74013197ab4fa236296d426b93fcdaecccebb1b6ea7658c6ffc08448e5787f9d25e9c40f971a4644eb8e142fd830c2 + checksum: 10c0/eebc198e2d8a9f90d94d42e90657147ac380cc7db053549d203a97780963d007f92545e5dab03a8126530c6444254d1b802a2a7918dfc537e16d172e17172d20 languageName: node linkType: hard -"@react-stately/tooltip@npm:^3.5.9": - version: 3.5.9 - resolution: "@react-stately/tooltip@npm:3.5.9" +"@react-stately/tooltip@npm:^3.5.11": + version: 3.5.11 + resolution: "@react-stately/tooltip@npm:3.5.11" dependencies: - "@react-stately/overlays": "npm:^3.6.21" - "@react-types/tooltip": "npm:^3.5.0" + "@react-stately/overlays": "npm:^3.6.23" + "@react-types/tooltip": "npm:^3.5.2" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/64dbf61d673bd6d9c27a8b1e01844ceeb0979ca6a4408a8361f6de538c2b8b0d5398786ae20b6426a59bacca23d5c6d451c7f9b05bf78cf516b9cc14686c1aa0 + checksum: 10c0/d21970faad11e33d3255ef7957ca299951f4b4e677b3a49418f534b1bc3ce5a627fd3531b23e7e9d77e4de6e5d2d1300c713de271e9614b0e5b12ced29d93103 languageName: node linkType: hard -"@react-stately/tree@npm:^3.9.4": - version: 3.9.4 - resolution: "@react-stately/tree@npm:3.9.4" +"@react-stately/tree@npm:^3.9.6": + version: 3.9.6 + resolution: "@react-stately/tree@npm:3.9.6" dependencies: - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/selection": "npm:^3.20.7" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/selection": "npm:^3.20.9" "@react-stately/utils": "npm:^3.11.0" - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/0c5e82caedf2d9d9e9d096e7f2650ceace40daf114b15e3ed491e76d3651dc7f742cff6dcb5599578ab4b88a077aac94a1bba06f6c0a32394e3cf9fe9848b7fa + checksum: 10c0/96b82237a95d3d6957989f36fd7595263f5c28b786e65efe3c51e18b379e1385cddcfd0b806380ed046798df76020a12eb12d4f4a810d15cfaed579e95e63e26 languageName: node linkType: hard @@ -10504,335 +10899,335 @@ __metadata: languageName: node linkType: hard -"@react-stately/virtualizer@npm:^4.4.4": - version: 4.4.4 - resolution: "@react-stately/virtualizer@npm:4.4.4" +"@react-stately/virtualizer@npm:^4.4.6": + version: 4.4.6 + resolution: "@react-stately/virtualizer@npm:4.4.6" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/cc8565c19eaae881956111722ae56b0dbbb7e89a42700e17cb103edd03d1d579667798c8d956690118230be74b87386fdd8c024951b3b25f8804d679cd3a8cfd + checksum: 10c0/fc7bdd9000ddfc0badec8e9adc8766e2344bf039f3dad6ae6aaee0d1b076a5c209d74f63e5cbfa8dc200ef740059296e36b5cb04069e0130303235698235ba51 languageName: node linkType: hard -"@react-types/autocomplete@npm:3.0.0-alpha.36": - version: 3.0.0-alpha.36 - resolution: "@react-types/autocomplete@npm:3.0.0-alpha.36" +"@react-types/autocomplete@npm:3.0.0-alpha.38": + version: 3.0.0-alpha.38 + resolution: "@react-types/autocomplete@npm:3.0.0-alpha.38" dependencies: - "@react-types/combobox": "npm:^3.13.10" - "@react-types/searchfield": "npm:^3.6.6" - "@react-types/shared": "npm:^3.32.1" + "@react-types/combobox": "npm:^3.14.0" + "@react-types/searchfield": "npm:^3.6.8" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/02a2d3da8cdba9e200eb90d145749acf5858e6523ba5b175d82838628f7d7bcb84263f4cec985613a1fc751715af5504bf18e46939a0e8300bc8ea3f2e92073d + checksum: 10c0/b5629078aa912fbcde06ff9b6247c0243752b42691b5d88e248b8d3d04dfd66eb53c1326144d36cb8ce74f5dc33902a15d13807c639304341add9af96185c7fa languageName: node linkType: hard -"@react-types/breadcrumbs@npm:^3.7.17": - version: 3.7.17 - resolution: "@react-types/breadcrumbs@npm:3.7.17" +"@react-types/breadcrumbs@npm:^3.7.19": + version: 3.7.19 + resolution: "@react-types/breadcrumbs@npm:3.7.19" dependencies: - "@react-types/link": "npm:^3.6.5" - "@react-types/shared": "npm:^3.32.1" + "@react-types/link": "npm:^3.6.7" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/4004f74909de12db959688653dfc22399a7d48c9a30749ff20e9ce012008a2be8d35e696402f98afe428e62565c91abaa75fb6e7192260c4e5832488136ac899 + checksum: 10c0/23502634fa73d3a2b54cf298a172b22df73e5cbf8296770fbcf028bc9edf338fe68f56f34cfc41cf1d195722b0dff75caab28215f6a3bccd1f809197896646c4 languageName: node linkType: hard -"@react-types/button@npm:^3.14.1": - version: 3.14.1 - resolution: "@react-types/button@npm:3.14.1" +"@react-types/button@npm:^3.15.1": + version: 3.15.1 + resolution: "@react-types/button@npm:3.15.1" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/227d92eefe84b7b6b4936ff257e58b33c01b19bb7d3c4ad01c4591675fec9ce72c3a2e63bbf7fec834b5b6f44f8fe21a60a584ec04ad0093781bccdc5cf62479 + checksum: 10c0/2b05f8c6735ebc46ee832f1065593f86ab64028c38147d71204c8ecda5599e345c210b6d7341c135842dd7efb149dc114ecb0e43d76365b82bdd9341c2659d53 languageName: node linkType: hard -"@react-types/calendar@npm:^3.8.1": - version: 3.8.1 - resolution: "@react-types/calendar@npm:3.8.1" +"@react-types/calendar@npm:^3.8.3": + version: 3.8.3 + resolution: "@react-types/calendar@npm:3.8.3" dependencies: - "@internationalized/date": "npm:^3.10.1" - "@react-types/shared": "npm:^3.32.1" + "@internationalized/date": "npm:^3.12.0" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/083f29679360bef8ab4ae2ddb7ac0ca828fe84f23938ba901f3b9560c20bff1294ae79e5222b842869c303e556156ab1a72639ee2ed345f696c0e16c7ac78987 + checksum: 10c0/c1f129b2f431227d7150639f95201c50f32299813e3e8714f2457335023675ff61b08508b67a687ceef18c0449e1b07c7863688e2710615be746cde6ffb60c0b languageName: node linkType: hard -"@react-types/checkbox@npm:^3.10.2": - version: 3.10.2 - resolution: "@react-types/checkbox@npm:3.10.2" +"@react-types/checkbox@npm:^3.10.4": + version: 3.10.4 + resolution: "@react-types/checkbox@npm:3.10.4" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/500f3de870ed1ccb489ac7ace8698013b196ebaf584210521dde616befc2569d3bdcf16ec8c89c132d1830debe5d693ffd4fe8ad372deaf0e33b973ba1931796 + checksum: 10c0/010479017ce2b42a1f4f10cf539f5fe48c85c555224bac1368c9e0433a7803d58bf2ba84a78597eae34387aac65caa7b64b9e6da8fbb391c91076f590957f25b languageName: node linkType: hard -"@react-types/color@npm:^3.1.2": - version: 3.1.2 - resolution: "@react-types/color@npm:3.1.2" +"@react-types/color@npm:^3.1.4": + version: 3.1.4 + resolution: "@react-types/color@npm:3.1.4" dependencies: - "@react-types/shared": "npm:^3.32.1" - "@react-types/slider": "npm:^3.8.2" + "@react-types/shared": "npm:^3.33.1" + "@react-types/slider": "npm:^3.8.4" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/32a70ddf4a06154b02e1e1cc0e7ff524843165fd1bbad263b44bf2e1b4138fdce4f45507893ecdcf2eb4a1f92173678a8126f71eae052e94d92896bc4306599c + checksum: 10c0/ab0505bad9bca20049539dff6949ba6be84e43e5dda375844d5f4957127ea0af8b565e11c3fb3cb1bff2885d75a9d31cc7c7e210b6a5434f9cd57dc04ef434d9 languageName: node linkType: hard -"@react-types/combobox@npm:^3.13.10": - version: 3.13.10 - resolution: "@react-types/combobox@npm:3.13.10" +"@react-types/combobox@npm:^3.14.0": + version: 3.14.0 + resolution: "@react-types/combobox@npm:3.14.0" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/106eaeabcb62f1dcc6d55a8d4cdd03876b677699a79e7b33e07c45222d43909de79ef81b7746d2f616a958253b8ad5bb5dc8f9c581e1fed628449f63321d556c + checksum: 10c0/6a36a6e2068cb03455e4ce254006ec9377a14ab958bcab0d3f0a8840e467a7efeda25a0817c46a4137ba861115f38dea0bf56d945a89c85356abfcaa45a2df34 languageName: node linkType: hard -"@react-types/datepicker@npm:^3.13.3": - version: 3.13.3 - resolution: "@react-types/datepicker@npm:3.13.3" +"@react-types/datepicker@npm:^3.13.5": + version: 3.13.5 + resolution: "@react-types/datepicker@npm:3.13.5" dependencies: - "@internationalized/date": "npm:^3.10.1" - "@react-types/calendar": "npm:^3.8.1" - "@react-types/overlays": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@internationalized/date": "npm:^3.12.0" + "@react-types/calendar": "npm:^3.8.3" + "@react-types/overlays": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/2a8c429f0f4e04049ce875b3784b25ada8a9a6f52e67b3f300d08cff7fefbbcd7bdb5e87142a1038320c8da9dfe704e2d43f64bce03fd5c96d4bbcd2f81d202b + checksum: 10c0/e011c1d0844ddd36ed50448fb83aec602fc3c6db4f32a5c6d76d3bb168debafb170aa880102de4abb664ff2f77d140037b63ca4a375f4b1574989ce5b471c3c3 languageName: node linkType: hard -"@react-types/dialog@npm:^3.5.22": - version: 3.5.22 - resolution: "@react-types/dialog@npm:3.5.22" +"@react-types/dialog@npm:^3.5.24": + version: 3.5.24 + resolution: "@react-types/dialog@npm:3.5.24" dependencies: - "@react-types/overlays": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/overlays": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/930fc5a744b8925ed807f65c287000cde67d4514d84f468aa5b262e4feb02cc129f2ce1969ebab06508afc3da24a54beb125ad1830bc57926448d6d60e47ba85 + checksum: 10c0/3c9471a620a63c953110593c92f0109072d12cc0d31c0073343589f16af046a3fd55f8cded8370f65d0a7cbaf5ca3744c402cf1baf984c894a3568115ac0655d languageName: node linkType: hard -"@react-types/form@npm:^3.7.16": - version: 3.7.16 - resolution: "@react-types/form@npm:3.7.16" +"@react-types/form@npm:^3.7.18": + version: 3.7.18 + resolution: "@react-types/form@npm:3.7.18" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/2f9a4f993ad26e67045d203175b44538104364ec4adb0cae1c12454b667c9cc401ecd690a6464197868e9b9d9ab18917baa5c05e512da5337c670029d52820f6 + checksum: 10c0/a81ee79dfb3af899f7ef6d635b0c5ab8d85dc589e7d4d5b0e0d85329f6ddc0ed2cfb8406a040a36b0176d9e32ef7e5eb2a92f3fe3261a75d308c3bcae0c4f37a languageName: node linkType: hard -"@react-types/grid@npm:^3.3.6": - version: 3.3.6 - resolution: "@react-types/grid@npm:3.3.6" +"@react-types/grid@npm:^3.3.8": + version: 3.3.8 + resolution: "@react-types/grid@npm:3.3.8" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/6949cfb2526825d657119707d30a7fdc19bc6fab8f41eda58aa3bbe2645ce161bdc1d27de4aa386208c989f4b648f0ec1bd04faf4fecd5b2ebdf3b590bb71325 + checksum: 10c0/2a47eb97919869d5d68ebe3d34e2f48fb36bd5d9b8e8cea973661c4658399484c801b0d1ddbedfea5358e05924e5460306b6f8745a5428c1a163b3d23360053c languageName: node linkType: hard -"@react-types/link@npm:^3.6.5": - version: 3.6.5 - resolution: "@react-types/link@npm:3.6.5" +"@react-types/link@npm:^3.6.7": + version: 3.6.7 + resolution: "@react-types/link@npm:3.6.7" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/f6d882cdd28c92a170be7cece4387b962ba612da1ebaf017e04b92b8bdb6f05ee3008846ef4c76af59978b119b54d9523786b0533d622691226936d377bf119c + checksum: 10c0/6d320b131ef342aa9b8fce0cc6821ccb3bdcdb3b22b620ed32fcbf0e9fc2faff4f55da9ac96dddad6dc4aaa43a42a639dc661382a8082a77f539e1612d14d7bd languageName: node linkType: hard -"@react-types/listbox@npm:^3.7.4": - version: 3.7.4 - resolution: "@react-types/listbox@npm:3.7.4" +"@react-types/listbox@npm:^3.7.6": + version: 3.7.6 + resolution: "@react-types/listbox@npm:3.7.6" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/9d3522dfe706bdb6e182c0adb2cdefc61122a68ae07f1f4a192e69f925bd15fa23390c5c69c305b1c4ede23af4cd6b39e67a74190a164b7b022f2f49d381f3e2 + checksum: 10c0/044cc7672d2cf1efeb037ad6fc7c22d96fe2191d3ca852038ac95de9d69eb788a90e4b96425b39eb86a06b9bd63723d7581dc9c2715390de14932f95d6a73734 languageName: node linkType: hard -"@react-types/menu@npm:^3.10.5": - version: 3.10.5 - resolution: "@react-types/menu@npm:3.10.5" +"@react-types/menu@npm:^3.10.7": + version: 3.10.7 + resolution: "@react-types/menu@npm:3.10.7" dependencies: - "@react-types/overlays": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/overlays": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/7d6b9b681d1b5d338f4ea46b02c2656bff8f74e108c3ed125967d3b8219c2bfde7d76ca483229ec704432ed9745c245e0d0d1c55cbc3cc1d7c33965cbf61184e + checksum: 10c0/4537966ccd72cd1e181082068e383a65eaaad294a1e11b81eda6d7f7d6a507729040be496df403d85d24a0cf28fd2efb5a33aa21a8e6532ff0f0ecf1230a71b5 languageName: node linkType: hard -"@react-types/meter@npm:^3.4.13": - version: 3.4.13 - resolution: "@react-types/meter@npm:3.4.13" +"@react-types/meter@npm:^3.4.15": + version: 3.4.15 + resolution: "@react-types/meter@npm:3.4.15" dependencies: - "@react-types/progress": "npm:^3.5.16" + "@react-types/progress": "npm:^3.5.18" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/f6375cc5b634eeb5ddde40026fab1efed211961960b3290db444c8bba7c6b3d0272bec239c6b90433cf9ac76c7d1d5e93ef9abdf3670288aedb3d2bfca994f90 + checksum: 10c0/6b9dfeb09cc34df848fb36e8d48aca64142a460a39c29a65e63e4d7a1ecac2c2b42210702fa3d3d653ed3f181568722424ac3cffb088eff09581aaffe27b7d62 languageName: node linkType: hard -"@react-types/numberfield@npm:^3.8.16": - version: 3.8.16 - resolution: "@react-types/numberfield@npm:3.8.16" +"@react-types/numberfield@npm:^3.8.18": + version: 3.8.18 + resolution: "@react-types/numberfield@npm:3.8.18" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/ad117da17113fe720535e228427a4a002c4ac6c13aac5c8ffaecb6da58951ce60386fa68828e6ef56dfc1be1fb6f488daaad966c478b43489373c7503cb5bd61 + checksum: 10c0/720e531c7e2aba91fe25193a0adb5acd2a46a04d813d5fef904db409dccaa5c761316f2d0c0e9329e4d305535c8d7569214aac08296df1f0085b0095b649dba7 languageName: node linkType: hard -"@react-types/overlays@npm:^3.9.2": - version: 3.9.2 - resolution: "@react-types/overlays@npm:3.9.2" +"@react-types/overlays@npm:^3.9.4": + version: 3.9.4 + resolution: "@react-types/overlays@npm:3.9.4" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/e9231cda79ce202a6d0f908630310fc1b0fd0246c6b17e4a3df0f77bbb47322580896ce6c6bfa4ef3d56f2ad6868db7538a9f7586c7b6ad0eafcf07398d8dd7b + checksum: 10c0/ecf332ad553029db14cf4a001893ade13f56bb6fc003357d2854166067c2b5dd2229e976cca79bf707c3f7c47d5dedb675d9b2ce45000114e4531dc5aaf00e04 languageName: node linkType: hard -"@react-types/progress@npm:^3.5.16": - version: 3.5.16 - resolution: "@react-types/progress@npm:3.5.16" +"@react-types/progress@npm:^3.5.18": + version: 3.5.18 + resolution: "@react-types/progress@npm:3.5.18" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/7a6ab3d17368b9dd64b0d4d78810c71f59d7ebaca377b7020cbdc21c6c2ba823902d1db69445930fe8455b3be1c8b1c62cfdb540aea8ea51a4c116bffff7d90d + checksum: 10c0/1eeb6a35faa42d7bda167cc8b306e589d509b7fa508c3964f360245f8b13e552afa1e35b9212ad883540f4934774666f5e8e050c0b63c42187f3c85b6d7d382e languageName: node linkType: hard -"@react-types/radio@npm:^3.9.2": - version: 3.9.2 - resolution: "@react-types/radio@npm:3.9.2" +"@react-types/radio@npm:^3.9.4": + version: 3.9.4 + resolution: "@react-types/radio@npm:3.9.4" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/67b085653016232ce84497345ab2201b6c2b423d723e2e27c5413e37b7e4aded965bf5e47901be8cafdcb99161ff6e0b1c5ecada549c19b0a6520b39b2cbcd65 + checksum: 10c0/a9634cf79e0ab6d6aa399b64d2684c28f8e54aaf85bb78a4e9a6457f0a87d199b27afb51f474dba19c7f55cd74ad0570962659c1031a70a8f04e8918f716b393 languageName: node linkType: hard -"@react-types/searchfield@npm:^3.6.6": - version: 3.6.6 - resolution: "@react-types/searchfield@npm:3.6.6" +"@react-types/searchfield@npm:^3.6.8": + version: 3.6.8 + resolution: "@react-types/searchfield@npm:3.6.8" dependencies: - "@react-types/shared": "npm:^3.32.1" - "@react-types/textfield": "npm:^3.12.6" + "@react-types/shared": "npm:^3.33.1" + "@react-types/textfield": "npm:^3.12.8" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/7775b5cd3d321d7c6fa7234224877a0b6f86b8c0d4b8e68e975f7465d7a7960371477da933fd5636554974b728ab333047d4b0e6172c0aab867390724ac669a7 + checksum: 10c0/de035fd6bf9f84758a1bb9d3c19585200959fcf4d58539a0516e2726c15399273093fba6c38bd2b8ad428318ecd21048e3f434571c8fc2437e90cd22fdaabc11 languageName: node linkType: hard -"@react-types/select@npm:^3.12.0": - version: 3.12.0 - resolution: "@react-types/select@npm:3.12.0" +"@react-types/select@npm:^3.12.2": + version: 3.12.2 + resolution: "@react-types/select@npm:3.12.2" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/b491cba525f9329217ceecf67cb5971c12b24b580e71f1fcef15c4f0a66215f3587f5d520a884ab26b8533eadc467e97b58a3d4b5763caad2d6a5623c6a70eb8 + checksum: 10c0/5e4f191174fbe2662d03c0d9a8b8fafa7617cf7a68765649bd739aedd25d30cf1f8466f0ec43d3659c789a43692690940db693d7ee65f07549b62c47caa257ff languageName: node linkType: hard -"@react-types/shared@npm:^3.32.1": - version: 3.32.1 - resolution: "@react-types/shared@npm:3.32.1" +"@react-types/shared@npm:^3.33.1": + version: 3.33.1 + resolution: "@react-types/shared@npm:3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/0a67a34e791c598c5819beb9aa5c11e67db06c9fccc9c5304453147b877fdfc7e73d520e92fcdde8b743e2f155b4cb6a50a15792001a776151191af73d60e24c + checksum: 10c0/072dbf92de80e3535441fbf4a9075009636221974d0b70bd1078ec1e343ae1373d69d2c02c2fba0963e50f4b134872144ffaf1573daec8477233e408f96e008c languageName: node linkType: hard -"@react-types/slider@npm:^3.8.2": - version: 3.8.2 - resolution: "@react-types/slider@npm:3.8.2" +"@react-types/slider@npm:^3.8.4": + version: 3.8.4 + resolution: "@react-types/slider@npm:3.8.4" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/b5184dc0097cd63563adb67c0a0ca5f9d152f98913b1d8033fac407bfb02ce59c1c3f79b059ae5772db268c689752ce610931d954ba9de03d662aa97c1a7501f + checksum: 10c0/e1bbc112d88aa73d1a808cac6df2b3cde14d144af8d82f2dd47a7a7b752cdfecd5b9238b16fe47aeb23af1b0767707d99bb48138bedc97df43cd61e9d023ceed languageName: node linkType: hard -"@react-types/switch@npm:^3.5.15": - version: 3.5.15 - resolution: "@react-types/switch@npm:3.5.15" +"@react-types/switch@npm:^3.5.17": + version: 3.5.17 + resolution: "@react-types/switch@npm:3.5.17" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/ce99a38e87f6f128df2844de756f1264aa8b5f1fa6f70695663a49e25aaacac84922e13c04793351b489f24ab0b4ecc1d5ed8f091f9dbf371396c01808db4349 + checksum: 10c0/d88735ac152673a96da90731bfa072e1c2b548c888dac508f363d8042e49b9ad3157d084db2a5394e29103742dde789f201cfeeb531fe66523eda7701d9e2bfb languageName: node linkType: hard -"@react-types/table@npm:^3.13.4": - version: 3.13.4 - resolution: "@react-types/table@npm:3.13.4" +"@react-types/table@npm:^3.13.6": + version: 3.13.6 + resolution: "@react-types/table@npm:3.13.6" dependencies: - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/3cd3a6c6b548fa2ec2ee60855257e56237e7e99e8f80fcf2b192f9c557007f05feb0328adee3a61057fd88c215d2983f3df6134f9d5d8813d3fe46d5065b5f8a + checksum: 10c0/32a8e88376050e7c8396f39cda8b81c02a27b7ffa0d4c47cadd52c00e24ea74c681002f63a811904a48414d6ffd199c074b2cf738eeef5c6b61170d3e158b0b5 languageName: node linkType: hard -"@react-types/tabs@npm:^3.3.20": - version: 3.3.20 - resolution: "@react-types/tabs@npm:3.3.20" +"@react-types/tabs@npm:^3.3.22": + version: 3.3.22 + resolution: "@react-types/tabs@npm:3.3.22" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/3f8e45d96923456c74b3224552f49481954ceb4d38e3bdd9d3073da7e7e04d6223891355d046688a88e32e1dce148555f0225cfeb377af6fcaf685a9f4697b4d + checksum: 10c0/b2318b580e3cf4a13918129424fd6f72ad7836e4244d9bb0f437462045a1b3b602b8eb341b1e1b5f20fb63ac5f9a59f6960167706157fbc950371fc260b4ded6 languageName: node linkType: hard -"@react-types/textfield@npm:^3.12.6": - version: 3.12.6 - resolution: "@react-types/textfield@npm:3.12.6" +"@react-types/textfield@npm:^3.12.8": + version: 3.12.8 + resolution: "@react-types/textfield@npm:3.12.8" dependencies: - "@react-types/shared": "npm:^3.32.1" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/c8f54002d34744031e1caac9efcfb2d2a631b4c42b79dfe61045ac765b33d54fe7a1fcee01169343c188cfa7b28540b3e157b1ad4a39fab0aef0b4bf14024433 + checksum: 10c0/9cebea13054ad7f191770b66a5aed35f627c4e5a4d1ac8163304a452f16254357cbf5fb51f0beafce4d770ea45526cc4311371a428ad709c9e50f8795ee18e6d languageName: node linkType: hard -"@react-types/tooltip@npm:^3.5.0": - version: 3.5.0 - resolution: "@react-types/tooltip@npm:3.5.0" +"@react-types/tooltip@npm:^3.5.2": + version: 3.5.2 + resolution: "@react-types/tooltip@npm:3.5.2" dependencies: - "@react-types/overlays": "npm:^3.9.2" - "@react-types/shared": "npm:^3.32.1" + "@react-types/overlays": "npm:^3.9.4" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/2b24b4eee393e6d329f1bdd9cbd6c0b1aadff2f68310bbed14dcd1e785afd0abeb248c3fd1cd93f647dbcc3a3d61db95d2cb1c1b6a9696603e269e6edf647b69 + checksum: 10c0/d94fd3102e69f459b4d67c561ee9306e9f2cec4456b868a668bf6448a22ac8df8b97d8e349903d9d664d6dfebc4acc29c824f8026e65a15a9abf851d54e7f810 languageName: node linkType: hard @@ -10863,13 +11258,6 @@ __metadata: languageName: node linkType: hard -"@repeaterjs/repeater@npm:^3.0.4, @repeaterjs/repeater@npm:^3.0.6": - version: 3.0.6 - resolution: "@repeaterjs/repeater@npm:3.0.6" - checksum: 10c0/c3915e2603927c7d6a9eb09673bc28fc49ab3a86947ec191a74663b33deebee2fcc4b03c31cc663ff27bd6db9e6c9487639b6935e265d601ce71b8c497f5f4a8 - languageName: node - linkType: hard - "@rjsf/core@npm:5.24.13": version: 5.24.13 resolution: "@rjsf/core@npm:5.24.13" @@ -13194,7 +13582,7 @@ __metadata: languageName: node linkType: hard -"@swc/helpers@npm:^0.5.0, @swc/helpers@npm:^0.5.17, @swc/helpers@npm:^0.5.8": +"@swc/helpers@npm:^0.5.0, @swc/helpers@npm:^0.5.8": version: 0.5.18 resolution: "@swc/helpers@npm:0.5.18" dependencies: @@ -13281,19 +13669,19 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:^9.0.0": - version: 9.3.4 - resolution: "@testing-library/dom@npm:9.3.4" +"@testing-library/dom@npm:^10.0.0": + version: 10.4.1 + resolution: "@testing-library/dom@npm:10.4.1" dependencies: "@babel/code-frame": "npm:^7.10.4" "@babel/runtime": "npm:^7.12.5" "@types/aria-query": "npm:^5.0.1" - aria-query: "npm:5.1.3" - chalk: "npm:^4.1.0" + aria-query: "npm:5.3.0" dom-accessibility-api: "npm:^0.5.9" lz-string: "npm:^1.5.0" + picocolors: "npm:1.1.1" pretty-format: "npm:^27.0.2" - checksum: 10c0/147da340e8199d7f98f3a4ad8aa22ed55b914b83957efa5eb22bfea021a979ebe5a5182afa9c1e5b7a5f99a7f6744a5a4d9325ae46ec3b33b5a15aed8750d794 + checksum: 10c0/19ce048012d395ad0468b0dbcc4d0911f6f9e39464d7a8464a587b29707eed5482000dad728f5acc4ed314d2f4d54f34982999a114d2404f36d048278db815b1 languageName: node linkType: hard @@ -13311,20 +13699,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:^14.0.0": - version: 14.3.1 - resolution: "@testing-library/react@npm:14.3.1" - dependencies: - "@babel/runtime": "npm:^7.12.5" - "@testing-library/dom": "npm:^9.0.0" - "@types/react-dom": "npm:^18.0.0" - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - checksum: 10c0/1ccf4eb1510500cc20a805cb0244c9098dca28a8745173a8f71ea1274d63774f0b7898a35c878b43c797b89c13621548909ff37843b835c1a27ee1efbbdd098c - languageName: node - linkType: hard - "@testing-library/react@npm:^16.0.0": version: 16.3.2 resolution: "@testing-library/react@npm:16.3.2" @@ -13354,20 +13728,6 @@ __metadata: languageName: node linkType: hard -"@theguild/federation-composition@npm:^0.21.3": - version: 0.21.3 - resolution: "@theguild/federation-composition@npm:0.21.3" - dependencies: - constant-case: "npm:^3.0.4" - debug: "npm:4.4.3" - json5: "npm:^2.2.3" - lodash.sortby: "npm:^4.7.0" - peerDependencies: - graphql: ^16.0.0 - checksum: 10c0/cb0880cfc0aa36b476f7e83513f747a9bf9498f13a3ab46f09dccd5ff243ac2d4c67774fec83f291fb041d800ce4216a5aa51c6b92d35f823b6c6484331796bd - languageName: node - linkType: hard - "@tokenizer/token@npm:^0.3.0": version: 0.3.0 resolution: "@tokenizer/token@npm:0.3.0" @@ -13910,6 +14270,26 @@ __metadata: languageName: node linkType: hard +"@types/eslint-scope@npm:^3.7.7": + version: 3.7.7 + resolution: "@types/eslint-scope@npm:3.7.7" + dependencies: + "@types/eslint": "npm:*" + "@types/estree": "npm:*" + checksum: 10c0/a0ecbdf2f03912679440550817ff77ef39a30fa8bfdacaf6372b88b1f931828aec392f52283240f0d648cf3055c5ddc564544a626bcf245f3d09fcb099ebe3cc + languageName: node + linkType: hard + +"@types/eslint@npm:*": + version: 9.6.1 + resolution: "@types/eslint@npm:9.6.1" + dependencies: + "@types/estree": "npm:*" + "@types/json-schema": "npm:*" + checksum: 10c0/69ba24fee600d1e4c5abe0df086c1a4d798abf13792d8cfab912d76817fe1a894359a1518557d21237fbaf6eda93c5ab9309143dee4c59ef54336d1b3570420e + languageName: node + linkType: hard + "@types/eslint@npm:^8.56.10": version: 8.56.12 resolution: "@types/eslint@npm:8.56.12" @@ -13920,7 +14300,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:1.0.8, @types/estree@npm:^1.0.0": +"@types/estree@npm:*, @types/estree@npm:1.0.8, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.8": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 @@ -14538,7 +14918,7 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:*, @types/ws@npm:^8.0.0, @types/ws@npm:^8.5.10": +"@types/ws@npm:*, @types/ws@npm:^8.5.10": version: 8.18.1 resolution: "@types/ws@npm:8.18.1" dependencies: @@ -14930,70 +15310,180 @@ __metadata: version: 1.11.1 resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.11.1" dependencies: - "@napi-rs/wasm-runtime": "npm:^0.2.11" - conditions: cpu=wasm32 + "@napi-rs/wasm-runtime": "npm:^0.2.11" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/ast@npm:1.14.1" + dependencies: + "@webassemblyjs/helper-numbers": "npm:1.13.2" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + checksum: 10c0/67a59be8ed50ddd33fbb2e09daa5193ac215bf7f40a9371be9a0d9797a114d0d1196316d2f3943efdb923a3d809175e1563a3cb80c814fb8edccd1e77494972b + languageName: node + linkType: hard + +"@webassemblyjs/floating-point-hex-parser@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.13.2" + checksum: 10c0/0e88bdb8b50507d9938be64df0867f00396b55eba9df7d3546eb5dc0ca64d62e06f8d881ec4a6153f2127d0f4c11d102b6e7d17aec2f26bb5ff95a5e60652412 + languageName: node + linkType: hard + +"@webassemblyjs/helper-api-error@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-api-error@npm:1.13.2" + checksum: 10c0/31be497f996ed30aae4c08cac3cce50c8dcd5b29660383c0155fce1753804fc55d47fcba74e10141c7dd2899033164e117b3bcfcda23a6b043e4ded4f1003dfb + languageName: node + linkType: hard + +"@webassemblyjs/helper-buffer@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/helper-buffer@npm:1.14.1" + checksum: 10c0/0d54105dc373c0fe6287f1091e41e3a02e36cdc05e8cf8533cdc16c59ff05a646355415893449d3768cda588af451c274f13263300a251dc11a575bc4c9bd210 + languageName: node + linkType: hard + +"@webassemblyjs/helper-numbers@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-numbers@npm:1.13.2" + dependencies: + "@webassemblyjs/floating-point-hex-parser": "npm:1.13.2" + "@webassemblyjs/helper-api-error": "npm:1.13.2" + "@xtuc/long": "npm:4.2.2" + checksum: 10c0/9c46852f31b234a8fb5a5a9d3f027bc542392a0d4de32f1a9c0075d5e8684aa073cb5929b56df565500b3f9cc0a2ab983b650314295b9bf208d1a1651bfc825a + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-bytecode@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.13.2" + checksum: 10c0/c4355d14f369b30cf3cbdd3acfafc7d0488e086be6d578e3c9780bd1b512932352246be96e034e2a7fcfba4f540ec813352f312bfcbbfe5bcfbf694f82ccc682 + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-section@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + checksum: 10c0/1f9b33731c3c6dbac3a9c483269562fa00d1b6a4e7133217f40e83e975e636fd0f8736e53abd9a47b06b66082ecc976c7384391ab0a68e12d509ea4e4b948d64 + languageName: node + linkType: hard + +"@webassemblyjs/ieee754@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/ieee754@npm:1.13.2" + dependencies: + "@xtuc/ieee754": "npm:^1.2.0" + checksum: 10c0/2e732ca78c6fbae3c9b112f4915d85caecdab285c0b337954b180460290ccd0fb00d2b1dc4bb69df3504abead5191e0d28d0d17dfd6c9d2f30acac8c4961c8a7 languageName: node linkType: hard -"@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1" - conditions: os=win32 & cpu=arm64 +"@webassemblyjs/leb128@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/leb128@npm:1.13.2" + dependencies: + "@xtuc/long": "npm:4.2.2" + checksum: 10c0/dad5ef9e383c8ab523ce432dfd80098384bf01c45f70eb179d594f85ce5db2f80fa8c9cba03adafd85684e6d6310f0d3969a882538975989919329ac4c984659 languageName: node linkType: hard -"@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1" - conditions: os=win32 & cpu=ia32 +"@webassemblyjs/utf8@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/utf8@npm:1.13.2" + checksum: 10c0/d3fac9130b0e3e5a1a7f2886124a278e9323827c87a2b971e6d0da22a2ba1278ac9f66a4f2e363ecd9fac8da42e6941b22df061a119e5c0335f81006de9ee799 languageName: node linkType: hard -"@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1" - conditions: os=win32 & cpu=x64 +"@webassemblyjs/wasm-edit@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-edit@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/helper-wasm-section": "npm:1.14.1" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + "@webassemblyjs/wasm-opt": "npm:1.14.1" + "@webassemblyjs/wasm-parser": "npm:1.14.1" + "@webassemblyjs/wast-printer": "npm:1.14.1" + checksum: 10c0/5ac4781086a2ca4b320bdbfd965a209655fe8a208ca38d89197148f8597e587c9a2c94fb6bd6f1a7dbd4527c49c6844fcdc2af981f8d793a97bf63a016aa86d2 languageName: node linkType: hard -"@whatwg-node/disposablestack@npm:^0.0.6": - version: 0.0.6 - resolution: "@whatwg-node/disposablestack@npm:0.0.6" +"@webassemblyjs/wasm-gen@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-gen@npm:1.14.1" dependencies: - "@whatwg-node/promise-helpers": "npm:^1.0.0" - tslib: "npm:^2.6.3" - checksum: 10c0/e751da9f8552728f28a140fd78c1da88be167ee8a5688371da88e024a2bf151298d194a61c9750b44bbbb4cf5c687959d495d41b1388e4cfcfe9dbe3584c79b3 + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/ieee754": "npm:1.13.2" + "@webassemblyjs/leb128": "npm:1.13.2" + "@webassemblyjs/utf8": "npm:1.13.2" + checksum: 10c0/d678810d7f3f8fecb2e2bdadfb9afad2ec1d2bc79f59e4711ab49c81cec578371e22732d4966f59067abe5fba8e9c54923b57060a729d28d408e608beef67b10 languageName: node linkType: hard -"@whatwg-node/fetch@npm:^0.10.0, @whatwg-node/fetch@npm:^0.10.4": - version: 0.10.13 - resolution: "@whatwg-node/fetch@npm:0.10.13" +"@webassemblyjs/wasm-opt@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-opt@npm:1.14.1" dependencies: - "@whatwg-node/node-fetch": "npm:^0.8.3" - urlpattern-polyfill: "npm:^10.0.0" - checksum: 10c0/afce42c44e9c5572ac5800615bac3a03865923af53af99098d2e931b40f6db556ad5d4a3e08c29e51ecf871809f0860fb11f2b024891daa26646a309f8b07fc1 + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + "@webassemblyjs/wasm-parser": "npm:1.14.1" + checksum: 10c0/515bfb15277ee99ba6b11d2232ddbf22aed32aad6d0956fe8a0a0a004a1b5a3a277a71d9a3a38365d0538ac40d1b7b7243b1a244ad6cd6dece1c1bb2eb5de7ee languageName: node linkType: hard -"@whatwg-node/node-fetch@npm:^0.8.3": - version: 0.8.5 - resolution: "@whatwg-node/node-fetch@npm:0.8.5" +"@webassemblyjs/wasm-parser@npm:1.14.1, @webassemblyjs/wasm-parser@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-parser@npm:1.14.1" dependencies: - "@fastify/busboy": "npm:^3.1.1" - "@whatwg-node/disposablestack": "npm:^0.0.6" - "@whatwg-node/promise-helpers": "npm:^1.3.2" - tslib: "npm:^2.6.3" - checksum: 10c0/9f0d944476cc40f5cfed79057cff269ddacf52bd4dda36017fe922cbf3e0a98850f26cb9c4e7990e87e6097f2f9dd94a20c6cc11f95d57652a516e99a5ccafc2 + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-api-error": "npm:1.13.2" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/ieee754": "npm:1.13.2" + "@webassemblyjs/leb128": "npm:1.13.2" + "@webassemblyjs/utf8": "npm:1.13.2" + checksum: 10c0/95427b9e5addbd0f647939bd28e3e06b8deefdbdadcf892385b5edc70091bf9b92fa5faac3fce8333554437c5d85835afef8c8a7d9d27ab6ba01ffab954db8c6 languageName: node linkType: hard -"@whatwg-node/promise-helpers@npm:^1.0.0, @whatwg-node/promise-helpers@npm:^1.2.1, @whatwg-node/promise-helpers@npm:^1.2.4, @whatwg-node/promise-helpers@npm:^1.3.0, @whatwg-node/promise-helpers@npm:^1.3.2": - version: 1.3.2 - resolution: "@whatwg-node/promise-helpers@npm:1.3.2" +"@webassemblyjs/wast-printer@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wast-printer@npm:1.14.1" dependencies: - tslib: "npm:^2.6.3" - checksum: 10c0/d20e8d740cfa1f0eac7dce11e8a7a84f1567513a8ff0bd1772724b581a8ca77df3f9600a95047c0d2628335626113fa98367517abd01c1ff49817fccf225a29a + "@webassemblyjs/ast": "npm:1.14.1" + "@xtuc/long": "npm:4.2.2" + checksum: 10c0/8d7768608996a052545251e896eac079c98e0401842af8dd4de78fba8d90bd505efb6c537e909cd6dae96e09db3fa2e765a6f26492553a675da56e2db51f9d24 languageName: node linkType: hard @@ -15020,6 +15510,13 @@ __metadata: languageName: node linkType: hard +"@xterm/addon-attach@npm:^0.12.0": + version: 0.12.0 + resolution: "@xterm/addon-attach@npm:0.12.0" + checksum: 10c0/afc8543d20172d18ac4777d70ed62d273e52033fbb859adac19f66b7f7658f4c0cecf400f46cb7befd4db12a75868f727a3ae0d492d8191ea7689db2f554ea67 + languageName: node + linkType: hard + "@xterm/addon-fit@npm:^0.10.0": version: 0.10.0 resolution: "@xterm/addon-fit@npm:0.10.0" @@ -15029,6 +15526,13 @@ __metadata: languageName: node linkType: hard +"@xterm/addon-fit@npm:^0.11.0": + version: 0.11.0 + resolution: "@xterm/addon-fit@npm:0.11.0" + checksum: 10c0/c0ce94f7e877d07e7197d15d041cc629ce2e853016400aadddc9810838ea8a26e836f87f325eba36df88c03feb72d9f13cd8c3bb41ee31e701f1058edb8553c3 + languageName: node + linkType: hard + "@xterm/xterm@npm:^5.5.0": version: 5.5.0 resolution: "@xterm/xterm@npm:5.5.0" @@ -15036,6 +15540,20 @@ __metadata: languageName: node linkType: hard +"@xtuc/ieee754@npm:^1.2.0": + version: 1.2.0 + resolution: "@xtuc/ieee754@npm:1.2.0" + checksum: 10c0/a8565d29d135039bd99ae4b2220d3e167d22cf53f867e491ed479b3f84f895742d0097f935b19aab90265a23d5d46711e4204f14c479ae3637fbf06c4666882f + languageName: node + linkType: hard + +"@xtuc/long@npm:4.2.2": + version: 4.2.2 + resolution: "@xtuc/long@npm:4.2.2" + checksum: 10c0/8582cbc69c79ad2d31568c412129bf23d2b1210a1dfb60c82d5a1df93334da4ee51f3057051658569e2c196d8dc33bc05ae6b974a711d0d16e801e1d0647ccd1 + languageName: node + linkType: hard + "@yarnpkg/lockfile@npm:^1.1.0": version: 1.1.0 resolution: "@yarnpkg/lockfile@npm:1.1.0" @@ -15083,7 +15601,7 @@ __metadata: languageName: node linkType: hard -"accepts@npm:^1.3.5, accepts@npm:~1.3.8": +"accepts@npm:^1.3.8, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" dependencies: @@ -15093,6 +15611,15 @@ __metadata: languageName: node linkType: hard +"acorn-import-phases@npm:^1.0.3": + version: 1.0.4 + resolution: "acorn-import-phases@npm:1.0.4" + peerDependencies: + acorn: ^8.14.0 + checksum: 10c0/338eb46fc1aed5544f628344cb9af189450b401d152ceadbf1f5746901a5d923016cd0e7740d5606062d374fdf6941c29bb515d2bd133c4f4242d5d4cd73a3c7 + languageName: node + linkType: hard + "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -15120,6 +15647,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.16.0": + version: 8.16.0 + resolution: "acorn@npm:8.16.0" + bin: + acorn: bin/acorn + checksum: 10c0/c9c52697227661b68d0debaf972222d4f622aa06b185824164e153438afa7b08273432ca43ea792cadb24dada1d46f6f6bb1ef8de9956979288cc1b96bf9914e + languageName: node + linkType: hard + "address@npm:^1.0.1, address@npm:^1.1.2": version: 1.2.2 resolution: "address@npm:1.2.2" @@ -15285,6 +15821,13 @@ __metadata: languageName: node linkType: hard +"anser@npm:^2.1.1": + version: 2.3.5 + resolution: "anser@npm:2.3.5" + checksum: 10c0/c8ab6eb8241f675a9a8c0e2f5ade9f8ead55ddb6e4f3587214ceac662012d89ab1a6e5d7daa0010941a5d8d9bda2c7ea958cd6fe6e52db0fb612f7fcd6c4ce3f + languageName: node + linkType: hard + "ansi-colors@npm:^4.1.3": version: 4.1.3 resolution: "ansi-colors@npm:4.1.3" @@ -15398,48 +15941,53 @@ __metadata: resolution: "app@workspace:packages/app" dependencies: "@backstage-community/plugin-tekton": "npm:^3.34.1" - "@backstage/app-defaults": "npm:^1.7.3" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/cli": "npm:^0.35.0" - "@backstage/core-app-api": "npm:^1.19.3" - "@backstage/core-components": "npm:^0.18.4" - "@backstage/core-plugin-api": "npm:^1.12.1" - "@backstage/integration-react": "npm:^1.2.13" - "@backstage/plugin-api-docs": "npm:^0.13.2" - "@backstage/plugin-catalog": "npm:^1.32.1" - "@backstage/plugin-catalog-common": "npm:^1.1.7" - "@backstage/plugin-catalog-graph": "npm:^0.5.4" - "@backstage/plugin-catalog-import": "npm:^0.13.8" - "@backstage/plugin-catalog-react": "npm:^1.21.4" - "@backstage/plugin-kubernetes": "npm:^0.12.14" - "@backstage/plugin-notifications": "npm:^0.5.12" - "@backstage/plugin-org": "npm:^0.6.47" - "@backstage/plugin-permission-react": "npm:^0.4.39" - "@backstage/plugin-scaffolder": "npm:^1.35.0" - "@backstage/plugin-search": "npm:^1.5.1" - "@backstage/plugin-search-react": "npm:^1.10.1" - "@backstage/plugin-signals": "npm:^0.0.26" - "@backstage/plugin-techdocs": "npm:^1.16.1" - "@backstage/plugin-techdocs-module-addons-contrib": "npm:^1.1.31" - "@backstage/plugin-techdocs-react": "npm:^1.3.6" - "@backstage/plugin-user-settings": "npm:^0.8.30" - "@backstage/test-utils": "npm:^1.7.14" - "@backstage/theme": "npm:^0.7.1" - "@backstage/ui": "npm:^0.10.0" + "@backstage/app-defaults": "npm:^1.7.6" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/cli": "npm:^0.36.0" + "@backstage/core-app-api": "npm:^1.19.6" + "@backstage/core-components": "npm:^0.18.8" + "@backstage/core-plugin-api": "npm:^1.12.4" + "@backstage/integration-react": "npm:^1.2.16" + "@backstage/plugin-api-docs": "npm:^0.13.5" + "@backstage/plugin-catalog": "npm:^2.0.1" + "@backstage/plugin-catalog-common": "npm:^1.1.8" + "@backstage/plugin-catalog-graph": "npm:^0.6.0" + "@backstage/plugin-catalog-import": "npm:^0.13.11" + "@backstage/plugin-catalog-react": "npm:^2.1.0" + "@backstage/plugin-kubernetes": "npm:^0.12.17" + "@backstage/plugin-notifications": "npm:^0.5.15" + "@backstage/plugin-org": "npm:^0.7.0" + "@backstage/plugin-permission-react": "npm:^0.4.41" + "@backstage/plugin-scaffolder": "npm:^1.36.1" + "@backstage/plugin-scaffolder-react": "npm:^1.20.0" + "@backstage/plugin-search": "npm:^1.7.0" + "@backstage/plugin-search-react": "npm:^1.11.0" + "@backstage/plugin-signals": "npm:^0.0.29" + "@backstage/plugin-techdocs": "npm:^1.17.2" + "@backstage/plugin-techdocs-module-addons-contrib": "npm:^1.1.34" + "@backstage/plugin-techdocs-react": "npm:^1.3.9" + "@backstage/plugin-user-settings": "npm:^0.9.1" + "@backstage/test-utils": "npm:^1.7.16" + "@backstage/theme": "npm:^0.7.2" + "@backstage/ui": "npm:^0.13.1" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@playwright/test": "npm:^1.32.3" "@roadiehq/backstage-plugin-argo-cd": "npm:^2.12.2" - "@testing-library/dom": "npm:^9.0.0" + "@testing-library/dom": "npm:^10.0.0" "@testing-library/jest-dom": "npm:^6.0.0" - "@testing-library/react": "npm:^14.0.0" + "@testing-library/react": "npm:^16.0.0" "@testing-library/user-event": "npm:^14.0.0" + "@types/react": "npm:*" "@types/react-dom": "npm:*" cross-env: "npm:^7.0.0" + jest: "npm:^30.2.0" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-router: "npm:^6.3.0" react-router-dom: "npm:^6.3.0" + react-use: "npm:^17.6.0" + webpack: "npm:~5.103.0" languageName: unknown linkType: soft @@ -15524,12 +16072,12 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:5.1.3": - version: 5.1.3 - resolution: "aria-query@npm:5.1.3" +"aria-query@npm:5.3.0": + version: 5.3.0 + resolution: "aria-query@npm:5.3.0" dependencies: - deep-equal: "npm:^2.0.5" - checksum: 10c0/edcbc8044c4663d6f88f785e983e6784f98cb62b4ba1e9dd8d61b725d0203e4cfca38d676aee984c31f354103461102a3d583aa4fbe4fd0a89b679744f4e5faf + dequal: "npm:^2.0.3" + checksum: 10c0/2bff0d4eba5852a9dd578ecf47eaef0e82cc52569b48469b0aac2db5145db0b17b7a58d9e01237706d1e14b7a1b0ac9b78e9c97027ad97679dd8f91b85da1469 languageName: node linkType: hard @@ -15540,7 +16088,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": +"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" dependencies: @@ -15702,6 +16250,17 @@ __metadata: languageName: node linkType: hard +"asn1js@npm:^3.0.6": + version: 3.0.7 + resolution: "asn1js@npm:3.0.7" + dependencies: + pvtsutils: "npm:^1.3.6" + pvutils: "npm:^1.1.3" + tslib: "npm:^2.8.1" + checksum: 10c0/7e79795edf1bcc86532c4084aa7c8c0ebc57f7dd6f964ad6de956abf617329722f6964b7af3a5d1c4554dd61b4b148ae1580e63e3ec2e70e7fba34f6df072b29 + languageName: node + linkType: hard + "assert@npm:^2.0.0": version: 2.1.0 resolution: "assert@npm:2.1.0" @@ -15877,7 +16436,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.0.0, axios@npm:^1.12.2, axios@npm:^1.7.4": +"axios@npm:^1.0.0, axios@npm:^1.12.2": version: 1.13.4 resolution: "axios@npm:1.13.4" dependencies: @@ -15888,6 +16447,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.12.0": + version: 1.13.6 + resolution: "axios@npm:1.13.6" + dependencies: + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/51fb5af055c3b85662fa97df17d986ae2c37d13bf86d50b6bb36b6b3a2dec6966a1d3a14ab3774b71707b155ae3597ed9b7babdf1a1a863d1a31840cb8e7ec71 + languageName: node + linkType: hard + "axobject-query@npm:^4.1.0": version: 4.1.0 resolution: "axobject-query@npm:4.1.0" @@ -15895,16 +16465,6 @@ __metadata: languageName: node linkType: hard -"azure-devops-node-api@npm:^14.0.0": - version: 14.1.0 - resolution: "azure-devops-node-api@npm:14.1.0" - dependencies: - tunnel: "npm:0.0.6" - typed-rest-client: "npm:2.1.0" - checksum: 10c0/7cfb4d9e5359e568dbcaaa5f6e0e1518994802ef840594d515652981ead99e6eeeeb05846b38fb264f05c24f06db360139f7eca0a614c69903b5fb8875243d16 - languageName: node - linkType: hard - "b4a@npm:^1.6.4": version: 1.7.3 resolution: "b4a@npm:1.7.3" @@ -16029,43 +16589,47 @@ __metadata: version: 0.0.0-use.local resolution: "backend@workspace:packages/backend" dependencies: - "@backstage/backend-defaults": "npm:^0.14.0" - "@backstage/backend-plugin-api": "npm:^1.6.1" - "@backstage/catalog-model": "npm:^1.7.6" - "@backstage/cli": "npm:^0.35.0" + "@backstage/backend-defaults": "npm:^0.16.0" + "@backstage/backend-plugin-api": "npm:^1.8.0" + "@backstage/catalog-model": "npm:^1.7.7" + "@backstage/cli": "npm:^0.36.0" "@backstage/config": "npm:^1.3.6" - "@backstage/plugin-app-backend": "npm:^0.5.9" - "@backstage/plugin-auth-backend": "npm:^0.25.7" - "@backstage/plugin-auth-backend-module-github-provider": "npm:^0.4.0" - "@backstage/plugin-auth-backend-module-guest-provider": "npm:^0.2.15" - "@backstage/plugin-auth-node": "npm:^0.6.10" - "@backstage/plugin-catalog-backend": "npm:^3.3.0" - "@backstage/plugin-catalog-backend-module-github": "npm:^0.12.1" - "@backstage/plugin-catalog-backend-module-github-org": "npm:^0.3.18" - "@backstage/plugin-catalog-backend-module-logs": "npm:^0.1.17" - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.2.15" - "@backstage/plugin-kubernetes-backend": "npm:^0.21.0" - "@backstage/plugin-notifications-backend": "npm:^0.6.1" - "@backstage/plugin-permission-backend": "npm:^0.7.7" - "@backstage/plugin-permission-backend-module-allow-all-policy": "npm:^0.2.15" - "@backstage/plugin-permission-common": "npm:^0.9.3" - "@backstage/plugin-permission-node": "npm:^0.10.7" - "@backstage/plugin-proxy-backend": "npm:^0.6.9" - "@backstage/plugin-scaffolder-backend": "npm:^3.1.0" - "@backstage/plugin-scaffolder-backend-module-github": "npm:^0.9.3" - "@backstage/plugin-scaffolder-backend-module-notifications": "npm:^0.1.17" - "@backstage/plugin-search-backend": "npm:^2.0.9" - "@backstage/plugin-search-backend-module-catalog": "npm:^0.3.11" - "@backstage/plugin-search-backend-module-pg": "npm:^0.5.51" - "@backstage/plugin-search-backend-module-techdocs": "npm:^0.4.9" - "@backstage/plugin-search-backend-node": "npm:^1.4.0" - "@backstage/plugin-signals-backend": "npm:^0.3.11" - "@backstage/plugin-techdocs-backend": "npm:^2.1.3" + "@backstage/errors": "npm:^1.2.7" + "@backstage/plugin-app-backend": "npm:^0.5.12" + "@backstage/plugin-auth-backend": "npm:^0.27.2" + "@backstage/plugin-auth-backend-module-github-provider": "npm:^0.5.1" + "@backstage/plugin-auth-backend-module-guest-provider": "npm:^0.2.17" + "@backstage/plugin-auth-node": "npm:^0.6.14" + "@backstage/plugin-catalog-backend": "npm:^3.5.0" + "@backstage/plugin-catalog-backend-module-github": "npm:^0.13.0" + "@backstage/plugin-catalog-backend-module-github-org": "npm:^0.3.20" + "@backstage/plugin-catalog-backend-module-logs": "npm:^0.1.20" + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.2.18" + "@backstage/plugin-kubernetes-backend": "npm:^0.21.2" + "@backstage/plugin-notifications-backend": "npm:^0.6.3" + "@backstage/plugin-permission-backend": "npm:^0.7.10" + "@backstage/plugin-permission-backend-module-allow-all-policy": "npm:^0.2.17" + "@backstage/plugin-permission-common": "npm:^0.9.7" + "@backstage/plugin-permission-node": "npm:^0.10.11" + "@backstage/plugin-proxy-backend": "npm:^0.6.11" + "@backstage/plugin-scaffolder-backend": "npm:^3.2.0" + "@backstage/plugin-scaffolder-backend-module-github": "npm:^0.9.7" + "@backstage/plugin-scaffolder-backend-module-notifications": "npm:^0.1.20" + "@backstage/plugin-scaffolder-node": "npm:^0.13.0" + "@backstage/plugin-search-backend": "npm:^2.1.0" + "@backstage/plugin-search-backend-module-catalog": "npm:^0.3.13" + "@backstage/plugin-search-backend-module-pg": "npm:^0.5.53" + "@backstage/plugin-search-backend-module-techdocs": "npm:^0.4.12" + "@backstage/plugin-search-backend-node": "npm:^1.4.2" + "@backstage/plugin-signals-backend": "npm:^0.3.13" + "@backstage/plugin-techdocs-backend": "npm:^2.1.6" app: "link:../app" better-sqlite3: "npm:^12.0.0" dotenv: "npm:^17.2.3" + jest: "npm:^30.2.0" node-gyp: "npm:^10.0.0" pg: "npm:^8.11.3" + webpack: "npm:~5.103.0" languageName: unknown linkType: soft @@ -16090,6 +16654,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b + languageName: node + linkType: hard + "bare-events@npm:^2.5.4, bare-events@npm:^2.7.0": version: 2.8.2 resolution: "bare-events@npm:2.8.2" @@ -16222,7 +16793,7 @@ __metadata: languageName: node linkType: hard -"before-after-hook@npm:^2.1.0, before-after-hook@npm:^2.2.0": +"before-after-hook@npm:^2.2.0": version: 2.2.3 resolution: "before-after-hook@npm:2.2.3" checksum: 10c0/0488c4ae12df758ca9d49b3bb27b47fd559677965c52cae7b335784724fb8bf96c42b6e5ba7d7afcbc31facb0e294c3ef717cc41c5bc2f7bd9e76f8b90acd31c @@ -16240,16 +16811,14 @@ __metadata: languageName: node linkType: hard -"bfj@npm:^8.0.0": - version: 8.0.0 - resolution: "bfj@npm:8.0.0" +"bfj@npm:^9.0.2": + version: 9.1.3 + resolution: "bfj@npm:9.1.3" dependencies: - bluebird: "npm:^3.7.2" check-types: "npm:^11.2.3" hoopy: "npm:^0.1.4" - jsonpath: "npm:^1.1.1" tryer: "npm:^1.0.1" - checksum: 10c0/380b702a8f58fa6690f7e2a3fa7befdd3d550cc6eaf75f626bd4e1bc9c1870deabcd3381b971519268a49083878d1f9e9e4aca871afe40f97e22a3482c9f39c7 + checksum: 10c0/04ce0002e394922999ba41e372e5ddf77cf080225939149d0c88f5cee9abffe5cc676bab121ec9b19eb5d1e16fe63d97c1dc90e2992666899643e82fc27be9c0 languageName: node linkType: hard @@ -16299,19 +16868,6 @@ __metadata: languageName: node linkType: hard -"bitbucket@npm:^2.12.0": - version: 2.12.0 - resolution: "bitbucket@npm:2.12.0" - dependencies: - before-after-hook: "npm:^2.1.0" - deepmerge: "npm:^4.2.2" - is-plain-object: "npm:^3.0.0" - node-fetch: "npm:^2.6.0" - url-template: "npm:^2.0.8" - checksum: 10c0/ade6ede53a3e5aa2b24d625456e3b5deda9ffd49d75bc392c5fce50881d2ae98209a69b2af6ffc2e5f1d74b204321a31e7818b9ecfdf5240ef42b18ab70e3bb3 - languageName: node - linkType: hard - "bl@npm:^4.0.3, bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -16421,6 +16977,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.2": + version: 5.0.4 + resolution: "brace-expansion@npm:5.0.4" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/359cbcfa80b2eb914ca1f3440e92313fbfe7919ee6b274c35db55bec555aded69dac5ee78f102cec90c35f98c20fa43d10936d0cd9978158823c249257e1643a + languageName: node + linkType: hard + "braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -16527,7 +17092,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.18.1, browserslist@npm:^4.21.4, browserslist@npm:^4.24.0": +"browserslist@npm:^4.0.0, browserslist@npm:^4.18.1, browserslist@npm:^4.21.4, browserslist@npm:^4.24.0, browserslist@npm:^4.26.3, browserslist@npm:^4.28.1": version: 4.28.1 resolution: "browserslist@npm:4.28.1" dependencies: @@ -16558,7 +17123,7 @@ __metadata: languageName: node linkType: hard -"btoa@npm:^1.2.1": +"btoa@npm:1.2.1, btoa@npm:^1.2.1": version: 1.2.1 resolution: "btoa@npm:1.2.1" bin: @@ -16680,6 +17245,13 @@ __metadata: languageName: node linkType: hard +"bytestreamjs@npm:^2.0.1": + version: 2.0.1 + resolution: "bytestreamjs@npm:2.0.1" + checksum: 10c0/edd66b7ca3f94aae99a1009304a42d82ca4c2085eb934192ff47a81f59215c975dc9d3cd8f23c40a2f43ef5b2fa6f01ace70b10ad247766cec6ec641b89eab48 + languageName: node + linkType: hard + "cacache@npm:^18.0.0": version: 18.0.4 resolution: "cacache@npm:18.0.4" @@ -16719,16 +17291,6 @@ __metadata: languageName: node linkType: hard -"cache-content-type@npm:^1.0.0": - version: 1.0.1 - resolution: "cache-content-type@npm:1.0.1" - dependencies: - mime-types: "npm:^2.1.18" - ylru: "npm:^1.2.0" - checksum: 10c0/59b50e29e64a24bb52a16e5d35b69ad27ef14313701acc5e462b0aeebf2f09ff87fb6538eb0c0f0de4de05c8a1eecaef47f455f5b4928079e68f607f816a0843 - languageName: node - linkType: hard - "cacheable-lookup@npm:^6.0.0": version: 6.1.0 resolution: "cacheable-lookup@npm:6.1.0" @@ -16746,7 +17308,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": version: 1.0.8 resolution: "call-bind@npm:1.0.8" dependencies: @@ -16952,6 +17514,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.1": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad + languageName: node + linkType: hard + "chownr@npm:^1.1.1": version: 1.1.4 resolution: "chownr@npm:1.1.4" @@ -16973,6 +17544,13 @@ __metadata: languageName: node linkType: hard +"chrome-trace-event@npm:^1.0.2": + version: 1.0.4 + resolution: "chrome-trace-event@npm:1.0.4" + checksum: 10c0/3058da7a5f4934b87cf6a90ef5fb68ebc5f7d06f143ed5a4650208e5d7acae47bc03ec844b29fbf5ba7e46e8daa6acecc878f7983a4f4bb7271593da91e61ff5 + languageName: node + linkType: hard + "ci-info@npm:^3.2.0": version: 3.9.0 resolution: "ci-info@npm:3.9.0" @@ -17044,6 +17622,16 @@ __metadata: languageName: node linkType: hard +"cleye@npm:^2.3.0": + version: 2.3.0 + resolution: "cleye@npm:2.3.0" + dependencies: + terminal-columns: "npm:^2.0.0" + type-flag: "npm:^4.1.0" + checksum: 10c0/8a652d7c11f07c289aa84cd919225b16279041cdf2a3d3c5a96f62dbf78ed52dfd9d8820045e7ef72c0d1628c938ce578afb1411eb3d9e674c0293342f271925 + languageName: node + linkType: hard + "cli-cursor@npm:^3.1.0": version: 3.1.0 resolution: "cli-cursor@npm:3.1.0" @@ -17145,7 +17733,7 @@ __metadata: languageName: node linkType: hard -"codemirror-graphql@npm:^2.0.11, codemirror-graphql@npm:^2.0.13": +"codemirror-graphql@npm:^2.2.1": version: 2.2.4 resolution: "codemirror-graphql@npm:2.2.4" dependencies: @@ -17311,6 +17899,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:11.1.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: 10c0/13cc6ac875e48780250f723fb81c1c1178d35c5decb1abb1b628b3177af08a8554e76b2c0f29de72d69eef7c864d12613272a71fabef8047922bc622ab75a179 + languageName: node + linkType: hard + "commander@npm:7, commander@npm:^7.2.0": version: 7.2.0 resolution: "commander@npm:7.2.0" @@ -17332,6 +17927,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^14.0.3": + version: 14.0.3 + resolution: "commander@npm:14.0.3" + checksum: 10c0/755652564bbf56ff2ff083313912b326450d3f8d8c85f4b71416539c9a05c3c67dbd206821ca72635bf6b160e2afdefcb458e86b317827d5cb333b69ce7f1a24 + languageName: node + linkType: hard + "commander@npm:^2.19.0, commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -17505,17 +18107,6 @@ __metadata: languageName: node linkType: hard -"constant-case@npm:^3.0.4": - version: 3.0.4 - resolution: "constant-case@npm:3.0.4" - dependencies: - no-case: "npm:^3.0.4" - tslib: "npm:^2.0.3" - upper-case: "npm:^2.0.2" - checksum: 10c0/91d54f18341fcc491ae66d1086642b0cc564be3e08984d7b7042f8b0a721c8115922f7f11d6a09f13ed96ff326eabae11f9d1eb0335fa9d8b6e39e4df096010e - languageName: node - linkType: hard - "constants-browserify@npm:^1.0.0": version: 1.0.0 resolution: "constants-browserify@npm:1.0.0" @@ -17523,7 +18114,7 @@ __metadata: languageName: node linkType: hard -"content-disposition@npm:~0.5.2, content-disposition@npm:~0.5.4": +"content-disposition@npm:~0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" dependencies: @@ -17532,7 +18123,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:^1.0.5, content-type@npm:~1.0.4, content-type@npm:~1.0.5": +"content-type@npm:^1.0.5, content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af @@ -17584,7 +18175,7 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.9.0": +"cookies@npm:~0.9.1": version: 0.9.1 resolution: "cookies@npm:0.9.1" dependencies: @@ -17603,6 +18194,13 @@ __metadata: languageName: node linkType: hard +"core-js-pure@npm:^3.23.3": + version: 3.49.0 + resolution: "core-js-pure@npm:3.49.0" + checksum: 10c0/b4580a57b917d0bf1029356b1a60abf0f9b99562b67bf39c01485d58891f23603459ed71bde1d7f75c0a9a346829d8c281b255c525fb119726341364c513e82e + languageName: node + linkType: hard + "core-js-pure@npm:^3.48.0": version: 3.48.0 resolution: "core-js-pure@npm:3.48.0" @@ -17674,7 +18272,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^8.1.0": +"cosmiconfig@npm:^8.2.0": version: 8.3.6 resolution: "cosmiconfig@npm:8.3.6" dependencies: @@ -17839,15 +18437,6 @@ __metadata: languageName: node linkType: hard -"cross-inspect@npm:1.0.1": - version: 1.0.1 - resolution: "cross-inspect@npm:1.0.1" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/2493ee47a801b46ede1c42ca6242b8d2059f7319b5baf23887bbaf46a6ea8e536d2e271d0990176c05092f67b32d084ffd8c93e7c1227eff4a06cceadb49af47 - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" @@ -18481,13 +19070,6 @@ __metadata: languageName: node linkType: hard -"data-uri-to-buffer@npm:^4.0.0": - version: 4.0.1 - resolution: "data-uri-to-buffer@npm:4.0.1" - checksum: 10c0/20a6b93107597530d71d4cb285acee17f66bcdfc03fd81040921a81252f19db27588d87fc8fc69e1950c55cfb0bf8ae40d0e5e21d907230813eb5d5a7f9eb45b - languageName: node - linkType: hard - "data-uri-to-buffer@npm:^6.0.2": version: 6.0.2 resolution: "data-uri-to-buffer@npm:6.0.2" @@ -18538,7 +19120,7 @@ __metadata: languageName: node linkType: hard -"dataloader@npm:^2.0.0, dataloader@npm:^2.2.3": +"dataloader@npm:^2.0.0": version: 2.2.3 resolution: "dataloader@npm:2.2.3" checksum: 10c0/9b9a056fbc863ca86da87d59e053e871e263b4966aa4d55e40d61a65e96815fae5530ca220629064ca5f8e3000c0c4ec93292e170c38ff393fb34256b4d7c1aa @@ -18591,7 +19173,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.4.3, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.4.0, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.4.0, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -18675,32 +19257,6 @@ __metadata: languageName: node linkType: hard -"deep-equal@npm:^2.0.5": - version: 2.2.3 - resolution: "deep-equal@npm:2.2.3" - dependencies: - array-buffer-byte-length: "npm:^1.0.0" - call-bind: "npm:^1.0.5" - es-get-iterator: "npm:^1.1.3" - get-intrinsic: "npm:^1.2.2" - is-arguments: "npm:^1.1.1" - is-array-buffer: "npm:^3.0.2" - is-date-object: "npm:^1.0.5" - is-regex: "npm:^1.1.4" - is-shared-array-buffer: "npm:^1.0.2" - isarray: "npm:^2.0.5" - object-is: "npm:^1.1.5" - object-keys: "npm:^1.1.1" - object.assign: "npm:^4.1.4" - regexp.prototype.flags: "npm:^1.5.1" - side-channel: "npm:^1.0.4" - which-boxed-primitive: "npm:^1.0.2" - which-collection: "npm:^1.0.1" - which-typed-array: "npm:^1.1.13" - checksum: 10c0/a48244f90fa989f63ff5ef0cc6de1e4916b48ea0220a9c89a378561960814794a5800c600254482a2c8fd2e49d6c2e196131dc983976adb024c94a42dfe4949f - languageName: node - linkType: hard - "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -18715,7 +19271,7 @@ __metadata: languageName: node linkType: hard -"deep-is@npm:^0.1.3, deep-is@npm:~0.1.3": +"deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c @@ -18841,7 +19397,7 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0, depd@npm:^2.0.0, depd@npm:~2.0.0": +"depd@npm:2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c @@ -18876,7 +19432,7 @@ __metadata: languageName: node linkType: hard -"des.js@npm:^1.0.0, des.js@npm:^1.1.0": +"des.js@npm:^1.0.0": version: 1.1.0 resolution: "des.js@npm:1.1.0" dependencies: @@ -18886,7 +19442,7 @@ __metadata: languageName: node linkType: hard -"destroy@npm:1.2.0, destroy@npm:^1.0.4, destroy@npm:~1.2.0": +"destroy@npm:1.2.0, destroy@npm:^1.0.4, destroy@npm:^1.2.0, destroy@npm:~1.2.0": version: 1.2.0 resolution: "destroy@npm:1.2.0" checksum: 10c0/bd7633942f57418f5a3b80d5cb53898127bcf53e24cdf5d5f4396be471417671f0fee48a4ebe9a1e9defbde2a31280011af58a57e090ff822f589b443ed4e643 @@ -19131,7 +19687,7 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:^3.2.4, dompurify@npm:^3.3.1": +"dompurify@npm:^3.3.1": version: 3.3.1 resolution: "dompurify@npm:3.3.1" dependencies: @@ -19143,6 +19699,18 @@ __metadata: languageName: node linkType: hard +"dompurify@npm:^3.3.2": + version: 3.3.3 + resolution: "dompurify@npm:3.3.3" + dependencies: + "@types/trusted-types": "npm:^2.0.7" + dependenciesMeta: + "@types/trusted-types": + optional: true + checksum: 10c0/097c14a21a3f6cb95beded9ecd255f7c3512c42767b048390c747b0fe35736f6a71e02320fc50a9ac2be645834b463e4760915d595d502a56452daf339d0ea9c + languageName: node + linkType: hard + "domutils@npm:^2.5.2, domutils@npm:^2.8.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" @@ -19178,13 +19746,6 @@ __metadata: languageName: node linkType: hard -"dset@npm:^3.1.2": - version: 3.1.4 - resolution: "dset@npm:3.1.4" - checksum: 10c0/b67bbd28dd8a539e90c15ffb61100eb64ef995c5270a124d4f99bbb53f4d82f55a051b731ba81f3215dd9dce2b4c8d69927dc20b3be1c5fc88bab159467aa438 - languageName: node - linkType: hard - "dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1" @@ -19316,20 +19877,20 @@ __metadata: languageName: node linkType: hard -"encodeurl@npm:^1.0.2, encodeurl@npm:~1.0.2": - version: 1.0.2 - resolution: "encodeurl@npm:1.0.2" - checksum: 10c0/f6c2387379a9e7c1156c1c3d4f9cb7bb11cf16dd4c1682e1f6746512564b053df5781029b6061296832b59fb22f459dbe250386d217c2f6e203601abb2ee0bec - languageName: node - linkType: hard - -"encodeurl@npm:~2.0.0": +"encodeurl@npm:^2.0.0, encodeurl@npm:~2.0.0": version: 2.0.0 resolution: "encodeurl@npm:2.0.0" checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb languageName: node linkType: hard +"encodeurl@npm:~1.0.2": + version: 1.0.2 + resolution: "encodeurl@npm:1.0.2" + checksum: 10c0/f6c2387379a9e7c1156c1c3d4f9cb7bb11cf16dd4c1682e1f6746512564b053df5781029b6061296832b59fb22f459dbe250386d217c2f6e203601abb2ee0bec + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -19348,6 +19909,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.17.3, enhanced-resolve@npm:^5.20.0": + version: 5.20.1 + resolution: "enhanced-resolve@npm:5.20.1" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.3.0" + checksum: 10c0/c6503ee1b2d725843e047e774445ecb12b779aa52db25d11ebe18d4b3adc148d3d993d2038b3d0c38ad836c9c4b3930fbc55df42f72b44785e2f94e5530eda69 + languageName: node + linkType: hard + "entities@npm:^2.0.0": version: 2.2.0 resolution: "entities@npm:2.2.0" @@ -19369,13 +19940,6 @@ __metadata: languageName: node linkType: hard -"entities@npm:~2.1.0": - version: 2.1.0 - resolution: "entities@npm:2.1.0" - checksum: 10c0/dd96ed95f7e017b7fbbcdd39bd6dc3dea6638f747c00610b53f23ea461ac409af87670f313805d85854bfce04f96e17d83575f75b3b2920365d78678ccd2a405 - languageName: node - linkType: hard - "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -19500,23 +20064,6 @@ __metadata: languageName: node linkType: hard -"es-get-iterator@npm:^1.1.3": - version: 1.1.3 - resolution: "es-get-iterator@npm:1.1.3" - dependencies: - call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.1.3" - has-symbols: "npm:^1.0.3" - is-arguments: "npm:^1.1.1" - is-map: "npm:^2.0.2" - is-set: "npm:^2.0.2" - is-string: "npm:^1.0.7" - isarray: "npm:^2.0.5" - stop-iteration-iterator: "npm:^1.0.0" - checksum: 10c0/ebd11effa79851ea75d7f079405f9d0dc185559fd65d986c6afea59a0ff2d46c2ed8675f19f03dce7429d7f6c14ff9aede8d121fbab78d75cfda6a263030bac0 - languageName: node - linkType: hard - "es-iterator-helpers@npm:^1.2.1": version: 1.2.2 resolution: "es-iterator-helpers@npm:1.2.2" @@ -19541,13 +20088,20 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^1.6.0": +"es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.6.0": version: 1.7.0 resolution: "es-module-lexer@npm:1.7.0" checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b languageName: node linkType: hard +"es-module-lexer@npm:^2.0.0": + version: 2.0.0 + resolution: "es-module-lexer@npm:2.0.0" + checksum: 10c0/ae78dbbd43035a4b972c46cfb6877e374ea290adfc62bc2f5a083fea242c0b2baaab25c5886af86be55f092f4a326741cb94334cd3c478c383fdc8a9ec5ff817 + languageName: node + linkType: hard + "es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": version: 1.1.1 resolution: "es-object-atoms@npm:1.1.1" @@ -19596,36 +20150,50 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.27.0": - version: 0.27.2 - resolution: "esbuild@npm:0.27.2" - dependencies: - "@esbuild/aix-ppc64": "npm:0.27.2" - "@esbuild/android-arm": "npm:0.27.2" - "@esbuild/android-arm64": "npm:0.27.2" - "@esbuild/android-x64": "npm:0.27.2" - "@esbuild/darwin-arm64": "npm:0.27.2" - "@esbuild/darwin-x64": "npm:0.27.2" - "@esbuild/freebsd-arm64": "npm:0.27.2" - "@esbuild/freebsd-x64": "npm:0.27.2" - "@esbuild/linux-arm": "npm:0.27.2" - "@esbuild/linux-arm64": "npm:0.27.2" - "@esbuild/linux-ia32": "npm:0.27.2" - "@esbuild/linux-loong64": "npm:0.27.2" - "@esbuild/linux-mips64el": "npm:0.27.2" - "@esbuild/linux-ppc64": "npm:0.27.2" - "@esbuild/linux-riscv64": "npm:0.27.2" - "@esbuild/linux-s390x": "npm:0.27.2" - "@esbuild/linux-x64": "npm:0.27.2" - "@esbuild/netbsd-arm64": "npm:0.27.2" - "@esbuild/netbsd-x64": "npm:0.27.2" - "@esbuild/openbsd-arm64": "npm:0.27.2" - "@esbuild/openbsd-x64": "npm:0.27.2" - "@esbuild/openharmony-arm64": "npm:0.27.2" - "@esbuild/sunos-x64": "npm:0.27.2" - "@esbuild/win32-arm64": "npm:0.27.2" - "@esbuild/win32-ia32": "npm:0.27.2" - "@esbuild/win32-x64": "npm:0.27.2" +"esbuild-loader@npm:^4.0.0": + version: 4.4.2 + resolution: "esbuild-loader@npm:4.4.2" + dependencies: + esbuild: "npm:^0.27.1" + get-tsconfig: "npm:^4.10.1" + loader-utils: "npm:^2.0.4" + webpack-sources: "npm:^1.4.3" + peerDependencies: + webpack: ^4.40.0 || ^5.0.0 + checksum: 10c0/f7fe5429b1972354894688005cf3446436751026f1b87b012e23e59323cff72332a5c8f4017890623562e8030cd92cd51c106eca4d92b2e71df2c67e7af0bb2e + languageName: node + linkType: hard + +"esbuild@npm:^0.27.1": + version: 0.27.4 + resolution: "esbuild@npm:0.27.4" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.4" + "@esbuild/android-arm": "npm:0.27.4" + "@esbuild/android-arm64": "npm:0.27.4" + "@esbuild/android-x64": "npm:0.27.4" + "@esbuild/darwin-arm64": "npm:0.27.4" + "@esbuild/darwin-x64": "npm:0.27.4" + "@esbuild/freebsd-arm64": "npm:0.27.4" + "@esbuild/freebsd-x64": "npm:0.27.4" + "@esbuild/linux-arm": "npm:0.27.4" + "@esbuild/linux-arm64": "npm:0.27.4" + "@esbuild/linux-ia32": "npm:0.27.4" + "@esbuild/linux-loong64": "npm:0.27.4" + "@esbuild/linux-mips64el": "npm:0.27.4" + "@esbuild/linux-ppc64": "npm:0.27.4" + "@esbuild/linux-riscv64": "npm:0.27.4" + "@esbuild/linux-s390x": "npm:0.27.4" + "@esbuild/linux-x64": "npm:0.27.4" + "@esbuild/netbsd-arm64": "npm:0.27.4" + "@esbuild/netbsd-x64": "npm:0.27.4" + "@esbuild/openbsd-arm64": "npm:0.27.4" + "@esbuild/openbsd-x64": "npm:0.27.4" + "@esbuild/openharmony-arm64": "npm:0.27.4" + "@esbuild/sunos-x64": "npm:0.27.4" + "@esbuild/win32-arm64": "npm:0.27.4" + "@esbuild/win32-ia32": "npm:0.27.4" + "@esbuild/win32-x64": "npm:0.27.4" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -19681,7 +20249,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10c0/cf83f626f55500f521d5fe7f4bc5871bec240d3deb2a01fbd379edc43b3664d1167428738a5aad8794b35d1cca985c44c375b1cd38a2ca613c77ced2c83aafcd + checksum: 10c0/2a1c2bcccda279f2afd72a7f8259860cb4483b32453d17878e1ecb4ac416b9e7c1001e7aa0a25ba4c29c1e250a3ceaae5d8bb72a119815bc8db4e9b5f5321490 languageName: node linkType: hard @@ -19727,25 +20295,6 @@ __metadata: languageName: node linkType: hard -"escodegen@npm:^1.8.1": - version: 1.14.3 - resolution: "escodegen@npm:1.14.3" - dependencies: - esprima: "npm:^4.0.1" - estraverse: "npm:^4.2.0" - esutils: "npm:^2.0.2" - optionator: "npm:^0.8.1" - source-map: "npm:~0.6.1" - dependenciesMeta: - source-map: - optional: true - bin: - escodegen: bin/escodegen.js - esgenerate: bin/esgenerate.js - checksum: 10c0/30d337803e8f44308c90267bf6192399e4b44792497c77a7506b68ab802ba6a48ebbe1ce77b219aba13dfd2de5f5e1c267e35be1ed87b2a9c3315e8b283e302a - languageName: node - linkType: hard - "escodegen@npm:^2.1.0": version: 2.1.0 resolution: "escodegen@npm:2.1.0" @@ -19963,6 +20512,16 @@ __metadata: languageName: node linkType: hard +"eslint-scope@npm:5.1.1": + version: 5.1.1 + resolution: "eslint-scope@npm:5.1.1" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^4.1.1" + checksum: 10c0/d30ef9dc1c1cbdece34db1539a4933fe3f9b14e1ffb27ecc85987902ee663ad7c9473bbd49a9a03195a373741e62e2f807c4938992e019b511993d163450e70a + languageName: node + linkType: hard + "eslint-scope@npm:^7.2.2": version: 7.2.2 resolution: "eslint-scope@npm:7.2.2" @@ -19987,6 +20546,22 @@ __metadata: languageName: node linkType: hard +"eslint-webpack-plugin@npm:^4.2.0": + version: 4.2.0 + resolution: "eslint-webpack-plugin@npm:4.2.0" + dependencies: + "@types/eslint": "npm:^8.56.10" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.5" + normalize-path: "npm:^3.0.0" + schema-utils: "npm:^4.2.0" + peerDependencies: + eslint: ^8.0.0 || ^9.0.0 + webpack: ^5.0.0 + checksum: 10c0/cf5c9b7afa3c025fffadb3e1451e7a55d914c3070614bb4d57f887774d164ca4298bb777f7c3afa16f47af9869174a19d6aebb4d1ca719bc2cc49f2eccd71a3b + languageName: node + linkType: hard + "eslint@npm:^8.6.0": version: 8.57.1 resolution: "eslint@npm:8.57.1" @@ -20053,16 +20628,6 @@ __metadata: languageName: node linkType: hard -"esprima@npm:1.2.2": - version: 1.2.2 - resolution: "esprima@npm:1.2.2" - bin: - esparse: ./bin/esparse.js - esvalidate: ./bin/esvalidate.js - checksum: 10c0/a5a8fd359651dd8228736d7352eb7636c7765e1ec6ff8fff3f6641622039a9f51fa501969a1a4777ba4187cf9942a8d7e0367dccaff768b782bdb1a71d046abf - languageName: node - linkType: hard - "esprima@npm:^4.0.0, esprima@npm:^4.0.1": version: 4.0.1 resolution: "esprima@npm:4.0.1" @@ -20091,7 +20656,7 @@ __metadata: languageName: node linkType: hard -"estraverse@npm:^4.2.0": +"estraverse@npm:^4.1.1": version: 4.3.0 resolution: "estraverse@npm:4.3.0" checksum: 10c0/9cb46463ef8a8a4905d3708a652d60122a0c20bb58dec7e0e12ab0e7235123d74214fc0141d743c381813e1b992767e2708194f6f6e0f9fd00c1b4e0887b8b6d @@ -20163,7 +20728,7 @@ __metadata: languageName: node linkType: hard -"events@npm:3.3.0, events@npm:^3.0.0, events@npm:^3.3.0": +"events@npm:3.3.0, events@npm:^3.0.0, events@npm:^3.2.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: 10c0/d6b6f2adbccbcda74ddbab52ed07db727ef52e31a61ed26db9feb7dc62af7fc8e060defa65e5f8af9449b86b52cc1a1f6a79f2eafcf4e62add2b7a1fa4a432f6 @@ -20283,12 +20848,14 @@ __metadata: languageName: node linkType: hard -"express-rate-limit@npm:^7.5.0": - version: 7.5.1 - resolution: "express-rate-limit@npm:7.5.1" +"express-rate-limit@npm:^8.2.2": + version: 8.3.1 + resolution: "express-rate-limit@npm:8.3.1" + dependencies: + ip-address: "npm:10.1.0" peerDependencies: express: ">= 4.11" - checksum: 10c0/b07de84d700a2c07c4bf2f040e7558ed5a1f660f03ed5f30bf8ff7b51e98ba7a85215640e70fc48cbbb9151066ea51239d9a1b41febc9b84d98c7915b0186161 + checksum: 10c0/e3229938457cec617460c54ef4e90c8c254facc884729325d20ea35e3838bd273e4c611fc1f4a76f6d14c411e30d31b15e88eb4be87408615ff0aacc142511a2 languageName: node linkType: hard @@ -20409,7 +20976,7 @@ __metadata: languageName: node linkType: hard -"fast-levenshtein@npm:^2.0.6, fast-levenshtein@npm:~2.0.6": +"fast-levenshtein@npm:^2.0.6": version: 2.0.6 resolution: "fast-levenshtein@npm:2.0.6" checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 @@ -20542,16 +21109,6 @@ __metadata: languageName: node linkType: hard -"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": - version: 3.2.0 - resolution: "fetch-blob@npm:3.2.0" - dependencies: - node-domexception: "npm:^1.0.0" - web-streams-polyfill: "npm:^3.0.3" - checksum: 10c0/60054bf47bfa10fb0ba6cb7742acec2f37c1f56344f79a70bb8b1c48d77675927c720ff3191fa546410a0442c998d27ab05e9144c32d530d8a52fbe68f843b69 - languageName: node - linkType: hard - "figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -20715,13 +21272,20 @@ __metadata: languageName: node linkType: hard -"flatted@npm:3.3.3, flatted@npm:^3.2.7, flatted@npm:^3.2.9": +"flatted@npm:^3.2.7, flatted@npm:^3.2.9": version: 3.3.3 resolution: "flatted@npm:3.3.3" checksum: 10c0/e957a1c6b0254aa15b8cce8533e24165abd98fadc98575db082b786b5da1b7d72062b81bfdcd1da2f4d46b6ed93bec2434e62333e9b4261d79ef2e75a10dd538 languageName: node linkType: hard +"flatted@npm:^3.3.4": + version: 3.4.2 + resolution: "flatted@npm:3.4.2" + checksum: 10c0/a65b67aae7172d6cdf63691be7de6c5cd5adbdfdfe2e9da1a09b617c9512ed794037741ee53d93114276bff3f93cd3b0d97d54f9b316e1e4885dde6e9ffdf7ed + languageName: node + linkType: hard + "fn.name@npm:1.x.x": version: 1.1.0 resolution: "fn.name@npm:1.1.0" @@ -20738,7 +21302,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.6": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.11, follow-redirects@npm:^1.15.6": version: 1.15.11 resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: @@ -20805,6 +21369,29 @@ __metadata: languageName: node linkType: hard +"fork-ts-checker-webpack-plugin@npm:^9.0.0": + version: 9.1.0 + resolution: "fork-ts-checker-webpack-plugin@npm:9.1.0" + dependencies: + "@babel/code-frame": "npm:^7.16.7" + chalk: "npm:^4.1.2" + chokidar: "npm:^4.0.1" + cosmiconfig: "npm:^8.2.0" + deepmerge: "npm:^4.2.2" + fs-extra: "npm:^10.0.0" + memfs: "npm:^3.4.1" + minimatch: "npm:^3.0.4" + node-abort-controller: "npm:^3.0.1" + schema-utils: "npm:^3.1.1" + semver: "npm:^7.3.5" + tapable: "npm:^2.2.1" + peerDependencies: + typescript: ">3.6.0" + webpack: ^5.11.0 + checksum: 10c0/b4acdf400862af5f57d3e159b3a444e7f9f73e9f4609d54604c3810f75f8adcea0165a8b17ee856ed3c65591d058ffd73cd08d273e289d4952844e75f6efa85d + languageName: node + linkType: hard + "form-data-encoder@npm:^1.7.2": version: 1.9.0 resolution: "form-data-encoder@npm:1.9.0" @@ -20826,7 +21413,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0, form-data@npm:^4.0.4": +"form-data@npm:^4.0.0, form-data@npm:^4.0.4, form-data@npm:^4.0.5": version: 4.0.5 resolution: "form-data@npm:4.0.5" dependencies: @@ -20856,15 +21443,6 @@ __metadata: languageName: node linkType: hard -"formdata-polyfill@npm:^4.0.10": - version: 4.0.10 - resolution: "formdata-polyfill@npm:4.0.10" - dependencies: - fetch-blob: "npm:^3.1.2" - checksum: 10c0/5392ec484f9ce0d5e0d52fb5a78e7486637d516179b0eb84d81389d7eccf9ca2f663079da56f761355c0a65792810e3b345dc24db9a8bbbcf24ef3c8c88570c6 - languageName: node - linkType: hard - "formstream@npm:^1.1.1": version: 1.5.2 resolution: "formstream@npm:1.5.2" @@ -20947,6 +21525,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^10.0.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/5f579466e7109719d162a9249abbeffe7f426eb133ea486e020b89bc6d67a741134076bf439983f2eb79276ceaf6bd7b7c1e43c3fd67fe889863e69072fb0a5e + languageName: node + linkType: hard + "fs-extra@npm:^11.0.0, fs-extra@npm:^11.2.0": version: 11.3.3 resolution: "fs-extra@npm:11.3.3" @@ -21151,7 +21740,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.1 resolution: "get-intrinsic@npm:1.3.1" dependencies: @@ -21230,6 +21819,15 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.10.1": + version: 4.13.6 + resolution: "get-tsconfig@npm:4.13.6" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/bab6937302f542f97217cbe7cbbdfa7e85a56a377bc7a73e69224c1f0b7c9ae8365918e55752ae8648265903f506c1705f63c0de1d4bab1ec2830fef3e539a1a + languageName: node + linkType: hard + "get-uri@npm:^6.0.1": version: 6.0.5 resolution: "get-uri@npm:6.0.5" @@ -21241,6 +21839,15 @@ __metadata: languageName: node linkType: hard +"get-value@npm:^3.0.1": + version: 3.0.1 + resolution: "get-value@npm:3.0.1" + dependencies: + isobject: "npm:^3.0.1" + checksum: 10c0/38cca1618ffe9a07a8b105a352c4de27bea36032d4916d21a0636ab5ff3bf1aa3bf200c6875f59f55cd4fb3f5156244dec838fbaf2ba580332d13ac51a25fabd + languageName: node + linkType: hard + "getopts@npm:2.3.0": version: 2.3.0 resolution: "getopts@npm:2.3.0" @@ -21301,6 +21908,13 @@ __metadata: languageName: node linkType: hard +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: 10c0/0486925072d7a916f052842772b61c3e86247f0a80cc0deb9b5a3e8a1a9faad5b04fb6f58986a09f34d3e96cd2a22a24b7e9882fb1cf904c31e9a310de96c429 + languageName: node + linkType: hard + "glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.4.1": version: 10.5.0 resolution: "glob@npm:10.5.0" @@ -21432,7 +22046,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.0.0, globby@npm:^11.0.3, globby@npm:^11.0.4, globby@npm:^11.1.0": +"globby@npm:^11.0.0, globby@npm:^11.0.4, globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -21503,7 +22117,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -21517,19 +22131,17 @@ __metadata: languageName: node linkType: hard -"graphiql@npm:3.1.1": - version: 3.1.1 - resolution: "graphiql@npm:3.1.1" +"graphiql@npm:^3.9.0": + version: 3.9.0 + resolution: "graphiql@npm:3.9.0" dependencies: - "@graphiql/react": "npm:^0.20.3" - "@graphiql/toolkit": "npm:^0.9.1" - graphql-language-service: "npm:^5.2.0" - markdown-it: "npm:^12.2.0" + "@graphiql/react": "npm:^0.29.0" + react-compiler-runtime: "npm:19.1.0-rc.1" peerDependencies: - graphql: ^15.5.0 || ^16.0.0 + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 - checksum: 10c0/d9d8e8fab7290d17c32f5aeb48194c1476c04aed0213b3895fe966e14699620f45f3afd90aa689fc2f8fa97b26ae79ebf24008bcff262d7eb14035e57a7c730e + checksum: 10c0/bdc23b7a5f5dbce4d2d67332a6efaef94802ed687a06a8527d4bc6ab291a811f800309c2605b7df0b7f9a3bfb2bedb83a9fbf3691da5f4f7f912cc825c3f67e1 languageName: node linkType: hard @@ -21542,31 +22154,6 @@ __metadata: languageName: node linkType: hard -"graphql-config@npm:^5.0.2": - version: 5.1.5 - resolution: "graphql-config@npm:5.1.5" - dependencies: - "@graphql-tools/graphql-file-loader": "npm:^8.0.0" - "@graphql-tools/json-file-loader": "npm:^8.0.0" - "@graphql-tools/load": "npm:^8.1.0" - "@graphql-tools/merge": "npm:^9.0.0" - "@graphql-tools/url-loader": "npm:^8.0.0" - "@graphql-tools/utils": "npm:^10.0.0" - cosmiconfig: "npm:^8.1.0" - jiti: "npm:^2.0.0" - minimatch: "npm:^9.0.5" - string-env-interpolation: "npm:^1.0.1" - tslib: "npm:^2.4.0" - peerDependencies: - cosmiconfig-toml-loader: ^1.0.0 - graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - peerDependenciesMeta: - cosmiconfig-toml-loader: - optional: true - checksum: 10c0/05e2a895dd899709da0a6f24c0248d858c23767c4848f27f68935a6fe44d00dfd804c38ef59157193ad30e54d4e0773d9ec562842fe00ce7aaa0330bca5053cd - languageName: node - linkType: hard - "graphql-http@npm:^1.22.0": version: 1.22.4 resolution: "graphql-http@npm:1.22.4" @@ -21576,7 +22163,7 @@ __metadata: languageName: node linkType: hard -"graphql-language-service@npm:5.5.0, graphql-language-service@npm:^5.2.0, graphql-language-service@npm:^5.2.2": +"graphql-language-service@npm:5.5.0, graphql-language-service@npm:^5.3.1": version: 5.5.0 resolution: "graphql-language-service@npm:5.5.0" dependencies: @@ -21622,25 +22209,6 @@ __metadata: languageName: node linkType: hard -"graphql-ws@npm:^6.0.6": - version: 6.0.7 - resolution: "graphql-ws@npm:6.0.7" - peerDependencies: - "@fastify/websocket": ^10 || ^11 - crossws: ~0.3 - graphql: ^15.10.1 || ^16 - ws: ^8 - peerDependenciesMeta: - "@fastify/websocket": - optional: true - crossws: - optional: true - ws: - optional: true - checksum: 10c0/3faba221334a96d3221fc49a8649554831f1504123f03857d1f62761eb5fc4f6f1a0f345dfb2442e871ea63009ed98d4dd995d74bcc671e7e3577ba1b4923953 - languageName: node - linkType: hard - "graphql@npm:^14.0.2 || ^15.5": version: 15.10.1 resolution: "graphql@npm:15.10.1" @@ -22047,7 +22615,7 @@ __metadata: languageName: node linkType: hard -"html-entities@npm:^2.5.2, html-entities@npm:^2.6.0": +"html-entities@npm:^2.1.0, html-entities@npm:^2.5.2, html-entities@npm:^2.6.0": version: 2.6.0 resolution: "html-entities@npm:2.6.0" checksum: 10c0/7c8b15d9ea0cd00dc9279f61bab002ba6ca8a7a0f3c36ed2db3530a67a9621c017830d1d2c1c65beb9b8e3436ea663e9cf8b230472e0e413359399413b27c8b7 @@ -22118,7 +22686,7 @@ __metadata: languageName: node linkType: hard -"http-assert@npm:^1.3.0": +"http-assert@npm:^1.5.0": version: 1.5.0 resolution: "http-assert@npm:1.5.0" dependencies: @@ -22153,20 +22721,7 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:^1.6.3, http-errors@npm:~1.8.0": - version: 1.8.1 - resolution: "http-errors@npm:1.8.1" - dependencies: - depd: "npm:~1.1.2" - inherits: "npm:2.0.4" - setprototypeof: "npm:1.2.0" - statuses: "npm:>= 1.5.0 < 2" - toidentifier: "npm:1.0.1" - checksum: 10c0/f01aeecd76260a6fe7f08e192fcbe9b2f39ed20fc717b852669a69930167053b01790998275c6297d44f435cf0e30edd50c05223d1bec9bc484e6cf35b2d6f43 - languageName: node - linkType: hard - -"http-errors@npm:~2.0.0, http-errors@npm:~2.0.1": +"http-errors@npm:^2.0.0, http-errors@npm:~2.0.0, http-errors@npm:~2.0.1": version: 2.0.1 resolution: "http-errors@npm:2.0.1" dependencies: @@ -22179,6 +22734,19 @@ __metadata: languageName: node linkType: hard +"http-errors@npm:~1.8.0": + version: 1.8.1 + resolution: "http-errors@npm:1.8.1" + dependencies: + depd: "npm:~1.1.2" + inherits: "npm:2.0.4" + setprototypeof: "npm:1.2.0" + statuses: "npm:>= 1.5.0 < 2" + toidentifier: "npm:1.0.1" + checksum: 10c0/f01aeecd76260a6fe7f08e192fcbe9b2f39ed20fc717b852669a69930167053b01790998275c6297d44f435cf0e30edd50c05223d1bec9bc484e6cf35b2d6f43 + languageName: node + linkType: hard + "http-parser-js@npm:>=0.5.1": version: 0.5.10 resolution: "http-parser-js@npm:0.5.10" @@ -22669,7 +23237,7 @@ __metadata: languageName: node linkType: hard -"ip-address@npm:^10.0.1": +"ip-address@npm:10.1.0, ip-address@npm:^10.0.1": version: 10.1.0 resolution: "ip-address@npm:10.1.0" checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 @@ -22690,7 +23258,7 @@ __metadata: languageName: node linkType: hard -"ipaddr.js@npm:^2.1.0": +"ipaddr.js@npm:^2.1.0, ipaddr.js@npm:^2.3.0": version: 2.3.0 resolution: "ipaddr.js@npm:2.3.0" checksum: 10c0/084bab99e2f6875d7a62adc3325e1c64b038a12c9521e35fb967b5e263a8b3afb1b8884dd77c276092331f5d63298b767491e10997ef147c62da01b143780bbd @@ -22741,7 +23309,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": +"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" dependencies: @@ -22983,7 +23551,7 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.2, is-map@npm:^2.0.3": +"is-map@npm:^2.0.3": version: 2.0.3 resolution: "is-map@npm:2.0.3" checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc @@ -23068,13 +23636,6 @@ __metadata: languageName: node linkType: hard -"is-plain-object@npm:^3.0.0": - version: 3.0.1 - resolution: "is-plain-object@npm:3.0.1" - checksum: 10c0/eac88599d3f030b313aa5a12d09bd3c52ce3b8cd975b2fdda6bb3bb69ac0bc1b93cd292123769eb480b914d1dd1fed7633cdeb490458d41294eb32efdedec230 - languageName: node - linkType: hard - "is-plain-object@npm:^5.0.0": version: 5.0.0 resolution: "is-plain-object@npm:5.0.0" @@ -23138,14 +23699,14 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.2, is-set@npm:^2.0.3": +"is-set@npm:^2.0.3": version: 2.0.3 resolution: "is-set@npm:2.0.3" checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4": +"is-shared-array-buffer@npm:^1.0.4": version: 1.0.4 resolution: "is-shared-array-buffer@npm:1.0.4" dependencies: @@ -23170,7 +23731,7 @@ __metadata: languageName: node linkType: hard -"is-string@npm:^1.0.7, is-string@npm:^1.1.1": +"is-string@npm:^1.1.1": version: 1.1.1 resolution: "is-string@npm:1.1.1" dependencies: @@ -23903,6 +24464,17 @@ __metadata: languageName: node linkType: hard +"jest-worker@npm:^27.4.5": + version: 27.5.1 + resolution: "jest-worker@npm:27.5.1" + dependencies: + "@types/node": "npm:*" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.0.0" + checksum: 10c0/8c4737ffd03887b3c6768e4cc3ca0269c0336c1e4b1b120943958ddb035ed2a0fc6acab6dc99631720a3720af4e708ff84fb45382ad1e83c27946adf3623969b + languageName: node + linkType: hard + "jest-worker@npm:^29.7.0": version: 29.7.0 resolution: "jest-worker@npm:29.7.0" @@ -23934,12 +24506,12 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^2.0.0": - version: 2.6.1 - resolution: "jiti@npm:2.6.1" +"jiti@npm:2.4.2": + version: 2.4.2 + resolution: "jiti@npm:2.4.2" bin: jiti: lib/jiti-cli.mjs - checksum: 10c0/79b2e96a8e623f66c1b703b98ec1b8be4500e1d217e09b09e343471bbb9c105381b83edbb979d01cef18318cc45ce6e153571b6c83122170eefa531c64b6789b + checksum: 10c0/4ceac133a08c8faff7eac84aabb917e85e8257f5ad659e843004ce76e981c457c390a220881748ac67ba1b940b9b729b30fb85cbaf6e7989f04b6002c94da331 languageName: node linkType: hard @@ -23985,13 +24557,6 @@ __metadata: languageName: node linkType: hard -"js-md4@npm:^0.3.2": - version: 0.3.2 - resolution: "js-md4@npm:0.3.2" - checksum: 10c0/8313e00c45f710a53bdadc199c095b48ebaf54ea7b8cdb67a3f1863c270a5e9d0f89f204436b73866002af8c7ac4cacc872fdf271fc70e26614e424c7685b577 - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -24094,7 +24659,7 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^2.3.0": +"json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1" checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 @@ -24263,17 +24828,6 @@ __metadata: languageName: node linkType: hard -"jsonpath@npm:^1.1.1": - version: 1.1.1 - resolution: "jsonpath@npm:1.1.1" - dependencies: - esprima: "npm:1.2.2" - static-eval: "npm:2.0.2" - underscore: "npm:1.12.1" - checksum: 10c0/4fea3f83bcb4df08c32090ba8a0d1a6d26244f6d19c4296f9b58caa01eeb7de0f8347eba40077ceee2f95acc69d032b0b48226d350339063ba580e87983f6dec - languageName: node - linkType: hard - "jsonpointer@npm:^5.0.0, jsonpointer@npm:^5.0.1": version: 5.0.1 resolution: "jsonpointer@npm:5.0.1" @@ -24446,6 +25000,17 @@ __metadata: languageName: node linkType: hard +"keytar@npm:^7.9.0": + version: 7.9.0 + resolution: "keytar@npm:7.9.0" + dependencies: + node-addon-api: "npm:^4.3.0" + node-gyp: "npm:latest" + prebuild-install: "npm:^7.0.1" + checksum: 10c0/a3f987ffc82b8c028c59451f9e50f71620b5455d9d356564d9c825df5bc36c47661caf21df0026795a5fbe0013c18c8e4bedff4da34bb20c9683ef20b685fee3 + languageName: node + linkType: hard + "keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -24531,44 +25096,29 @@ __metadata: languageName: node linkType: hard -"koa-convert@npm:^2.0.0": - version: 2.0.0 - resolution: "koa-convert@npm:2.0.0" - dependencies: - co: "npm:^4.6.0" - koa-compose: "npm:^4.1.0" - checksum: 10c0/d3e243ceccd11524d5f4942f6ccd828a9b18a1a967c4375192aa9eedf844f790563632839f006732ce8ca720275737c65a3bab344e13b25f41fb2be451ea102c - languageName: node - linkType: hard - -"koa@npm:2.15.4": - version: 2.15.4 - resolution: "koa@npm:2.15.4" +"koa@npm:3.0.3": + version: 3.0.3 + resolution: "koa@npm:3.0.3" dependencies: - accepts: "npm:^1.3.5" - cache-content-type: "npm:^1.0.0" - content-disposition: "npm:~0.5.2" - content-type: "npm:^1.0.4" - cookies: "npm:~0.9.0" - debug: "npm:^4.3.2" + accepts: "npm:^1.3.8" + content-disposition: "npm:~0.5.4" + content-type: "npm:^1.0.5" + cookies: "npm:~0.9.1" delegates: "npm:^1.0.0" - depd: "npm:^2.0.0" - destroy: "npm:^1.0.4" - encodeurl: "npm:^1.0.2" + destroy: "npm:^1.2.0" + encodeurl: "npm:^2.0.0" escape-html: "npm:^1.0.3" fresh: "npm:~0.5.2" - http-assert: "npm:^1.3.0" - http-errors: "npm:^1.6.3" - is-generator-function: "npm:^1.0.7" + http-assert: "npm:^1.5.0" + http-errors: "npm:^2.0.0" koa-compose: "npm:^4.1.0" - koa-convert: "npm:^2.0.0" - on-finished: "npm:^2.3.0" - only: "npm:~0.0.2" - parseurl: "npm:^1.3.2" - statuses: "npm:^1.5.0" - type-is: "npm:^1.6.16" + mime-types: "npm:^3.0.1" + on-finished: "npm:^2.4.1" + parseurl: "npm:^1.3.3" + statuses: "npm:^2.0.1" + type-is: "npm:^2.0.1" vary: "npm:^1.1.2" - checksum: 10c0/fd2171b4dba706d35244fe60403a61671717a167453349813757999dad280049ddd0dcdba23cda197a5a3538f4c034cf0fd1f9caeb849be1ca1eecaa78db2f99 + checksum: 10c0/596472cb6dcc6c443a1917afd65e0e1e73e76ab58507e0654af0e77acaecd5cd5f2217107a65783e51b1917f2bed77aa5422c8db0da4d4fec8afcf5e75af7c02 languageName: node linkType: hard @@ -24650,29 +25200,19 @@ __metadata: languageName: node linkType: hard -"levn@npm:~0.3.0": - version: 0.3.0 - resolution: "levn@npm:0.3.0" - dependencies: - prelude-ls: "npm:~1.1.2" - type-check: "npm:~0.3.2" - checksum: 10c0/e440df9de4233da0b389cd55bd61f0f6aaff766400bebbccd1231b81801f6dbc1d816c676ebe8d70566394b749fa624b1ed1c68070e9c94999f0bdecc64cb676 - languageName: node - linkType: hard - -"libsodium-wrappers@npm:^0.7.11": - version: 0.7.16 - resolution: "libsodium-wrappers@npm:0.7.16" +"libsodium-wrappers@npm:^0.8.0": + version: 0.8.2 + resolution: "libsodium-wrappers@npm:0.8.2" dependencies: - libsodium: "npm:^0.7.16" - checksum: 10c0/7e6b5f3db8b8a3181e713b5017e000384142137a1712cd4c966f11f03e53ee5169c3277b80c3f2b68676f7e9a6ad141e58ce8b6a71728538b42d1201a566faf6 + libsodium: "npm:^0.8.0" + checksum: 10c0/4e0cdce7348bd2f45d4d6549e804b10e76bcebc8acbcd68f200188d3168a652730f0555d5aa2d964b31fa3dcacfca9b3c9a0d519b2e4f0640de2ab87d7cc11f9 languageName: node linkType: hard -"libsodium@npm:^0.7.16": - version: 0.7.16 - resolution: "libsodium@npm:0.7.16" - checksum: 10c0/4f1edd4d5d703c20505bbed74bf0dea22db7b9787cfbe4ab9572f0f4ca16f7a5d1d90e18a822ee4d91058a7c9701f82a24a5f22d0a29be5203b9ab9d8c037bda +"libsodium@npm:^0.8.0": + version: 0.8.2 + resolution: "libsodium@npm:0.8.2" + checksum: 10c0/e943b269331670e45d63885ee32dec5471daa4c78ddfbe35e00e27a5b0ea7b51e7fa8260142ba478dcba0918e8117e9151883c11466b2fab2638ec29a1f3482b languageName: node linkType: hard @@ -24699,15 +25239,6 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:^3.0.1": - version: 3.0.3 - resolution: "linkify-it@npm:3.0.3" - dependencies: - uc.micro: "npm:^1.0.1" - checksum: 10c0/468cb4954f85cdfc16e169db89a42d65287e3f121a9448b29c3c00d64c6f5a8f4367bea3978ba9109a0e3a10b19d50632b983639f91b9be9f20d1f63a5ff5bc1 - languageName: node - linkType: hard - "linkify-it@npm:^5.0.0": version: 5.0.0 resolution: "linkify-it@npm:5.0.0" @@ -24734,6 +25265,13 @@ __metadata: languageName: node linkType: hard +"loader-runner@npm:^4.3.1": + version: 4.3.1 + resolution: "loader-runner@npm:4.3.1" + checksum: 10c0/a523b6329f114e0a98317158e30a7dfce044b731521be5399464010472a93a15ece44757d1eaed1d8845019869c5390218bc1c7c3110f4eeaef5157394486eac + languageName: node + linkType: hard + "loader-utils@npm:^1.1.0": version: 1.4.2 resolution: "loader-utils@npm:1.4.2" @@ -24745,7 +25283,7 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:^2.0.0": +"loader-utils@npm:^2.0.0, loader-utils@npm:^2.0.4": version: 2.0.4 resolution: "loader-utils@npm:2.0.4" dependencies: @@ -24917,13 +25455,6 @@ __metadata: languageName: node linkType: hard -"lodash.sortby@npm:^4.7.0": - version: 4.7.0 - resolution: "lodash.sortby@npm:4.7.0" - checksum: 10c0/fc48fb54ff7669f33bb32997cab9460757ee99fafaf72400b261c3e10fde21538e47d8cfcbe6a25a31bcb5b7b727c27d52626386fc2de24eb059a6d64a89cdf5 - languageName: node - linkType: hard - "lodash.topath@npm:^4.5.2": version: 4.5.2 resolution: "lodash.topath@npm:4.5.2" @@ -25187,21 +25718,6 @@ __metadata: languageName: node linkType: hard -"markdown-it@npm:^12.2.0": - version: 12.3.2 - resolution: "markdown-it@npm:12.3.2" - dependencies: - argparse: "npm:^2.0.1" - entities: "npm:~2.1.0" - linkify-it: "npm:^3.0.1" - mdurl: "npm:^1.0.1" - uc.micro: "npm:^1.0.5" - bin: - markdown-it: bin/markdown-it.js - checksum: 10c0/7f97b924e6f90e2c5ccdfb486a19bd7885b938f568a86b527bf6f916a16b01a298e6739f86a99e77acb5e7c020f6c8b34bd726364179b3f820e48b2971a6450c - languageName: node - linkType: hard - "markdown-it@npm:^14.1.0": version: 14.1.0 resolution: "markdown-it@npm:14.1.0" @@ -25490,13 +26006,6 @@ __metadata: languageName: node linkType: hard -"mdurl@npm:^1.0.1": - version: 1.0.1 - resolution: "mdurl@npm:1.0.1" - checksum: 10c0/ea8534341eb002aaa532a722daef6074cd8ca66202e10a2b4cda46722c1ebdb1da92197ac300bc953d3ef1bf41cd6561ef2cc69d82d5d0237dae00d4a61a4eee - languageName: node - linkType: hard - "mdurl@npm:^2.0.0": version: 2.0.0 resolution: "mdurl@npm:2.0.0" @@ -25518,7 +26027,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.1.2": +"memfs@npm:^3.1.2, memfs@npm:^3.4.1": version: 3.5.3 resolution: "memfs@npm:3.5.3" dependencies: @@ -25586,7 +26095,7 @@ __metadata: languageName: node linkType: hard -"meros@npm:^1.1.4, meros@npm:^1.2.1": +"meros@npm:^1.1.4": version: 1.3.2 resolution: "meros@npm:1.3.2" peerDependencies: @@ -25934,7 +26443,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -25970,7 +26479,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.18, mime-types@npm:^2.1.27, mime-types@npm:^2.1.35, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34, mime-types@npm:~2.1.35": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.35, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34, mime-types@npm:~2.1.35": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -25979,7 +26488,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^3.0.1": +"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1": version: 3.0.2 resolution: "mime-types@npm:3.0.2" dependencies: @@ -26036,6 +26545,18 @@ __metadata: languageName: node linkType: hard +"mini-css-extract-plugin@npm:^2.4.2": + version: 2.10.1 + resolution: "mini-css-extract-plugin@npm:2.10.1" + dependencies: + schema-utils: "npm:^4.0.0" + tapable: "npm:^2.2.1" + peerDependencies: + webpack: ^5.0.0 + checksum: 10c0/2e90dacdca5bc35862e601e6b3989673e498217c789c4bb270a35bbd22b4d6e85f091795d0324d5cda5a9e2aa2a8f1f3340b6db5a96d66ec208c627659bebb08 + languageName: node + linkType: hard + "minim@npm:~0.23.8": version: 0.23.8 resolution: "minim@npm:0.23.8" @@ -26077,6 +26598,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.2.1": + version: 10.2.4 + resolution: "minimatch@npm:10.2.4" + dependencies: + brace-expansion: "npm:^5.0.2" + checksum: 10c0/35f3dfb7b99b51efd46afd378486889f590e7efb10e0f6a10ba6800428cf65c9a8dedb74427d0570b318d749b543dc4e85f06d46d2858bc8cac7e1eb49a95945 + languageName: node + linkType: hard + "minimatch@npm:^5.0.1, minimatch@npm:^5.1.0": version: 5.1.6 resolution: "minimatch@npm:5.1.6" @@ -26095,7 +26625,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": +"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -26647,13 +27177,22 @@ __metadata: languageName: node linkType: hard -"node-abort-controller@npm:^3.1.1": +"node-abort-controller@npm:^3.0.1, node-abort-controller@npm:^3.1.1": version: 3.1.1 resolution: "node-abort-controller@npm:3.1.1" checksum: 10c0/f7ad0e7a8e33809d4f3a0d1d65036a711c39e9d23e0319d80ebe076b9a3b4432b4d6b86a7fab65521de3f6872ffed36fc35d1327487c48eb88c517803403eda3 languageName: node linkType: hard +"node-addon-api@npm:^4.3.0": + version: 4.3.0 + resolution: "node-addon-api@npm:4.3.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/5febe94d58cdef319bc96a357b43d7a13776c93ee3f2edb374000f16454e65cec06035497947d5fdaa50db1cc7ab8e3a30ca8669bb07a1b159f0307dc2c1ccdf + languageName: node + linkType: hard + "node-addon-api@npm:^8.2.2, node-addon-api@npm:^8.3.0, node-addon-api@npm:^8.3.1": version: 8.5.0 resolution: "node-addon-api@npm:8.5.0" @@ -26708,17 +27247,6 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^3.3.2": - version: 3.3.2 - resolution: "node-fetch@npm:3.3.2" - dependencies: - data-uri-to-buffer: "npm:^4.0.0" - fetch-blob: "npm:^3.1.4" - formdata-polyfill: "npm:^4.0.10" - checksum: 10c0/f3d5e56190562221398c9f5750198b34cf6113aa304e34ee97c94fd300ec578b25b2c2906edba922050fce983338fde0d5d34fcb0fc3336ade5bd0e429ad7538 - languageName: node - linkType: hard - "node-forge@npm:^1, node-forge@npm:^1.2.1, node-forge@npm:^1.3.2": version: 1.3.3 resolution: "node-forge@npm:1.3.3" @@ -26866,15 +27394,6 @@ __metadata: languageName: node linkType: hard -"normalize-path@npm:^2.1.1": - version: 2.1.1 - resolution: "normalize-path@npm:2.1.1" - dependencies: - remove-trailing-separator: "npm:^1.0.1" - checksum: 10c0/db814326ff88057437233361b4c7e9cac7b54815b051b57f2d341ce89b1d8ec8cbd43e7fa95d7652b3b69ea8fcc294b89b8530d556a84d1bdace94229e1e9a8b - languageName: node - linkType: hard - "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" @@ -27123,7 +27642,7 @@ __metadata: languageName: node linkType: hard -"on-finished@npm:^2.3.0, on-finished@npm:^2.4.1, on-finished@npm:~2.4.1": +"on-finished@npm:^2.4.1, on-finished@npm:~2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" dependencies: @@ -27175,13 +27694,6 @@ __metadata: languageName: node linkType: hard -"only@npm:~0.0.2": - version: 0.0.2 - resolution: "only@npm:0.0.2" - checksum: 10c0/d26b1347835a5a9b17afbd889ed60de3d3ae14cdeca5ba008d86e6bf055466a431adc731b82e1e8ab24a3b8be5b5c2cdbc16e652d231d18cc1a5752320aaf0a0 - languageName: node - linkType: hard - "ono@npm:^7.1.3": version: 7.1.3 resolution: "ono@npm:7.1.3" @@ -27273,20 +27785,6 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.8.1": - version: 0.8.3 - resolution: "optionator@npm:0.8.3" - dependencies: - deep-is: "npm:~0.1.3" - fast-levenshtein: "npm:~2.0.6" - levn: "npm:~0.3.0" - prelude-ls: "npm:~1.1.2" - type-check: "npm:~0.3.2" - word-wrap: "npm:~1.2.3" - checksum: 10c0/ad7000ea661792b3ec5f8f86aac28895850988926f483b5f308f59f4607dfbe24c05df2d049532ee227c040081f39401a268cf7bbf3301512f74c4d760dc6dd8 - languageName: node - linkType: hard - "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -27366,15 +27864,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:3.1.0, p-limit@npm:^3.0.1, p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: "npm:^0.1.0" - checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a - languageName: node - linkType: hard - "p-limit@npm:^2.0.0, p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -27384,6 +27873,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^3.0.1, p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + "p-locate@npm:^3.0.0": version: 3.0.0 resolution: "p-locate@npm:3.0.0" @@ -27641,7 +28139,7 @@ __metadata: languageName: node linkType: hard -"parseurl@npm:^1.3.2, parseurl@npm:~1.3.3": +"parseurl@npm:^1.3.3, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5 @@ -27955,20 +28453,13 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": +"picocolors@npm:1.1.1, picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 languageName: node linkType: hard -"picomatch-browser@npm:^2.2.6": - version: 2.2.6 - resolution: "picomatch-browser@npm:2.2.6" - checksum: 10c0/bf97d3e6f77dee776fe4cc7728037931b681c56e1fd964023ed797de341a0e32dcc1e90a5552cc74923cb97566464870a37be188b09e3db7279f9e9a9b12d977 - languageName: node - linkType: hard - "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -27983,6 +28474,13 @@ __metadata: languageName: node linkType: hard +"pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc + languageName: node + linkType: hard + "pify@npm:^4.0.1": version: 4.0.1 resolution: "pify@npm:4.0.1" @@ -28073,6 +28571,20 @@ __metadata: languageName: node linkType: hard +"pkijs@npm:^3.3.3": + version: 3.4.0 + resolution: "pkijs@npm:3.4.0" + dependencies: + "@noble/hashes": "npm:1.4.0" + asn1js: "npm:^3.0.6" + bytestreamjs: "npm:^2.0.1" + pvtsutils: "npm:^1.3.6" + pvutils: "npm:^1.1.3" + tslib: "npm:^2.8.1" + checksum: 10c0/33cfab9283702782ae228bd2d4a51b1e9b2e0d6e2141207f29ee95716101ac4fe6e6821882da5f5eca28c74be3964b181b09e95cbbb757b2bd9dca918a5765fd + languageName: node + linkType: hard + "playwright-core@npm:1.58.1": version: 1.58.1 resolution: "playwright-core@npm:1.58.1" @@ -28235,6 +28747,19 @@ __metadata: languageName: node linkType: hard +"postcss-import@npm:^16.1.0": + version: 16.1.1 + resolution: "postcss-import@npm:16.1.1" + dependencies: + postcss-value-parser: "npm:^4.0.0" + read-cache: "npm:^1.0.0" + resolve: "npm:^1.1.7" + peerDependencies: + postcss: ^8.0.0 + checksum: 10c0/b91245971564891110cb407c7459bd470893ccbdd3328fdd384ef2be4d0e5f304e5834acbb8b537ad562c001db34bee1c29b55994bead5ab5a53b1edc2a687b9 + languageName: node + linkType: hard + "postcss-load-config@npm:^3.0.0": version: 3.1.4 resolution: "postcss-load-config@npm:3.1.4" @@ -28566,7 +29091,7 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 @@ -28614,7 +29139,7 @@ __metadata: languageName: node linkType: hard -"prebuild-install@npm:^7.1.1, prebuild-install@npm:^7.1.3": +"prebuild-install@npm:^7.0.1, prebuild-install@npm:^7.1.1, prebuild-install@npm:^7.1.3": version: 7.1.3 resolution: "prebuild-install@npm:7.1.3" dependencies: @@ -28643,13 +29168,6 @@ __metadata: languageName: node linkType: hard -"prelude-ls@npm:~1.1.2": - version: 1.1.2 - resolution: "prelude-ls@npm:1.1.2" - checksum: 10c0/7284270064f74e0bb7f04eb9bff7be677e4146417e599ccc9c1200f0f640f8b11e592d94eb1b18f7aa9518031913bb42bea9c86af07ba69902864e61005d6f18 - languageName: node - linkType: hard - "prettier@npm:^2.3.2": version: 2.8.8 resolution: "prettier@npm:2.8.8" @@ -28781,6 +29299,17 @@ __metadata: languageName: node linkType: hard +"proper-lockfile@npm:^4.1.2": + version: 4.1.2 + resolution: "proper-lockfile@npm:4.1.2" + dependencies: + graceful-fs: "npm:^4.2.4" + retry: "npm:^0.12.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/2f265dbad15897a43110a02dae55105c04d356ec4ed560723dcb9f0d34bc4fb2f13f79bb930e7561be10278e2314db5aca2527d5d3dcbbdee5e6b331d1571f6d + languageName: node + linkType: hard + "property-information@npm:^5.0.0": version: 5.6.0 resolution: "property-information@npm:5.6.0" @@ -28909,7 +29438,23 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.1, qs@npm:^6.9.4, qs@npm:~6.14.0": +"pvtsutils@npm:^1.3.6": + version: 1.3.6 + resolution: "pvtsutils@npm:1.3.6" + dependencies: + tslib: "npm:^2.8.1" + checksum: 10c0/b1b42646370505ccae536dcffa662303b2c553995211330c8e39dec9ab8c197585d7751c2c5b9ab2f186feda0219d9bb23c34ee1e565573be96450f79d89a13c + languageName: node + linkType: hard + +"pvutils@npm:^1.1.3": + version: 1.1.5 + resolution: "pvutils@npm:1.1.5" + checksum: 10c0/e968b07b78a58fec9377fe7aa6342c8cfa21c8fb4afc4e51e1489bd42bec6dc71b8a52541d0aede0aea17adec7ca3f89f29f56efdc31d0083cc02e9bb5721bcf + languageName: node + linkType: hard + +"qs@npm:^6.10.1, qs@npm:^6.11.2, qs@npm:^6.12.3, qs@npm:^6.14.1, qs@npm:^6.9.4, qs@npm:~6.14.0": version: 6.14.1 resolution: "qs@npm:6.14.1" dependencies: @@ -29052,13 +29597,6 @@ __metadata: languageName: node linkType: hard -"rate-limiter-flexible@npm:^4.0.1": - version: 4.0.1 - resolution: "rate-limiter-flexible@npm:4.0.1" - checksum: 10c0/93db9ed61a62c4d7d411713e12ed9cd7ea196a08b81cb289156f7ff0fe85bd4607916e82be750d2d8c44248dafefaff3f4a1cd4b7caae077b078573ad7f24fa6 - languageName: node - linkType: hard - "raw-body@npm:^2.4.1, raw-body@npm:~2.5.3": version: 2.5.3 resolution: "raw-body@npm:2.5.3" @@ -29131,96 +29669,96 @@ __metadata: languageName: node linkType: hard -"react-aria-components@npm:^1.13.0": - version: 1.14.0 - resolution: "react-aria-components@npm:1.14.0" +"react-aria-components@npm:^1.14.0": + version: 1.16.0 + resolution: "react-aria-components@npm:1.16.0" dependencies: - "@internationalized/date": "npm:^3.10.1" + "@internationalized/date": "npm:^3.12.0" "@internationalized/string": "npm:^3.2.7" - "@react-aria/autocomplete": "npm:3.0.0-rc.4" - "@react-aria/collections": "npm:^3.0.1" - "@react-aria/dnd": "npm:^3.11.4" - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/autocomplete": "npm:3.0.0-rc.6" + "@react-aria/collections": "npm:^3.0.3" + "@react-aria/dnd": "npm:^3.11.6" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/interactions": "npm:^3.27.1" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/overlays": "npm:^3.31.0" + "@react-aria/overlays": "npm:^3.31.2" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/toolbar": "npm:3.0.0-beta.22" - "@react-aria/utils": "npm:^3.32.0" - "@react-aria/virtualizer": "npm:^4.1.11" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/toolbar": "npm:3.0.0-beta.24" + "@react-aria/utils": "npm:^3.33.1" + "@react-aria/virtualizer": "npm:^4.1.13" "@react-stately/autocomplete": "npm:3.0.0-beta.4" - "@react-stately/layout": "npm:^4.5.2" - "@react-stately/selection": "npm:^3.20.7" - "@react-stately/table": "npm:^3.15.2" + "@react-stately/layout": "npm:^4.6.0" + "@react-stately/selection": "npm:^3.20.9" + "@react-stately/table": "npm:^3.15.4" "@react-stately/utils": "npm:^3.11.0" - "@react-stately/virtualizer": "npm:^4.4.4" - "@react-types/form": "npm:^3.7.16" - "@react-types/grid": "npm:^3.3.6" - "@react-types/shared": "npm:^3.32.1" - "@react-types/table": "npm:^3.13.4" + "@react-stately/virtualizer": "npm:^4.4.6" + "@react-types/form": "npm:^3.7.18" + "@react-types/grid": "npm:^3.3.8" + "@react-types/shared": "npm:^3.33.1" + "@react-types/table": "npm:^3.13.6" "@swc/helpers": "npm:^0.5.0" client-only: "npm:^0.0.1" - react-aria: "npm:^3.45.0" - react-stately: "npm:^3.43.0" - use-sync-external-store: "npm:^1.4.0" + react-aria: "npm:^3.47.0" + react-stately: "npm:^3.45.0" + use-sync-external-store: "npm:^1.6.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/e532b87d2b4aca5568e29e95caf0a0a6f19e35ea08014bea4d389e63b10067497a3e9441d1b72bf11ab6d5b17c8c93c13fe193bcbd369c81a43825bd87a791ce + checksum: 10c0/d71ebc84e68704c72ad5dca7455cc3705c231093161d88b6c41bd8e7609871955ab3f5d01b903588947e1786898faf705b824e0d10d9c0b643d393356245c3f1 languageName: node linkType: hard -"react-aria@npm:^3.45.0": - version: 3.45.0 - resolution: "react-aria@npm:3.45.0" +"react-aria@npm:^3.47.0": + version: 3.47.0 + resolution: "react-aria@npm:3.47.0" dependencies: "@internationalized/string": "npm:^3.2.7" - "@react-aria/breadcrumbs": "npm:^3.5.30" - "@react-aria/button": "npm:^3.14.3" - "@react-aria/calendar": "npm:^3.9.3" - "@react-aria/checkbox": "npm:^3.16.3" - "@react-aria/color": "npm:^3.1.3" - "@react-aria/combobox": "npm:^3.14.1" - "@react-aria/datepicker": "npm:^3.15.3" - "@react-aria/dialog": "npm:^3.5.32" - "@react-aria/disclosure": "npm:^3.1.1" - "@react-aria/dnd": "npm:^3.11.4" - "@react-aria/focus": "npm:^3.21.3" - "@react-aria/gridlist": "npm:^3.14.2" - "@react-aria/i18n": "npm:^3.12.14" - "@react-aria/interactions": "npm:^3.26.0" - "@react-aria/label": "npm:^3.7.23" - "@react-aria/landmark": "npm:^3.0.8" - "@react-aria/link": "npm:^3.8.7" - "@react-aria/listbox": "npm:^3.15.1" - "@react-aria/menu": "npm:^3.19.4" - "@react-aria/meter": "npm:^3.4.28" - "@react-aria/numberfield": "npm:^3.12.3" - "@react-aria/overlays": "npm:^3.31.0" - "@react-aria/progress": "npm:^3.4.28" - "@react-aria/radio": "npm:^3.12.3" - "@react-aria/searchfield": "npm:^3.8.10" - "@react-aria/select": "npm:^3.17.1" - "@react-aria/selection": "npm:^3.27.0" - "@react-aria/separator": "npm:^3.4.14" - "@react-aria/slider": "npm:^3.8.3" + "@react-aria/breadcrumbs": "npm:^3.5.32" + "@react-aria/button": "npm:^3.14.5" + "@react-aria/calendar": "npm:^3.9.5" + "@react-aria/checkbox": "npm:^3.16.5" + "@react-aria/color": "npm:^3.1.5" + "@react-aria/combobox": "npm:^3.15.0" + "@react-aria/datepicker": "npm:^3.16.1" + "@react-aria/dialog": "npm:^3.5.34" + "@react-aria/disclosure": "npm:^3.1.3" + "@react-aria/dnd": "npm:^3.11.6" + "@react-aria/focus": "npm:^3.21.5" + "@react-aria/gridlist": "npm:^3.14.4" + "@react-aria/i18n": "npm:^3.12.16" + "@react-aria/interactions": "npm:^3.27.1" + "@react-aria/label": "npm:^3.7.25" + "@react-aria/landmark": "npm:^3.0.10" + "@react-aria/link": "npm:^3.8.9" + "@react-aria/listbox": "npm:^3.15.3" + "@react-aria/menu": "npm:^3.21.0" + "@react-aria/meter": "npm:^3.4.30" + "@react-aria/numberfield": "npm:^3.12.5" + "@react-aria/overlays": "npm:^3.31.2" + "@react-aria/progress": "npm:^3.4.30" + "@react-aria/radio": "npm:^3.12.5" + "@react-aria/searchfield": "npm:^3.8.12" + "@react-aria/select": "npm:^3.17.3" + "@react-aria/selection": "npm:^3.27.2" + "@react-aria/separator": "npm:^3.4.16" + "@react-aria/slider": "npm:^3.8.5" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/switch": "npm:^3.7.9" - "@react-aria/table": "npm:^3.17.9" - "@react-aria/tabs": "npm:^3.10.9" - "@react-aria/tag": "npm:^3.7.3" - "@react-aria/textfield": "npm:^3.18.3" - "@react-aria/toast": "npm:^3.0.9" - "@react-aria/tooltip": "npm:^3.9.0" - "@react-aria/tree": "npm:^3.1.5" - "@react-aria/utils": "npm:^3.32.0" - "@react-aria/visually-hidden": "npm:^3.8.29" - "@react-types/shared": "npm:^3.32.1" + "@react-aria/switch": "npm:^3.7.11" + "@react-aria/table": "npm:^3.17.11" + "@react-aria/tabs": "npm:^3.11.1" + "@react-aria/tag": "npm:^3.8.1" + "@react-aria/textfield": "npm:^3.18.5" + "@react-aria/toast": "npm:^3.0.11" + "@react-aria/tooltip": "npm:^3.9.2" + "@react-aria/tree": "npm:^3.1.7" + "@react-aria/utils": "npm:^3.33.1" + "@react-aria/visually-hidden": "npm:^3.8.31" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/f2a8a170f9f3632560b2621dfcc3527cbbde7c0d744a7af0f2447e7d45fd4f7ff0344ab08a39d60058dcece97d580606ab7bc2724ba77e377e6c9bbdd7c1e840 + checksum: 10c0/9ec9a05d83dee25b5b0e7f97ec5ce86cfe00a0ae3f8507f3c414a44c5c7fc88dfd1430fe49f2b57333f964244f36122e255ebe795cca41a3562ea28e24d31ea5 languageName: node linkType: hard @@ -29242,6 +29780,15 @@ __metadata: languageName: node linkType: hard +"react-compiler-runtime@npm:19.1.0-rc.1": + version: 19.1.0-rc.1 + resolution: "react-compiler-runtime@npm:19.1.0-rc.1" + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental + checksum: 10c0/7bb644339464e0193dba15ac4f5324d10f31dcd1e1807d7fd156355154f5fa360b59b4ffd07dc1138c493c1eebf9ef27d695d833ebe1b1030b3a9201a99e0698 + languageName: node + linkType: hard + "react-copy-to-clipboard@npm:5.1.0": version: 5.1.0 resolution: "react-copy-to-clipboard@npm:5.1.0" @@ -29539,10 +30086,10 @@ __metadata: languageName: node linkType: hard -"react-refresh@npm:^0.17.0": - version: 0.17.0 - resolution: "react-refresh@npm:0.17.0" - checksum: 10c0/002cba940384c9930008c0bce26cac97a9d5682bc623112c2268ba0c155127d9c178a9a5cc2212d560088d60dfd503edd808669a25f9b377f316a32361d0b23c +"react-refresh@npm:^0.18.0": + version: 0.18.0 + resolution: "react-refresh@npm:0.18.0" + checksum: 10c0/34a262f7fd803433a534f50deb27a148112a81adcae440c7d1cbae7ef14d21ea8f2b3d783e858cb7698968183b77755a38b4d4b5b1d79b4f4689c2f6d358fff2 languageName: node linkType: hard @@ -29658,39 +30205,39 @@ __metadata: languageName: node linkType: hard -"react-stately@npm:^3.43.0": - version: 3.43.0 - resolution: "react-stately@npm:3.43.0" - dependencies: - "@react-stately/calendar": "npm:^3.9.1" - "@react-stately/checkbox": "npm:^3.7.3" - "@react-stately/collections": "npm:^3.12.8" - "@react-stately/color": "npm:^3.9.3" - "@react-stately/combobox": "npm:^3.12.1" - "@react-stately/data": "npm:^3.15.0" - "@react-stately/datepicker": "npm:^3.15.3" - "@react-stately/disclosure": "npm:^3.0.9" - "@react-stately/dnd": "npm:^3.7.2" - "@react-stately/form": "npm:^3.2.2" - "@react-stately/list": "npm:^3.13.2" - "@react-stately/menu": "npm:^3.9.9" - "@react-stately/numberfield": "npm:^3.10.3" - "@react-stately/overlays": "npm:^3.6.21" - "@react-stately/radio": "npm:^3.11.3" - "@react-stately/searchfield": "npm:^3.5.17" - "@react-stately/select": "npm:^3.9.0" - "@react-stately/selection": "npm:^3.20.7" - "@react-stately/slider": "npm:^3.7.3" - "@react-stately/table": "npm:^3.15.2" - "@react-stately/tabs": "npm:^3.8.7" - "@react-stately/toast": "npm:^3.1.2" - "@react-stately/toggle": "npm:^3.9.3" - "@react-stately/tooltip": "npm:^3.5.9" - "@react-stately/tree": "npm:^3.9.4" - "@react-types/shared": "npm:^3.32.1" +"react-stately@npm:^3.45.0": + version: 3.45.0 + resolution: "react-stately@npm:3.45.0" + dependencies: + "@react-stately/calendar": "npm:^3.9.3" + "@react-stately/checkbox": "npm:^3.7.5" + "@react-stately/collections": "npm:^3.12.10" + "@react-stately/color": "npm:^3.9.5" + "@react-stately/combobox": "npm:^3.13.0" + "@react-stately/data": "npm:^3.15.2" + "@react-stately/datepicker": "npm:^3.16.1" + "@react-stately/disclosure": "npm:^3.0.11" + "@react-stately/dnd": "npm:^3.7.4" + "@react-stately/form": "npm:^3.2.4" + "@react-stately/list": "npm:^3.13.4" + "@react-stately/menu": "npm:^3.9.11" + "@react-stately/numberfield": "npm:^3.11.0" + "@react-stately/overlays": "npm:^3.6.23" + "@react-stately/radio": "npm:^3.11.5" + "@react-stately/searchfield": "npm:^3.5.19" + "@react-stately/select": "npm:^3.9.2" + "@react-stately/selection": "npm:^3.20.9" + "@react-stately/slider": "npm:^3.7.5" + "@react-stately/table": "npm:^3.15.4" + "@react-stately/tabs": "npm:^3.8.9" + "@react-stately/toast": "npm:^3.1.3" + "@react-stately/toggle": "npm:^3.9.5" + "@react-stately/tooltip": "npm:^3.5.11" + "@react-stately/tree": "npm:^3.9.6" + "@react-types/shared": "npm:^3.33.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/198ec9dea31e93f46cf82cd31c079ecf9c2b62dc8ef55515226dd8caabbfe5487defe2af57e036153b6e729cf97b1b4fdb64c9323b4cd71e5bc652ec4b520763 + checksum: 10c0/847d614e15d9928160664680707e885398f7b0d7d73e966edab821dec1a4abe55fe909c51a02b674aa4d75397028cb3c024bfc5d5201f01f346686b0503ba233 languageName: node linkType: hard @@ -29767,7 +30314,7 @@ __metadata: languageName: node linkType: hard -"react-use@npm:^17.2.4, react-use@npm:^17.3.2, react-use@npm:^17.4.0": +"react-use@npm:^17.2.4, react-use@npm:^17.3.2, react-use@npm:^17.4.0, react-use@npm:^17.6.0": version: 17.6.0 resolution: "react-use@npm:17.6.0" dependencies: @@ -29824,6 +30371,15 @@ __metadata: languageName: node linkType: hard +"read-cache@npm:^1.0.0": + version: 1.0.0 + resolution: "read-cache@npm:1.0.0" + dependencies: + pify: "npm:^2.3.0" + checksum: 10c0/90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814 + languageName: node + linkType: hard + "read-tls-client-hello@npm:^1.1.0": version: 1.1.0 resolution: "read-tls-client-hello@npm:1.1.0" @@ -29902,6 +30458,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62 + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -29980,6 +30543,13 @@ __metadata: languageName: node linkType: hard +"reflect-metadata@npm:^0.2.2": + version: 0.2.2 + resolution: "reflect-metadata@npm:0.2.2" + checksum: 10c0/1cd93a15ea291e420204955544637c264c216e7aac527470e393d54b4bb075f10a17e60d8168ec96600c7e0b9fcc0cb0bb6e91c3fbf5b0d8c9056f04e6ac1ec2 + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9": version: 1.0.10 resolution: "reflect.getprototypeof@npm:1.0.10" @@ -30123,13 +30693,6 @@ __metadata: languageName: node linkType: hard -"remove-trailing-separator@npm:^1.0.1": - version: 1.1.0 - resolution: "remove-trailing-separator@npm:1.1.0" - checksum: 10c0/3568f9f8f5af3737b4aee9e6e1e8ec4be65a92da9cb27f989e0893714d50aa95ed2ff02d40d1fa35e1b1a234dc9c2437050ef356704a3999feaca6667d9e9bfc - languageName: node - linkType: hard - "renderkid@npm:^3.0.0": version: 3.0.0 resolution: "renderkid@npm:3.0.0" @@ -30224,13 +30787,6 @@ __metadata: languageName: node linkType: hard -"resolve-from@npm:5.0.0, resolve-from@npm:^5.0.0": - version: 5.0.0 - resolution: "resolve-from@npm:5.0.0" - checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 - languageName: node - linkType: hard - "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -30238,6 +30794,13 @@ __metadata: languageName: node linkType: hard +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + "resolve-pkg-maps@npm:^1.0.0": version: 1.0.0 resolution: "resolve-pkg-maps@npm:1.0.0" @@ -30258,7 +30821,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4": +"resolve@npm:^1.1.7, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4": version: 1.22.11 resolution: "resolve@npm:1.22.11" dependencies: @@ -30297,7 +30860,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.17.0#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": version: 1.22.11 resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" dependencies: @@ -30603,8 +31166,9 @@ __metadata: version: 0.0.0-use.local resolution: "root@workspace:." dependencies: - "@backstage/cli": "npm:^0.35.0" - "@backstage/e2e-test-utils": "npm:^0.1.1" + "@backstage/cli": "npm:^0.36.0" + "@backstage/cli-defaults": "npm:^0.1.0" + "@backstage/e2e-test-utils": "npm:^0.1.2" "@jest/environment-jsdom-abstract": "npm:^30.0.0" "@playwright/test": "npm:^1.32.3" "@types/jest": "npm:^30.0.0" @@ -30613,6 +31177,7 @@ __metadata: node-gyp: "npm:^10.0.0" prettier: "npm:^2.3.2" typescript: "npm:~5.8.0" + webpack: "npm:~5.103.0" languageName: unknown linkType: soft @@ -30785,7 +31350,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0": +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: @@ -30796,7 +31361,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^4.0.0, schema-utils@npm:^4.2.0": +"schema-utils@npm:^4.0.0, schema-utils@npm:^4.2.0, schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.3": version: 4.3.3 resolution: "schema-utils@npm:4.3.3" dependencies: @@ -30848,6 +31413,16 @@ __metadata: languageName: node linkType: hard +"selfsigned@npm:^5.5.0": + version: 5.5.0 + resolution: "selfsigned@npm:5.5.0" + dependencies: + "@peculiar/x509": "npm:^1.14.2" + pkijs: "npm:^3.3.3" + checksum: 10c0/a31e9d928e22cd6f4e14759a099feba79d9d789c852c7cf65ff8e2f62d7f6313fe477639590e7ed06115b4516a4bebbe0dec5d072a2d01cc372a9cfd58eb893b + languageName: node + linkType: hard + "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -31061,7 +31636,7 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.7.3, shell-quote@npm:^1.8.3": +"shell-quote@npm:^1.7.3, shell-quote@npm:^1.8.1, shell-quote@npm:^1.8.3": version: 1.8.3 resolution: "shell-quote@npm:1.8.3" checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd @@ -31113,7 +31688,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.4, side-channel@npm:^1.1.0": +"side-channel@npm:^1.1.0": version: 1.1.0 resolution: "side-channel@npm:1.1.0" dependencies: @@ -31266,6 +31841,13 @@ __metadata: languageName: node linkType: hard +"source-list-map@npm:^2.0.0": + version: 2.0.1 + resolution: "source-list-map@npm:2.0.1" + checksum: 10c0/2e5e421b185dcd857f46c3c70e2e711a65d717b78c5f795e2e248c9d67757882ea989b80ebc08cf164eeeda5f4be8aa95d3b990225070b2daaaf3257c5958149 + languageName: node + linkType: hard + "source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" @@ -31314,6 +31896,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.7.3": + version: 0.7.6 + resolution: "source-map@npm:0.7.6" + checksum: 10c0/59f6f05538539b274ba771d2e9e32f6c65451982510564438e048bc1352f019c6efcdc6dd07909b1968144941c14015c2c7d4369fb7c4d7d53ae769716dcc16c + languageName: node + linkType: hard + "space-separated-tokens@npm:^1.0.0": version: 1.1.5 resolution: "space-separated-tokens@npm:1.1.5" @@ -31501,30 +32090,21 @@ __metadata: languageName: node linkType: hard -"static-eval@npm:2.0.2": - version: 2.0.2 - resolution: "static-eval@npm:2.0.2" - dependencies: - escodegen: "npm:^1.8.1" - checksum: 10c0/9bc1114ea5ba2a6978664907c4dd3fde6f58767274f6cb4fbfb11ba3a73cb6e74dc11e89ec4a7bf1472a587c1f976fcd4ab8fe9aae1651f5e576f097745d48ff - languageName: node - linkType: hard - -"statuses@npm:>= 1.5.0 < 2, statuses@npm:^1.5.0, statuses@npm:~1.5.0": +"statuses@npm:>= 1.5.0 < 2, statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: 10c0/e433900956357b3efd79b1c547da4d291799ac836960c016d10a98f6a810b1b5c0dcc13b5a7aa609a58239b5190e1ea176ad9221c2157d2fd1c747393e6b2940 languageName: node linkType: hard -"statuses@npm:~2.0.1, statuses@npm:~2.0.2": +"statuses@npm:^2.0.1, statuses@npm:~2.0.1, statuses@npm:~2.0.2": version: 2.0.2 resolution: "statuses@npm:2.0.2" checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f languageName: node linkType: hard -"stop-iteration-iterator@npm:^1.0.0, stop-iteration-iterator@npm:^1.1.0": +"stop-iteration-iterator@npm:^1.1.0": version: 1.1.0 resolution: "stop-iteration-iterator@npm:1.1.0" dependencies: @@ -31608,13 +32188,6 @@ __metadata: languageName: node linkType: hard -"string-env-interpolation@npm:^1.0.1": - version: 1.0.1 - resolution: "string-env-interpolation@npm:1.0.1" - checksum: 10c0/410046e621e71678e71816377d799b40ba88d236708c0ad015114137fa3575f1b3cf14bfd63ec5eaa35ea43ac582308e60a8e1a3839a10f475b8db73470105bc - languageName: node - linkType: hard - "string-hash@npm:^1.1.1": version: 1.1.3 resolution: "string-hash@npm:1.1.3" @@ -32100,17 +32673,6 @@ __metadata: languageName: node linkType: hard -"sync-fetch@npm:0.6.0-2": - version: 0.6.0-2 - resolution: "sync-fetch@npm:0.6.0-2" - dependencies: - node-fetch: "npm:^3.3.2" - timeout-signal: "npm:^2.0.0" - whatwg-mimetype: "npm:^4.0.0" - checksum: 10c0/1b3e96dfe12de520d9530abb0765baa3ce5921b6fc33ff23171cf838916a58956e755eb359669fba59bfba9b0eefd7e5b6eed737db0ba03bc2cb98a93de5cdb3 - languageName: node - linkType: hard - "synckit@npm:^0.11.8": version: 0.11.12 resolution: "synckit@npm:0.11.12" @@ -32134,7 +32696,7 @@ __metadata: languageName: node linkType: hard -"tapable@npm:^2.0.0": +"tapable@npm:^2.0.0, tapable@npm:^2.2.1, tapable@npm:^2.3.0": version: 2.3.0 resolution: "tapable@npm:2.3.0" checksum: 10c0/cb9d67cc2c6a74dedc812ef3085d9d681edd2c1fa18e4aef57a3c0605fdbe44e6b8ea00bd9ef21bc74dd45314e39d31227aa031ebf2f5e38164df514136f2681 @@ -32194,7 +32756,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.1.11, tar@npm:^6.1.12, tar@npm:^6.2.1": +"tar@npm:^6.1.11, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" dependencies: @@ -32221,6 +32783,19 @@ __metadata: languageName: node linkType: hard +"tar@npm:^7.5.6": + version: 7.5.12 + resolution: "tar@npm:7.5.12" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/3825c5974f5fde792981f47ee9ffea021ee7f4b552b7ab95eeb98e5dfadfd5a5d5861f01fb772e2e5637a41980d3c019fd6cdad1be48b462b886abd7fe0fa17c + languageName: node + linkType: hard + "tarn@npm:^3.0.2": version: 3.0.2 resolution: "tarn@npm:3.0.2" @@ -32250,6 +32825,34 @@ __metadata: languageName: node linkType: hard +"terminal-columns@npm:^2.0.0": + version: 2.0.0 + resolution: "terminal-columns@npm:2.0.0" + checksum: 10c0/b62c9ea709c787178624cc9c328227be9e731a9ee1d61457780063dcf887e6c8f6e3c2c67ec8f7de52eb4f99dd4c228737bfd46092c1bb06818c6f6786d8a5a5 + languageName: node + linkType: hard + +"terser-webpack-plugin@npm:^5.3.11, terser-webpack-plugin@npm:^5.3.17": + version: 5.4.0 + resolution: "terser-webpack-plugin@npm:5.4.0" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.25" + jest-worker: "npm:^27.4.5" + schema-utils: "npm:^4.3.0" + terser: "npm:^5.31.1" + peerDependencies: + webpack: ^5.1.0 + peerDependenciesMeta: + "@swc/core": + optional: true + esbuild: + optional: true + uglify-js: + optional: true + checksum: 10c0/1feed4b9575af795dae6af0c8f0d76d6e1fb7b357b8628d90e834c23a651b918a58cdc48d0ae6c1f0581f74bc8169b33c3b8d049f2d2190bac4e310964e59fde + languageName: node + linkType: hard + "terser@npm:^5.10.0": version: 5.46.0 resolution: "terser@npm:5.46.0" @@ -32264,6 +32867,20 @@ __metadata: languageName: node linkType: hard +"terser@npm:^5.31.1": + version: 5.46.1 + resolution: "terser@npm:5.46.1" + dependencies: + "@jridgewell/source-map": "npm:^0.3.3" + acorn: "npm:^8.15.0" + commander: "npm:^2.20.0" + source-map-support: "npm:~0.5.20" + bin: + terser: bin/terser + checksum: 10c0/45ba6566af976c518ff4e140250348d606761af822e23ea28d95b30fcf60fb69d3fabd93c6fafa4085d9fe31b67207e58b8480a64370b6cca07066c434101602 + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -32362,13 +32979,6 @@ __metadata: languageName: node linkType: hard -"timeout-signal@npm:^2.0.0": - version: 2.0.0 - resolution: "timeout-signal@npm:2.0.0" - checksum: 10c0/dd0a41712552fd45e075664edbdb5d1715a0791e6a206f1d00f5808b954b18046f87b71a7b9216a5030ba772516212b696bbbfb3115bf81b3277b04f62aab135 - languageName: node - linkType: hard - "timers-browserify@npm:^2.0.4": version: 2.0.12 resolution: "timers-browserify@npm:2.0.12" @@ -32708,14 +33318,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.14.1": +"tslib@npm:^1.14.1, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2, tslib@npm:^2.6.3, tslib@npm:^2.7.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2, tslib@npm:^2.7.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -32729,6 +33339,15 @@ __metadata: languageName: node linkType: hard +"tsyringe@npm:^4.10.0": + version: 4.10.0 + resolution: "tsyringe@npm:4.10.0" + dependencies: + tslib: "npm:^1.9.3" + checksum: 10c0/918594b4dfac97beb8be2c041c6ec45f078ef3768ed4edfe35ae2c709ab503e2e6b454b2b37e692c658572d1972a428fbfdbc0a2b42fee727a83c1c685fbe5e1 + languageName: node + linkType: hard + "tty-browserify@npm:0.0.1": version: 0.0.1 resolution: "tty-browserify@npm:0.0.1" @@ -32745,13 +33364,6 @@ __metadata: languageName: node linkType: hard -"tunnel@npm:0.0.6": - version: 0.0.6 - resolution: "tunnel@npm:0.0.6" - checksum: 10c0/e27e7e896f2426c1c747325b5f54efebc1a004647d853fad892b46d64e37591ccd0b97439470795e5262b5c0748d22beb4489a04a0a448029636670bfd801b75 - languageName: node - linkType: hard - "tweetnacl@npm:^0.14.3": version: 0.14.5 resolution: "tweetnacl@npm:0.14.5" @@ -32768,15 +33380,6 @@ __metadata: languageName: node linkType: hard -"type-check@npm:~0.3.2": - version: 0.3.2 - resolution: "type-check@npm:0.3.2" - dependencies: - prelude-ls: "npm:~1.1.2" - checksum: 10c0/776217116b2b4e50e368c7ee0c22c0a85e982881c16965b90d52f216bc296d6a52ef74f9202d22158caacc092a7645b0b8d5fe529a96e3fe35d0fb393966c875 - languageName: node - linkType: hard - "type-detect@npm:4.0.8": version: 4.0.8 resolution: "type-detect@npm:4.0.8" @@ -32812,7 +33415,14 @@ __metadata: languageName: node linkType: hard -"type-is@npm:^1.6.16, type-is@npm:^1.6.18, type-is@npm:~1.6.18": +"type-flag@npm:^4.1.0": + version: 4.1.0 + resolution: "type-flag@npm:4.1.0" + checksum: 10c0/6dd0968769f3b261509f2e58d3339fde73789666d586e2e6c36eca44a6702e37499a1d2b8aeee1aae6a85c259f15770a5683ffb905bd8fb4026f280401927ae9 + languageName: node + linkType: hard + +"type-is@npm:^1.6.18, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" dependencies: @@ -32822,6 +33432,17 @@ __metadata: languageName: node linkType: hard +"type-is@npm:^2.0.1": + version: 2.0.1 + resolution: "type-is@npm:2.0.1" + dependencies: + content-type: "npm:^1.0.5" + media-typer: "npm:^1.1.0" + mime-types: "npm:^3.0.0" + checksum: 10c0/7f7ec0a060b16880bdad36824ab37c26019454b67d73e8a465ed5a3587440fbe158bc765f0da68344498235c877e7dbbb1600beccc94628ed05599d667951b99 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-buffer@npm:1.0.3" @@ -32882,19 +33503,6 @@ __metadata: languageName: node linkType: hard -"typed-rest-client@npm:2.1.0": - version: 2.1.0 - resolution: "typed-rest-client@npm:2.1.0" - dependencies: - des.js: "npm:^1.1.0" - js-md4: "npm:^0.3.2" - qs: "npm:^6.10.3" - tunnel: "npm:0.0.6" - underscore: "npm:^1.12.1" - checksum: 10c0/b9d29db5217b6d3d0ae9aa68e87e84be8c2d885e7a932f4df3eca070bb615ded5f390035f26857996911803830d28ba2296d6cb748072dbc6d8657916107132d - languageName: node - linkType: hard - "typedarray@npm:^0.0.6": version: 0.0.6 resolution: "typedarray@npm:0.0.6" @@ -32970,13 +33578,6 @@ __metadata: languageName: node linkType: hard -"uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5": - version: 1.0.6 - resolution: "uc.micro@npm:1.0.6" - checksum: 10c0/9bde2afc6f2e24b899db6caea47dae778b88862ca76688d844ef6e6121dec0679c152893a74a6cfbd2e6fde34654e6bd8424fee8e0166cdfa6c9ae5d42b8a17b - languageName: node - linkType: hard - "uc.micro@npm:^2.0.0, uc.micro@npm:^2.1.0": version: 2.1.0 resolution: "uc.micro@npm:2.1.0" @@ -33021,14 +33622,7 @@ __metadata: languageName: node linkType: hard -"underscore@npm:1.12.1": - version: 1.12.1 - resolution: "underscore@npm:1.12.1" - checksum: 10c0/00f392357e363353ac485e7c156b749505087e31ff4fdad22e04ebd2f94a56fbc554cd41a6722e3895a818466cf298b1cae93ff6211d102d373a9b50db63bfd0 - languageName: node - linkType: hard - -"underscore@npm:^1.12.1, underscore@npm:^1.13.3": +"underscore@npm:^1.13.3": version: 1.13.7 resolution: "underscore@npm:1.13.7" checksum: 10c0/fad2b4aac48847674aaf3c30558f383399d4fdafad6dd02dd60e4e1b8103b52c5a9e5937e0cc05dacfd26d6a0132ed0410ab4258241240757e4a4424507471cd @@ -33202,15 +33796,6 @@ __metadata: languageName: node linkType: hard -"unixify@npm:^1.0.0": - version: 1.0.0 - resolution: "unixify@npm:1.0.0" - dependencies: - normalize-path: "npm:^2.1.1" - checksum: 10c0/8b89100619ebde9f0ab4024a4d402316fb7b1d4853723410fc828944e8d3d01480f210cddf94d9a1699559f8180d861eb6323da8011b7bcc1bbaf6a11a5b1f1e - languageName: node - linkType: hard - "unpipe@npm:~1.0.0": version: 1.0.0 resolution: "unpipe@npm:1.0.0" @@ -33323,15 +33908,6 @@ __metadata: languageName: node linkType: hard -"upper-case@npm:^2.0.2": - version: 2.0.2 - resolution: "upper-case@npm:2.0.2" - dependencies: - tslib: "npm:^2.0.3" - checksum: 10c0/5ac176c9d3757abb71400df167f9abb46d63152d5797c630d1a9f083fbabd89711fb4b3dc6de06ff0138fe8946fa5b8518b4fcdae9ca8a3e341417075beae069 - languageName: node - linkType: hard - "uri-js@npm:^4.2.2, uri-js@npm:^4.4.1": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -33367,13 +33943,6 @@ __metadata: languageName: node linkType: hard -"url-template@npm:^2.0.8": - version: 2.0.8 - resolution: "url-template@npm:2.0.8" - checksum: 10c0/56a15057eacbcf05d52b0caed8279c8451b3dd9d32856a1fdd91c6dc84dcb1646f12bafc756b7ade62ca5b1564da8efd7baac5add35868bafb43eb024c62805b - languageName: node - linkType: hard - "url@npm:^0.11.4": version: 0.11.4 resolution: "url@npm:0.11.4" @@ -33403,13 +33972,6 @@ __metadata: languageName: node linkType: hard -"urlpattern-polyfill@npm:^10.0.0": - version: 10.1.0 - resolution: "urlpattern-polyfill@npm:10.1.0" - checksum: 10c0/5b124fd8d0ae920aa2a48b49a7a3b9ad1643b5ce7217b808fb6877826e751cabc01897fd4c85cd1989c4e729072b63aad5c3ba1c1325e4433e0d2f6329156bf1 - languageName: node - linkType: hard - "urlpattern-polyfill@npm:^8.0.0": version: 8.0.2 resolution: "urlpattern-polyfill@npm:8.0.2" @@ -33750,6 +34312,16 @@ __metadata: languageName: node linkType: hard +"watchpack@npm:^2.4.4, watchpack@npm:^2.5.1": + version: 2.5.1 + resolution: "watchpack@npm:2.5.1" + dependencies: + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.1.2" + checksum: 10c0/dffbb483d1f61be90dc570630a1eb308581e2227d507d783b1d94a57ac7b705ecd9a1a4b73d73c15eab596d39874e5276a3d9cb88bbb698bafc3f8d08c34cf17 + languageName: node + linkType: hard + "wbuf@npm:^1.1.0, wbuf@npm:^1.7.3": version: 1.7.3 resolution: "wbuf@npm:1.7.3" @@ -33841,6 +34413,144 @@ __metadata: languageName: node linkType: hard +"webpack-dev-server@npm:^5.0.0": + version: 5.2.3 + resolution: "webpack-dev-server@npm:5.2.3" + dependencies: + "@types/bonjour": "npm:^3.5.13" + "@types/connect-history-api-fallback": "npm:^1.5.4" + "@types/express": "npm:^4.17.25" + "@types/express-serve-static-core": "npm:^4.17.21" + "@types/serve-index": "npm:^1.9.4" + "@types/serve-static": "npm:^1.15.5" + "@types/sockjs": "npm:^0.3.36" + "@types/ws": "npm:^8.5.10" + ansi-html-community: "npm:^0.0.8" + bonjour-service: "npm:^1.2.1" + chokidar: "npm:^3.6.0" + colorette: "npm:^2.0.10" + compression: "npm:^1.8.1" + connect-history-api-fallback: "npm:^2.0.0" + express: "npm:^4.22.1" + graceful-fs: "npm:^4.2.6" + http-proxy-middleware: "npm:^2.0.9" + ipaddr.js: "npm:^2.1.0" + launch-editor: "npm:^2.6.1" + open: "npm:^10.0.3" + p-retry: "npm:^6.2.0" + schema-utils: "npm:^4.2.0" + selfsigned: "npm:^5.5.0" + serve-index: "npm:^1.9.1" + sockjs: "npm:^0.3.24" + spdy: "npm:^4.0.2" + webpack-dev-middleware: "npm:^7.4.2" + ws: "npm:^8.18.0" + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + bin: + webpack-dev-server: bin/webpack-dev-server.js + checksum: 10c0/a716f1d509635ad9f2779baf242657740e6ad516ce210fe094cbf3b16f25f114e477c45a751ad2bbf1c601cbbe67b6ba9b8b43159b7c01fc3342c95b985fe963 + languageName: node + linkType: hard + +"webpack-sources@npm:^1.4.3": + version: 1.4.3 + resolution: "webpack-sources@npm:1.4.3" + dependencies: + source-list-map: "npm:^2.0.0" + source-map: "npm:~0.6.1" + checksum: 10c0/78dafb3e1e297d3f4eb6204311e8c64d28cd028f82887ba33aaf03fffc82482d8e1fdf6de25a60f4dde621d3565f4c3b1bfb350f09add8f4e54e00279ff3db5e + languageName: node + linkType: hard + +"webpack-sources@npm:^3.3.3, webpack-sources@npm:^3.3.4": + version: 3.3.4 + resolution: "webpack-sources@npm:3.3.4" + checksum: 10c0/94a42508531338eb41939cf1d48a4a8a6db97f3a47e5453cff2133a68d3169ca779d4bcbe9dfed072ce16611959eba1e16f085bc2dc56714e1a1c1783fd661a3 + languageName: node + linkType: hard + +"webpack@npm:~5.103.0": + version: 5.103.0 + resolution: "webpack@npm:5.103.0" + dependencies: + "@types/eslint-scope": "npm:^3.7.7" + "@types/estree": "npm:^1.0.8" + "@types/json-schema": "npm:^7.0.15" + "@webassemblyjs/ast": "npm:^1.14.1" + "@webassemblyjs/wasm-edit": "npm:^1.14.1" + "@webassemblyjs/wasm-parser": "npm:^1.14.1" + acorn: "npm:^8.15.0" + acorn-import-phases: "npm:^1.0.3" + browserslist: "npm:^4.26.3" + chrome-trace-event: "npm:^1.0.2" + enhanced-resolve: "npm:^5.17.3" + es-module-lexer: "npm:^1.2.1" + eslint-scope: "npm:5.1.1" + events: "npm:^3.2.0" + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.2.11" + json-parse-even-better-errors: "npm:^2.3.1" + loader-runner: "npm:^4.3.1" + mime-types: "npm:^2.1.27" + neo-async: "npm:^2.6.2" + schema-utils: "npm:^4.3.3" + tapable: "npm:^2.3.0" + terser-webpack-plugin: "npm:^5.3.11" + watchpack: "npm:^2.4.4" + webpack-sources: "npm:^3.3.3" + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 10c0/d0cf86f8cac249874d6f36292e25011413ebb5bae82c48fa78a165a217e63db00b1a1f563f5195070eb17a055c6da4b6ab89fbdd37f781abdda862aa8c0bd623 + languageName: node + linkType: hard + +"webpack@npm:~5.105.0": + version: 5.105.4 + resolution: "webpack@npm:5.105.4" + dependencies: + "@types/eslint-scope": "npm:^3.7.7" + "@types/estree": "npm:^1.0.8" + "@types/json-schema": "npm:^7.0.15" + "@webassemblyjs/ast": "npm:^1.14.1" + "@webassemblyjs/wasm-edit": "npm:^1.14.1" + "@webassemblyjs/wasm-parser": "npm:^1.14.1" + acorn: "npm:^8.16.0" + acorn-import-phases: "npm:^1.0.3" + browserslist: "npm:^4.28.1" + chrome-trace-event: "npm:^1.0.2" + enhanced-resolve: "npm:^5.20.0" + es-module-lexer: "npm:^2.0.0" + eslint-scope: "npm:5.1.1" + events: "npm:^3.2.0" + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.2.11" + json-parse-even-better-errors: "npm:^2.3.1" + loader-runner: "npm:^4.3.1" + mime-types: "npm:^2.1.27" + neo-async: "npm:^2.6.2" + schema-utils: "npm:^4.3.3" + tapable: "npm:^2.3.0" + terser-webpack-plugin: "npm:^5.3.17" + watchpack: "npm:^2.5.1" + webpack-sources: "npm:^3.3.4" + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 10c0/e9896d20bac351b119d59942b7efae5b117056ecf203acc0d1a84ecbf0a5a9a80ca733735f96bd163e3530be6ab7f615cd67e5320bd3c47d709c9bfe376c3280 + languageName: node + linkType: hard + "websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": version: 0.7.4 resolution: "websocket-driver@npm:0.7.4" @@ -33893,7 +34603,7 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": +"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" dependencies: @@ -33927,7 +34637,7 @@ __metadata: languageName: node linkType: hard -"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": +"which-collection@npm:^1.0.2": version: 1.0.2 resolution: "which-collection@npm:1.0.2" dependencies: @@ -33939,7 +34649,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2": +"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2": version: 1.1.20 resolution: "which-typed-array@npm:1.1.20" dependencies: @@ -34037,7 +34747,7 @@ __metadata: languageName: node linkType: hard -"word-wrap@npm:^1.2.5, word-wrap@npm:~1.2.3": +"word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20 @@ -34101,7 +34811,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:*, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.18.2, ws@npm:^8.18.3, ws@npm:^8.19.0, ws@npm:^8.8.0": +"ws@npm:*, ws@npm:^8.18.0, ws@npm:^8.18.2, ws@npm:^8.18.3, ws@npm:^8.8.0": version: 8.19.0 resolution: "ws@npm:8.19.0" peerDependencies: @@ -34140,13 +34850,6 @@ __metadata: languageName: node linkType: hard -"xcase@npm:^2.0.1": - version: 2.0.1 - resolution: "xcase@npm:2.0.1" - checksum: 10c0/11b8ae8f6734b29d442a5acf1dff3a896cabbf49e7ffa01472ff6fa687a6e6f6a25889d06c10a41950e7a90fe89239fa78d95eab0c5eb654ca75f0ccd71ba8ed - languageName: node - linkType: hard - "xml-but-prettier@npm:^1.0.1": version: 1.0.1 resolution: "xml-but-prettier@npm:1.0.1" @@ -34272,17 +34975,17 @@ __metadata: languageName: node linkType: hard -"yauzl@npm:^3.0.0": - version: 3.2.0 - resolution: "yauzl@npm:3.2.0" +"yauzl@npm:^3.2.1": + version: 3.2.1 + resolution: "yauzl@npm:3.2.1" dependencies: buffer-crc32: "npm:~0.2.3" pend: "npm:~1.2.0" - checksum: 10c0/7b40b3dc46b95761a2a764391d257a11f494d365875af73a1b48fe16d4bd103dd178612e60168d12a0e59a8ba4f6411a15a5e8871d5a5f78255d6cc1ce39ee62 + checksum: 10c0/fe1997a8ee53c42556789ca61a28278e9ea2ca8f68cae1ae9904edaf3b5fbbc5d5537a3fb8623ac82c2bd0bd9d67233ce593f1acaadfb8718489d9f06d1bb3d0 languageName: node linkType: hard -"ylru@npm:^1.2.0, ylru@npm:^1.3.2": +"ylru@npm:^1.3.2": version: 1.4.0 resolution: "ylru@npm:1.4.0" checksum: 10c0/eaadc38ed6d78d4fda49abed45cfdaf149bd334df761dbeadd3cff62936d25ffa94571f84c25b64a9a4b5efd8f489ee6fee3eaaf8e7b2886418a3bcb9ec84b84 @@ -34345,7 +35048,7 @@ __metadata: languageName: node linkType: hard -"zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4, zod-to-json-schema@npm:^3.25.1": +"zod-to-json-schema@npm:^3.21.4, zod-to-json-schema@npm:^3.25.1": version: 3.25.1 resolution: "zod-to-json-schema@npm:3.25.1" peerDependencies: @@ -34363,6 +35066,15 @@ __metadata: languageName: node linkType: hard +"zod-validation-error@npm:^5.0.0": + version: 5.0.0 + resolution: "zod-validation-error@npm:5.0.0" + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + checksum: 10c0/1f137003dad6da34e954e31f270ea8bf2cc6e29dbb9b03858951f6498a7f728b935ca66782514b2fca6485750844cef4633ce1acbd17ad3dde359a2ad6b22973 + languageName: node + linkType: hard + "zod@npm:^3.22.4, zod@npm:^3.25.76": version: 3.25.76 resolution: "zod@npm:3.25.76" @@ -34370,6 +35082,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.25.76 || ^4.0.0": + version: 4.3.6 + resolution: "zod@npm:4.3.6" + checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307 + languageName: node + linkType: hard + "zstd-codec@npm:^0.1.5": version: 0.1.5 resolution: "zstd-codec@npm:0.1.5" diff --git a/docs/APP_STARTUP_GUIDE.md b/docs/APP_STARTUP_GUIDE.md index c0fa5c8..ffdb29d 100644 --- a/docs/APP_STARTUP_GUIDE.md +++ b/docs/APP_STARTUP_GUIDE.md @@ -81,7 +81,7 @@ The sections below describe each step that `task setup` performs automatically. Ensure you have the following installed (run `task check` to verify): -- **Go** >= 1.24 ([install](https://go.dev/dl/)) +- **Go** >= 1.26 ([install](https://go.dev/dl/)) - **Docker** ([install](https://docs.docker.com/get-docker/)) - **kubectl** ([install](https://kubernetes.io/docs/tasks/tools/)) - **k3d** ([install](https://k3d.io/) or `curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash`) diff --git a/scripts/check-prereqs.sh b/scripts/check-prereqs.sh index f23f738..69182aa 100755 --- a/scripts/check-prereqs.sh +++ b/scripts/check-prereqs.sh @@ -30,6 +30,49 @@ version_gte() { printf '%s\n%s' "$2" "$1" | sort -t. -k1,1n -k2,2n -k3,3n -C } +trim_ws() { + local s="$1" + s="${s#"${s%%[![:space:]]*}"}" + s="${s%"${s##*[![:space:]]}"}" + printf '%s' "$s" +} + +read_env_value() { + local env_file="$1" + local wanted_key="$2" + local line key value + + while IFS= read -r line || [[ -n "$line" ]]; do + line="$(trim_ws "$line")" + [[ -z "$line" || "${line:0:1}" == "#" ]] && continue + + if [[ "$line" == export\ * ]]; then + line="${line#export }" + line="$(trim_ws "$line")" + fi + + [[ "$line" != *=* ]] && continue + + key="$(trim_ws "${line%%=*}")" + value="$(trim_ws "${line#*=}")" + + if [[ "$key" != "$wanted_key" ]]; then + continue + fi + + if [[ "$value" == \"*\" && "$value" == *\" ]]; then + value="${value:1:${#value}-2}" + elif [[ "$value" == \'*\' && "$value" == *\' ]]; then + value="${value:1:${#value}-2}" + fi + + printf '%s' "$value" + return 0 + done < "$env_file" + + return 1 +} + # --------------------------------------------------------------------------- # Check a single binary # $1 = binary name @@ -67,29 +110,29 @@ echo -e "\n${BOLD}Helios Platform - Prerequisite Check${NC}\n" echo "----------------------------------------------" echo -e "\n${BOLD}Core Tools${NC}" -check_tool "go" "1.24" \ - "go version | grep -oP 'go\K[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1" \ +check_tool "go" "1.26" \ + "go version | awk '{print \$3}' | sed -E 's/^go//' | head -1" \ "https://go.dev/dl/" check_tool "docker" "" \ - "docker --version | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1" \ + "docker --version | sed -E 's/.* ([0-9]+\.[0-9]+\.[0-9]+).*/\\1/' | head -1" \ "https://docs.docker.com/get-docker/" check_tool "kubectl" "" \ - "kubectl version --client -o json 2>/dev/null | grep -oP '\"gitVersion\":\\s*\"v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1" \ + "kubectl version --client -o json 2>/dev/null | sed -n -E 's/.*\"gitVersion\"[[:space:]]*:[[:space:]]*\"v([^\"]+)\".*/\\1/p' | head -1" \ "https://kubernetes.io/docs/tasks/tools/" check_tool "k3d" "" \ - "k3d version | head -1 | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+'" \ + "k3d version | head -1 | sed -n -E 's/.*v([0-9]+\.[0-9]+\.[0-9]+).*/\\1/p'" \ "https://k3d.io/ or: curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash" check_tool "cue" "" \ - "cue version | head -1 | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+'" \ + "cue version | head -1 | sed -n -E 's/.*v([0-9]+\.[0-9]+\.[0-9]+).*/\\1/p'" \ "go install cuelang.org/go/cmd/cue@latest" echo -e "\n${BOLD}Node.js / Frontend${NC}" check_tool "node" "22.0.0" \ - "node --version | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+'" \ + "node --version | sed -E 's/^v//'" \ "https://nodejs.org/ or use nvm: nvm install 22" check_tool "yarn" "" \ @@ -130,10 +173,9 @@ if $CHECK_ENV; then pass ".env file exists" REQUIRED_VARS=(GITHUB_TOKEN GITHUB_USER AUTH_GITHUB_CLIENT_ID AUTH_GITHUB_CLIENT_SECRET) - set -a; source "$ENV_FILE"; set +a for var in "${REQUIRED_VARS[@]}"; do - val="${!var:-}" + val="$(read_env_value "$ENV_FILE" "$var" || true)" if [[ -z "$val" || "$val" == ghp_xxxx* || "$val" == "your-"* ]]; then fail "$var is not set (or still has placeholder value) in .env" else diff --git a/scripts/start-portal.ps1 b/scripts/start-portal.ps1 index 4954fc7..aebf1f8 100644 --- a/scripts/start-portal.ps1 +++ b/scripts/start-portal.ps1 @@ -8,23 +8,30 @@ try { $pass = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($passB64)).Trim() $body = @{ username = "admin"; password = $pass } | ConvertTo-Json -Compress - - # PowerShell 5.1 compatibility for skipping SSL checks - [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } - - $parsed = Invoke-RestMethod -Uri "https://127.0.0.1:${ArgocdPort}/api/v1/session" ` - -Method Post ` - -ContentType "application/json" ` - -Body $body + + # Limit TLS bypass scope to the local ArgoCD login request. + $previousValidationCallback = [System.Net.ServicePointManager]::ServerCertificateValidationCallback + try { + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } + + $parsed = Invoke-RestMethod -Uri "https://127.0.0.1:${ArgocdPort}/api/v1/session" ` + -Method Post ` + -ContentType "application/json" ` + -Body $body + } + finally { + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $previousValidationCallback + } + if ($parsed.token) { $env:ARGOCD_AUTH_TOKEN = $parsed.token - Write-Host "ArgoCD token generated." + Write-Output "ArgoCD token generated." } else { - Write-Host "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." + Write-Warning "Could not generate ArgoCD token. ArgoCD features may not work." } } catch { - Write-Host "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." - Write-Host "Error: $_" + Write-Warning "Could not generate ArgoCD token. ArgoCD features may not work." + Write-Error "ArgoCD token request failed: $_" } # Start Backstage From df39c0dfea9637c439c8919e807dc798140fb753 Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 22 Mar 2026 21:21:10 +0700 Subject: [PATCH 16/19] fix: harden operator runtime and address CodeRabbit follow-ups - include CUE definitions in operator image and fix docker build context for e2e - harden ArgoCD DB_* diff ignore with null-safe jq expression - add DB_PORT injection and storage-drift detection in database reconciliation with tests - improve Taskfile and shell robustness (shared env helpers, safer SA patching, jq token fallback, pipefail) - tighten scaffolder template and validation defaults plus React type pinning - pin workflow action refs to immutable SHAs and clean docs whitespace --- Taskfile.yml | 16 +- apps/operator/.github/workflows/lint.yml | 6 +- apps/operator/Dockerfile | 16 +- apps/operator/Makefile | 2 +- .../internal/controller/argocd_resources.go | 2 +- .../internal/controller/database_resources.go | 77 ++++++++- .../controller/database_resources_test.go | 150 ++++++++++++++++-- .../examples/advanced-template/template.yaml | 5 + .../source/src/prisma/prisma.service.ts | 2 + .../nestjs-prisma-template/template.yaml | 5 + apps/portal/packages/app/package.json | 4 +- .../DatabasePicker.tsx | 4 +- .../DatabasePickerExtension/extension.ts | 4 +- apps/portal/start-dev.sh | 46 +----- apps/portal/yarn.lock | 4 +- docs/APP_STARTUP_GUIDE.md | 2 +- scripts/check-prereqs.sh | 47 +----- scripts/lib/env_helpers.sh | 44 +++++ 18 files changed, 308 insertions(+), 128 deletions(-) create mode 100644 scripts/lib/env_helpers.sh diff --git a/Taskfile.yml b/Taskfile.yml index 85d1496..fcb645d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,4 +1,4 @@ -# ============================================================================= +# ============================================================================= # Helios Platform - Root Taskfile # ============================================================================= # Usage: @@ -146,9 +146,11 @@ tasks: --docker-email=${DOCKER_EMAIL:-dev@helios.io} --dry-run=client -o yaml | kubectl apply -f - - >- - kubectl patch sa pipeline - -p '{"secrets": [{"name": "docker-credentials"}]}' - 2>/dev/null || true + if kubectl get sa pipeline >/dev/null 2>&1; then + kubectl patch sa pipeline -p '{"secrets": [{"name": "docker-credentials"}]}' + else + echo "pipeline ServiceAccount not found yet; skipping patch (will be created by Tekton)" + fi setup:portal-deps: desc: Install Backstage portal dependencies @@ -243,7 +245,11 @@ tasks: -H "Content-Type: application/json" \ -d "{\"username\":\"admin\",\"password\":\"$ARGOCD_PASS\"}" \ https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session) - export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | sed 's/.*"token":"\([^"]*\)".*/\1/') + if command -v jq >/dev/null 2>&1; then + export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | jq -r '.token // empty') + else + export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | sed 's/.*"token":"\([^"]*\)".*/\1/') + fi if [ -z "$ARGOCD_AUTH_TOKEN" ] || [ "${#ARGOCD_AUTH_TOKEN}" -lt 20 ]; then echo "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." else diff --git a/apps/operator/.github/workflows/lint.yml b/apps/operator/.github/workflows/lint.yml index 330765d..b366484 100644 --- a/apps/operator/.github/workflows/lint.yml +++ b/apps/operator/.github/workflows/lint.yml @@ -10,14 +10,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 with: go-version-file: go.mod - name: Run linter - uses: golangci/golangci-lint-action@v9 + uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 with: version: v2.11.3 diff --git a/apps/operator/Dockerfile b/apps/operator/Dockerfile index 86e798a..3d8cf14 100644 --- a/apps/operator/Dockerfile +++ b/apps/operator/Dockerfile @@ -3,18 +3,19 @@ FROM golang:1.26 AS builder ARG TARGETOS ARG TARGETARCH -WORKDIR /workspace +WORKDIR /workspace/apps/operator # Copy the Go Modules manifests -COPY go.mod go.mod -COPY go.sum go.sum +COPY apps/operator/go.mod go.mod +COPY apps/operator/go.sum go.sum # cache deps before building and copying source so that we don't need to re-download as much # and so that source changes don't invalidate our downloaded layer RUN go mod download # Copy the go source -COPY cmd/main.go cmd/main.go -COPY api/ api/ -COPY internal/ internal/ +COPY apps/operator/cmd/main.go cmd/main.go +COPY apps/operator/api/ api/ +COPY apps/operator/internal/ internal/ +COPY cue/ /workspace/cue/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command @@ -27,7 +28,8 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o ma # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot WORKDIR / -COPY --from=builder /workspace/manager . +COPY --from=builder /workspace/apps/operator/manager . +COPY --from=builder /workspace/cue /etc/helios/cue USER 65532:65532 ENTRYPOINT ["/manager"] diff --git a/apps/operator/Makefile b/apps/operator/Makefile index 2decfd1..d526f27 100644 --- a/apps/operator/Makefile +++ b/apps/operator/Makefile @@ -168,7 +168,7 @@ run: manifests generate fmt vet ## Run a controller from your host. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build docker-build: ## Build docker image with the manager. - $(CONTAINER_TOOL) build -t ${IMG} . + $(CONTAINER_TOOL) build -t ${IMG} -f ../../apps/operator/Dockerfile ../.. .PHONY: docker-push docker-push: ## Push docker image with the manager. diff --git a/apps/operator/internal/controller/argocd_resources.go b/apps/operator/internal/controller/argocd_resources.go index f68cfe8..7fc9d00 100644 --- a/apps/operator/internal/controller/argocd_resources.go +++ b/apps/operator/internal/controller/argocd_resources.go @@ -52,7 +52,7 @@ func GenerateArgoApplication(heliosApp *appv1alpha1.HeliosApp) (*unstructured.Un "group": "apps", "kind": "Deployment", "jqPathExpressions": []any{ - `.spec.template.spec.containers[].env[] | select(.name == "DB_HOST" or .name == "DB_USER" or .name == "DB_PASS")`, + `.spec.template.spec.containers[].env[]? | select(.name | test("^DB_"))`, }, }, }, diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index b2cd32c..7180ad8 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -13,6 +13,7 @@ import ( "encoding/json" "fmt" "math/big" + "strconv" "strings" appsv1 "k8s.io/api/apps/v1" @@ -404,6 +405,20 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app "component", dbTrait.ComponentName, "statefulset", dbHost) + currentStorage, storageErr := getCurrentStorageRequest(existingSts) + if storageErr != nil { + return fmt.Errorf("failed to read StatefulSet %s storage: %w", dbHost, storageErr) + } + + desiredStorageQty, parseErr := resource.ParseQuantity(storage) + if parseErr != nil { + return fmt.Errorf("invalid desired storage for StatefulSet %s: %w", dbHost, parseErr) + } + + if currentStorage.Cmp(desiredStorageQty) != 0 { + return fmt.Errorf("database storage drift detected for %s: current=%s desired=%s; StatefulSet volume expansion is not handled automatically", dbHost, currentStorage.String(), desiredStorageQty.String()) + } + // We only update the mutable fields (Replicas, Template) updatedSts := existingSts.DeepCopy() updatedSts.Spec.Replicas = sts.Spec.Replicas @@ -467,9 +482,8 @@ func (r *HeliosAppReconciler) reconcileDatabaseInstance(ctx context.Context, app return nil } -// databaseEnvVarNames lists the env var names injected by the operator -// for database credential secret injection. -var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} +// databaseSecretEnvVarNames lists env vars resolved from Secret keys. +var databaseSecretEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} // InjectDatabaseEnvVars patches a Deployment's first container to include // DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. @@ -478,15 +492,16 @@ var databaseEnvVarNames = []string{"DB_HOST", "DB_USER", "DB_PASS"} // If an env var exists but points to a different source (wrong secret, plain // value, etc.), it is updated to the expected secretKeyRef. func InjectDatabaseEnvVars(deploy *appsv1.Deployment, secretName string) bool { - changed, _ := InjectDatabaseEnvVarsForContainer(deploy, secretName, "") + changed, _ := InjectDatabaseEnvVarsForContainer(deploy, secretName, "", DefaultPostgresPort) return changed } // InjectDatabaseEnvVarsForContainer patches a Deployment container to include -// DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret. +// DB_HOST, DB_USER, DB_PASS env vars referencing the given K8s Secret, plus a +// literal DB_PORT value. // If preferredContainerName is not found, it falls back to the first container. // Returns (changed, exactMatch). -func InjectDatabaseEnvVarsForContainer(deploy *appsv1.Deployment, secretName, preferredContainerName string) (bool, bool) { +func InjectDatabaseEnvVarsForContainer(deploy *appsv1.Deployment, secretName, preferredContainerName string, dbPort int32) (bool, bool) { if len(deploy.Spec.Template.Spec.Containers) == 0 { return false, false } @@ -499,7 +514,7 @@ func InjectDatabaseEnvVarsForContainer(deploy *appsv1.Deployment, secretName, pr container := &deploy.Spec.Template.Spec.Containers[containerIndex] changed := false - for _, envName := range databaseEnvVarNames { + for _, envName := range databaseSecretEnvVarNames { expectedRef := &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ @@ -540,6 +555,30 @@ func InjectDatabaseEnvVarsForContainer(deploy *appsv1.Deployment, secretName, pr } } + portValue := strconv.FormatInt(int64(dbPort), 10) + foundDBPort := false + for i := range container.Env { + if container.Env[i].Name != "DB_PORT" { + continue + } + foundDBPort = true + if container.Env[i].Value == portValue && container.Env[i].ValueFrom == nil { + break + } + container.Env[i].Value = portValue + container.Env[i].ValueFrom = nil + changed = true + break + } + + if !foundDBPort { + container.Env = append(container.Env, corev1.EnvVar{ + Name: "DB_PORT", + Value: portValue, + }) + changed = true + } + return changed, exactMatch } @@ -610,8 +649,13 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte return false, fmt.Errorf("failed to get Deployment %s: %w", deployName, err) } + port := dbTrait.Properties.Port + if port <= 0 { + port = DefaultPostgresPort + } + // Inject env vars if not already present. - changed, exactContainerMatch := InjectDatabaseEnvVarsForContainer(deploy, secretName, dbTrait.ComponentName) + changed, exactContainerMatch := InjectDatabaseEnvVarsForContainer(deploy, secretName, dbTrait.ComponentName, int32(port)) if !exactContainerMatch { log.Info("Preferred application container not found, using first container for DB env injection", "component", dbTrait.ComponentName, @@ -790,6 +834,23 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s }, nil } +func getCurrentStorageRequest(sts *appsv1.StatefulSet) (resource.Quantity, error) { + if sts == nil { + return resource.Quantity{}, fmt.Errorf("statefulset is nil") + } + + if len(sts.Spec.VolumeClaimTemplates) == 0 { + return resource.Quantity{}, fmt.Errorf("no volumeClaimTemplates found") + } + + qty, ok := sts.Spec.VolumeClaimTemplates[0].Spec.Resources.Requests[corev1.ResourceStorage] + if !ok { + return resource.Quantity{}, fmt.Errorf("storage request is missing") + } + + return qty, nil +} + // ValidateDatabaseSecret ensures an existing database credential Secret has all // required keys and the expected DB_HOST value. func ValidateDatabaseSecret(secret *corev1.Secret, expectedHost string) error { diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 14115d0..88b5827 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -7,6 +7,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -952,6 +953,117 @@ func TestReconcileDatabaseInstance(t *testing.T) { t.Errorf("Expected no StatefulSets for redis type, got %d", len(stsList.Items)) } }) + + t.Run("UpdatesExistingStatefulSetAndService", func(t *testing.T) { + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + existingSts := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "api-server-db", Namespace: app.Namespace}, + Spec: appsv1.StatefulSetSpec{ + Replicas: func() *int32 { r := int32(1); return &r }(), + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "postgres", + Image: "postgres:15", + }}, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{{ + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("2Gi"), + }, + }, + }, + }}, + }, + } + + existingSvc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "api-server-db", Namespace: app.Namespace}, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Name: "db", Port: 15432}}, + }, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(app, existingSts, existingSvc). + Build() + + r := &HeliosAppReconciler{Client: fakeClient, Scheme: fullScheme} + + ctx := t.Context() + err := r.reconcileDatabaseInstance(ctx, app) + if err != nil { + t.Fatalf("reconcileDatabaseInstance failed: %v", err) + } + + updatedSts := &appsv1.StatefulSet{} + err = fakeClient.Get(ctx, types.NamespacedName{Name: "api-server-db", Namespace: app.Namespace}, updatedSts) + if err != nil { + t.Fatalf("failed to get updated StatefulSet: %v", err) + } + if got := updatedSts.Spec.Template.Spec.Containers[0].Image; got != "postgres:16" { + t.Fatalf("expected image postgres:16, got %s", got) + } + + updatedSvc := &corev1.Service{} + err = fakeClient.Get(ctx, types.NamespacedName{Name: "api-server-db", Namespace: app.Namespace}, updatedSvc) + if err != nil { + t.Fatalf("failed to get updated Service: %v", err) + } + if got := updatedSvc.Spec.Ports[0].Port; got != 5432 { + t.Fatalf("expected service port 5432, got %d", got) + } + }) + + t.Run("FailsOnStatefulSetStorageDrift", func(t *testing.T) { + fullScheme := runtime.NewScheme() + _ = corev1.AddToScheme(fullScheme) + _ = appv1alpha1.AddToScheme(fullScheme) + _ = appsv1.AddToScheme(fullScheme) + + existingSts := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "api-server-db", Namespace: app.Namespace}, + Spec: appsv1.StatefulSetSpec{ + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{{ + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + }}, + }, + } + + existingSvc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "api-server-db", Namespace: app.Namespace}, + Spec: corev1.ServiceSpec{Ports: []corev1.ServicePort{{Name: "db", Port: 5432}}}, + } + + fakeClient := fake.NewClientBuilder(). + WithScheme(fullScheme). + WithObjects(app, existingSts, existingSvc). + Build() + + r := &HeliosAppReconciler{Client: fakeClient, Scheme: fullScheme} + + err := r.reconcileDatabaseInstance(t.Context(), app) + if err == nil { + t.Fatal("expected storage drift error, got nil") + } + if !strings.Contains(err.Error(), "storage drift detected") { + t.Fatalf("expected storage drift error, got %v", err) + } + }) } func TestInjectDatabaseEnvVars(t *testing.T) { @@ -980,9 +1092,9 @@ func TestInjectDatabaseEnvVars(t *testing.T) { } container := deploy.Spec.Template.Spec.Containers[0] - // Should have PORT + DB_HOST + DB_USER + DB_PASS = 4 - if len(container.Env) != 4 { - t.Fatalf("Expected 4 env vars, got %d", len(container.Env)) + // Should have PORT + DB_HOST + DB_USER + DB_PASS + DB_PORT = 5 + if len(container.Env) != 5 { + t.Fatalf("Expected 5 env vars, got %d", len(container.Env)) } expectedEnvs := map[string]string{ @@ -990,6 +1102,7 @@ func TestInjectDatabaseEnvVars(t *testing.T) { "DB_USER": "DB_USER", "DB_PASS": "DB_PASS", } + foundDBPort := false for _, env := range container.Env { if expectedKey, ok := expectedEnvs[env.Name]; ok { if env.ValueFrom == nil || env.ValueFrom.SecretKeyRef == nil { @@ -1006,10 +1119,22 @@ func TestInjectDatabaseEnvVars(t *testing.T) { } delete(expectedEnvs, env.Name) } + if env.Name == "DB_PORT" { + if env.Value != "5432" { + t.Errorf("Expected DB_PORT=5432, got %q", env.Value) + } + if env.ValueFrom != nil { + t.Error("DB_PORT should not use ValueFrom") + } + foundDBPort = true + } } if len(expectedEnvs) > 0 { t.Errorf("Missing expected env vars: %v", expectedEnvs) } + if !foundDBPort { + t.Error("Missing DB_PORT env var") + } }) t.Run("Idempotent", func(t *testing.T) { @@ -1075,9 +1200,9 @@ func TestInjectDatabaseEnvVars(t *testing.T) { } container := deploy.Spec.Template.Spec.Containers[0] - // Should have PORT + DB_HOST + DB_USER + DB_PASS = 4 - if len(container.Env) != 4 { - t.Fatalf("Expected 4 env vars, got %d", len(container.Env)) + // Should have PORT + DB_HOST + DB_USER + DB_PASS + DB_PORT = 5 + if len(container.Env) != 5 { + t.Fatalf("Expected 5 env vars, got %d", len(container.Env)) } // DB_HOST should now reference the secret, not a plain value @@ -1135,7 +1260,7 @@ func TestInjectDatabaseEnvVars(t *testing.T) { }, } - changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "api-server-db-secret", "api-server") + changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "api-server-db-secret", "api-server", 5433) if !changed { t.Fatal("Expected env injection changes") } @@ -1149,10 +1274,13 @@ func TestInjectDatabaseEnvVars(t *testing.T) { } appContainer := deploy.Spec.Template.Spec.Containers[1] - expected := map[string]bool{"DB_HOST": false, "DB_USER": false, "DB_PASS": false} + expected := map[string]bool{"DB_HOST": false, "DB_USER": false, "DB_PASS": false, "DB_PORT": false} for _, env := range appContainer.Env { if _, ok := expected[env.Name]; ok { expected[env.Name] = true + if env.Name == "DB_PORT" && env.Value != "5433" { + t.Errorf("Expected DB_PORT=5433, got %q", env.Value) + } } } for name, found := range expected { @@ -1175,15 +1303,15 @@ func TestInjectDatabaseEnvVars(t *testing.T) { }, } - changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "app-db-secret", "missing-app") + changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "app-db-secret", "missing-app", 5432) if !changed { t.Fatal("Expected env injection changes") } if exactMatch { t.Fatal("Expected fallback because preferred container does not exist") } - if len(deploy.Spec.Template.Spec.Containers[0].Env) != 3 { - t.Fatalf("Expected 3 injected DB env vars, got %d", len(deploy.Spec.Template.Spec.Containers[0].Env)) + if len(deploy.Spec.Template.Spec.Containers[0].Env) != 4 { + t.Fatalf("Expected 4 injected DB env vars, got %d", len(deploy.Spec.Template.Spec.Containers[0].Env)) } }) } diff --git a/apps/portal/examples/advanced-template/template.yaml b/apps/portal/examples/advanced-template/template.yaml index a58945d..37a4709 100644 --- a/apps/portal/examples/advanced-template/template.yaml +++ b/apps/portal/examples/advanced-template/template.yaml @@ -14,6 +14,8 @@ spec: - name - owner - port + - dockerOrg + - repoName properties: name: title: Name @@ -49,6 +51,9 @@ spec: databaseConfig: title: Database Settings type: object + default: + dbType: none + dbName: "" ui:field: DatabasePicker # --- END OF NEW STEP --- diff --git a/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts index 0a2f14e..3a2be90 100644 --- a/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts +++ b/apps/portal/examples/nestjs-prisma-template/content/source/src/prisma/prisma.service.ts @@ -31,6 +31,8 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul port: parseInt(process.env.DB_PORT || '5432', 10), }); + // TODO(helios-platform#328): Remove this cast when @prisma/adapter-pg exposes + // a Pool-compatible type accepted by PrismaPg in all supported versions. const adapter = new PrismaPg(pool as any); super({ adapter }); diff --git a/apps/portal/examples/nestjs-prisma-template/template.yaml b/apps/portal/examples/nestjs-prisma-template/template.yaml index 24a3ba8..aa9fecf 100644 --- a/apps/portal/examples/nestjs-prisma-template/template.yaml +++ b/apps/portal/examples/nestjs-prisma-template/template.yaml @@ -17,6 +17,8 @@ spec: - name - owner - port + - dockerOrg + - repoName properties: name: title: Name @@ -50,6 +52,9 @@ spec: databaseConfig: title: Database Settings type: object + default: + dbType: none + dbName: "" ui:field: DatabasePicker - title: Repository & Webhook diff --git a/apps/portal/packages/app/package.json b/apps/portal/packages/app/package.json index 5e6da8a..5e77cef 100644 --- a/apps/portal/packages/app/package.json +++ b/apps/portal/packages/app/package.json @@ -59,8 +59,8 @@ "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.0.0", - "@types/react": "*", - "@types/react-dom": "*", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "cross-env": "^7.0.0", "jest": "^30.2.0", "webpack": "~5.103.0" diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx index 138b44b..e7843e6 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx @@ -24,7 +24,9 @@ export const DatabasePicker = ({ const dbType = formData?.dbType || 'none'; const dbName = formData?.dbName || ''; - const handleTypeChange = (event: ChangeEvent<{ value: unknown }>) => { + const handleTypeChange = ( + event: ChangeEvent<{ name?: string; value: unknown }>, + ) => { const newType = event.target.value as string; onChange({ dbType: newType, diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts index fda085e..fece3a4 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -9,7 +9,9 @@ export const DatabasePickerExtension: any = scaffolderPlugin.provide( validation: (value: any, validation: any) => { // Custom validation: If postgres is selected, dbName MUST be provided if (value?.dbType === 'postgres') { - if (!value?.dbName) { + const dbName = + typeof value?.dbName === 'string' ? value.dbName.trim() : ''; + if (!dbName) { validation.addError( 'Database Name is required when PostgreSQL is selected', ); diff --git a/apps/portal/start-dev.sh b/apps/portal/start-dev.sh index 33bff03..135a0d3 100755 --- a/apps/portal/start-dev.sh +++ b/apps/portal/start-dev.sh @@ -1,47 +1,9 @@ #!/bin/bash -set -e +set -euo pipefail -trim_ws() { - local s="$1" - s="${s#"${s%%[![:space:]]*}"}" - s="${s%"${s##*[![:space:]]}"}" - printf '%s' "$s" -} - -read_env_value() { - local env_file="$1" - local wanted_key="$2" - local line key value - - while IFS= read -r line || [[ -n "$line" ]]; do - line="$(trim_ws "$line")" - [[ -z "$line" || "${line:0:1}" == "#" ]] && continue - - if [[ "$line" == export\ * ]]; then - line="${line#export }" - line="$(trim_ws "$line")" - fi - - [[ "$line" != *=* ]] && continue - key="$(trim_ws "${line%%=*}")" - value="$(trim_ws "${line#*=}")" - - if [[ "$key" != "$wanted_key" ]]; then - continue - fi - - if [[ "$value" == \"*\" && "$value" == *\" ]]; then - value="${value:1:${#value}-2}" - elif [[ "$value" == \'*\' && "$value" == *\' ]]; then - value="${value:1:${#value}-2}" - fi - - printf '%s' "$value" - return 0 - done < "$env_file" - - return 1 -} +SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" +# shellcheck source=../../scripts/lib/env_helpers.sh +source "$SCRIPT_DIR/../../scripts/lib/env_helpers.sh" decode_base64() { if printf 'Zg==' | base64 --decode >/dev/null 2>&1; then diff --git a/apps/portal/yarn.lock b/apps/portal/yarn.lock index 9de9c86..c08a9c0 100644 --- a/apps/portal/yarn.lock +++ b/apps/portal/yarn.lock @@ -15978,8 +15978,8 @@ __metadata: "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" "@testing-library/user-event": "npm:^14.0.0" - "@types/react": "npm:*" - "@types/react-dom": "npm:*" + "@types/react": "npm:^18.0.0" + "@types/react-dom": "npm:^18.0.0" cross-env: "npm:^7.0.0" jest: "npm:^30.2.0" react: "npm:^18.0.2" diff --git a/docs/APP_STARTUP_GUIDE.md b/docs/APP_STARTUP_GUIDE.md index ffdb29d..b2ea580 100644 --- a/docs/APP_STARTUP_GUIDE.md +++ b/docs/APP_STARTUP_GUIDE.md @@ -146,7 +146,7 @@ kubectl rollout restart deployment tekton-pipelines-controller -n tekton-pipelin > [!WARNING] > **Only apply this patch if your k3s node version is below 1.28.** -> + > k3d typically ships with a recent k3s version (1.28+), so this patch is rarely needed. Check with `kubectl version`. ```bash diff --git a/scripts/check-prereqs.sh b/scripts/check-prereqs.sh index 69182aa..2638a22 100755 --- a/scripts/check-prereqs.sh +++ b/scripts/check-prereqs.sh @@ -10,6 +10,10 @@ # ============================================================================= set -euo pipefail +SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" +# shellcheck source=./lib/env_helpers.sh +source "$SCRIPT_DIR/lib/env_helpers.sh" + RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -30,49 +34,6 @@ version_gte() { printf '%s\n%s' "$2" "$1" | sort -t. -k1,1n -k2,2n -k3,3n -C } -trim_ws() { - local s="$1" - s="${s#"${s%%[![:space:]]*}"}" - s="${s%"${s##*[![:space:]]}"}" - printf '%s' "$s" -} - -read_env_value() { - local env_file="$1" - local wanted_key="$2" - local line key value - - while IFS= read -r line || [[ -n "$line" ]]; do - line="$(trim_ws "$line")" - [[ -z "$line" || "${line:0:1}" == "#" ]] && continue - - if [[ "$line" == export\ * ]]; then - line="${line#export }" - line="$(trim_ws "$line")" - fi - - [[ "$line" != *=* ]] && continue - - key="$(trim_ws "${line%%=*}")" - value="$(trim_ws "${line#*=}")" - - if [[ "$key" != "$wanted_key" ]]; then - continue - fi - - if [[ "$value" == \"*\" && "$value" == *\" ]]; then - value="${value:1:${#value}-2}" - elif [[ "$value" == \'*\' && "$value" == *\' ]]; then - value="${value:1:${#value}-2}" - fi - - printf '%s' "$value" - return 0 - done < "$env_file" - - return 1 -} - # --------------------------------------------------------------------------- # Check a single binary # $1 = binary name diff --git a/scripts/lib/env_helpers.sh b/scripts/lib/env_helpers.sh new file mode 100644 index 0000000..f826009 --- /dev/null +++ b/scripts/lib/env_helpers.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +trim_ws() { + local s="$1" + s="${s#"${s%%[![:space:]]*}"}" + s="${s%"${s##*[![:space:]]}"}" + printf '%s' "$s" +} + +read_env_value() { + local env_file="$1" + local wanted_key="$2" + local line key value + + while IFS= read -r line || [[ -n "$line" ]]; do + line="$(trim_ws "$line")" + [[ -z "$line" || "${line:0:1}" == "#" ]] && continue + + if [[ "$line" == export\ * ]]; then + line="${line#export }" + line="$(trim_ws "$line")" + fi + + [[ "$line" != *=* ]] && continue + + key="$(trim_ws "${line%%=*}")" + value="$(trim_ws "${line#*=}")" + + if [[ "$key" != "$wanted_key" ]]; then + continue + fi + + if [[ "$value" == \"*\" ]]; then + value="${value:1:${#value}-2}" + elif [[ "$value" == \'*\' ]]; then + value="${value:1:${#value}-2}" + fi + + printf '%s' "$value" + return 0 + done < "$env_file" + + return 1 +} \ No newline at end of file From 877de421fc542ae86276b821e1b671024781c4ff Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 22 Mar 2026 21:56:39 +0700 Subject: [PATCH 17/19] fix: apply PR review hardening and refactor controller tests --- Taskfile.yml | 80 +------ .../internal/controller/database_resources.go | 6 +- .../controller/database_resources_test.go | 225 +++++++----------- .../DatabasePicker.tsx | 2 +- .../DatabasePickerExtension/extension.ts | 2 +- docs/APP_STARTUP_GUIDE.md | 1 - 6 files changed, 91 insertions(+), 225 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index fcb645d..64f5060 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -74,7 +74,7 @@ tasks: - k3d cluster list 2>/dev/null | grep -q '{{.CLUSTER_NAME}}' cmds: - echo "Creating k3d cluster '{{.CLUSTER_NAME}}'..." - - k3d cluster create {{.CLUSTER_NAME}} --agents 1 --wait --api-port 0.0.0.0:6550 + - k3d cluster create {{.CLUSTER_NAME}} --agents 1 --wait --api-port 127.0.0.1:6550 - kubectl config set-cluster k3d-{{.CLUSTER_NAME}} --server=https://localhost:6550 - kubectl cluster-info @@ -170,8 +170,6 @@ tasks: dir: apps/operator env: HELIOS_CUE_PATH: '{{.ROOT_DIR}}/cue' - GITHUB_TOKEN: $GITHUB_TOKEN - GITHUB_USER: $GITHUB_USER cmds: - cmd: make run platforms: [linux, darwin] @@ -180,82 +178,8 @@ tasks: dev:portal: desc: Run the Backstage portal with ArgoCD + kubectl proxy - dir: apps/portal - env: - AUTH_GITHUB_CLIENT_ID: $AUTH_GITHUB_CLIENT_ID - AUTH_GITHUB_CLIENT_SECRET: $AUTH_GITHUB_CLIENT_SECRET - GITHUB_TOKEN: $GITHUB_TOKEN - GITHUB_ORG: $GITHUB_ORG - cmds: - - task: dev:portal:proxy - - task: dev:portal:start - - dev:portal:proxy: - desc: Start kubectl proxy and ArgoCD port-forward - internal: true - cmds: - - echo "Starting ArgoCD port-forward (localhost:{{.ARGOCD_PORT}})..." - - cmd: kubectl port-forward -n argocd svc/argocd-server {{.ARGOCD_PORT}}:443 &>/dev/null & - platforms: [linux, darwin] - - cmd: powershell -Command "Start-Process kubectl -ArgumentList 'port-forward', '-n', 'argocd', 'svc/argocd-server', '{{.ARGOCD_PORT}}:443' -WindowStyle Hidden" - platforms: [windows] - - cmd: sleep 2 - platforms: [linux, darwin] - - cmd: powershell -Command "Start-Sleep -Seconds 2" - platforms: [windows] - - echo "Starting kubectl proxy (localhost:{{.KUBECTL_PROXY_PORT}})..." - - cmd: kubectl proxy --port={{.KUBECTL_PROXY_PORT}} &>/dev/null & - platforms: [linux, darwin] - - cmd: powershell -Command "Start-Process kubectl -ArgumentList 'proxy', '--port={{.KUBECTL_PROXY_PORT}}' -WindowStyle Hidden" - platforms: [windows] - - cmd: sleep 1 - platforms: [linux, darwin] - - cmd: powershell -Command "Start-Sleep -Seconds 1" - platforms: [windows] - - dev:portal:start: - desc: Generate ArgoCD token and start Backstage - internal: true - dir: apps/portal cmds: - - cmd: | - decode_base64() { - if printf 'Zg==' | base64 --decode >/dev/null 2>&1; then - base64 --decode - return - fi - if printf 'Zg==' | base64 -d >/dev/null 2>&1; then - base64 -d - return - fi - if printf 'Zg==' | base64 -D >/dev/null 2>&1; then - base64 -D - return - fi - if command -v openssl >/dev/null 2>&1; then - openssl base64 -d -A - return - fi - echo "No compatible base64 decoder found" >&2 - return 1 - } - - ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | decode_base64) - TOKEN_JSON=$(curl -k -s -X POST \ - -H "Content-Type: application/json" \ - -d "{\"username\":\"admin\",\"password\":\"$ARGOCD_PASS\"}" \ - https://127.0.0.1:{{.ARGOCD_PORT}}/api/v1/session) - if command -v jq >/dev/null 2>&1; then - export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | jq -r '.token // empty') - else - export ARGOCD_AUTH_TOKEN=$(echo "$TOKEN_JSON" | sed 's/.*"token":"\([^"]*\)".*/\1/') - fi - if [ -z "$ARGOCD_AUTH_TOKEN" ] || [ "${#ARGOCD_AUTH_TOKEN}" -lt 20 ]; then - echo "WARNING: Could not generate ArgoCD token. ArgoCD features may not work." - else - echo "ArgoCD token generated." - fi - yarn start + - cmd: cd apps/portal && ./start-dev.sh platforms: [linux, darwin] - cmd: powershell -ExecutionPolicy Bypass -File ../../scripts/start-portal.ps1 -ArgocdPort {{.ARGOCD_PORT}} platforms: [windows] diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index 7180ad8..a291c4c 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -37,8 +37,8 @@ const ( DefaultUsernameLength = 16 // PasswordCharset contains valid characters for password generation - // Includes uppercase, lowercase, digits, and special characters - PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" + // Includes uppercase, lowercase, digits, and URL/shell-safe symbols. + PasswordCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" // UsernameCharset contains valid characters for username generation // Lowercase letters and digits only for database compatibility @@ -588,7 +588,7 @@ func selectTargetContainerIndex(containers []corev1.Container, preferredContaine } if preferredContainerName == "" { - return 0, true + return 0, false } for i := range containers { diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 88b5827..93fc59d 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -1,6 +1,7 @@ package controller import ( + "encoding/base64" "encoding/json" "strings" "testing" @@ -11,11 +12,44 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" appv1alpha1 "github.com/helios-platform-team/helios-platform/apps/operator/api/v1alpha1" ) +func newControllerTestScheme(t *testing.T) *runtime.Scheme { + t.Helper() + + scheme := runtime.NewScheme() + if err := corev1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add corev1 to scheme: %v", err) + } + if err := appv1alpha1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add appv1alpha1 to scheme: %v", err) + } + if err := appsv1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add appsv1 to scheme: %v", err) + } + + return scheme +} + +func newControllerTestReconciler(t *testing.T, objs ...client.Object) (*HeliosAppReconciler, client.Client) { + t.Helper() + + scheme := newControllerTestScheme(t) + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(objs...). + Build() + + return &HeliosAppReconciler{ + Client: fakeClient, + Scheme: scheme, + }, fakeClient +} + func TestGenerateSecurePassword(t *testing.T) { tests := []struct { name string @@ -316,11 +350,6 @@ func TestExtractDatabaseTraits(t *testing.T) { } func TestReconcileDatabaseSecrets(t *testing.T) { - // Create a fake client - scheme := runtime.NewScheme() - _ = corev1.AddToScheme(scheme) - _ = appv1alpha1.AddToScheme(scheme) - dbProps := map[string]any{ "dbType": "postgres", "dbName": "mydb", @@ -353,15 +382,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { } t.Run("CreateNewSecret", func(t *testing.T) { - client := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(app). - Build() - - r := &HeliosAppReconciler{ - Client: client, - Scheme: scheme, - } + r, fakeClient := newControllerTestReconciler(t, app) ctx := t.Context() err := r.reconcileDatabaseSecrets(ctx, app) @@ -371,7 +392,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { // Verify secret was created secret := &corev1.Secret{} - err = client.Get(ctx, types.NamespacedName{ + err = fakeClient.Get(ctx, types.NamespacedName{ Name: "api-server-db-secret", Namespace: "default", }, secret) @@ -404,15 +425,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { }, } - client := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(app, existingSecret). - Build() - - r := &HeliosAppReconciler{ - Client: client, - Scheme: scheme, - } + r, fakeClient := newControllerTestReconciler(t, app, existingSecret) ctx := t.Context() err := r.reconcileDatabaseSecrets(ctx, app) @@ -422,7 +435,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { // Verify existing secret was not modified secret := &corev1.Secret{} - err = client.Get(ctx, types.NamespacedName{ + err = fakeClient.Get(ctx, types.NamespacedName{ Name: "api-server-db-secret", Namespace: "default", }, secret) @@ -434,6 +447,12 @@ func TestReconcileDatabaseSecrets(t *testing.T) { if string(secret.Data["DB_USER"]) != "existing-user" { t.Errorf("Expected existing DB_USER to be preserved, got %s", string(secret.Data["DB_USER"])) } + if string(secret.Data["DB_PASS"]) != "existing-pass" { + t.Errorf("Expected existing DB_PASS to be preserved, got %s", string(secret.Data["DB_PASS"])) + } + if string(secret.Data["DB_HOST"]) != "api-server-db" { + t.Errorf("Expected existing DB_HOST to be preserved, got %s", string(secret.Data["DB_HOST"])) + } }) t.Run("NoDatabaseTraits", func(t *testing.T) { @@ -461,15 +480,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { }, } - client := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(appWithoutDB). - Build() - - r := &HeliosAppReconciler{ - Client: client, - Scheme: scheme, - } + r, fakeClient := newControllerTestReconciler(t, appWithoutDB) ctx := t.Context() err := r.reconcileDatabaseSecrets(ctx, appWithoutDB) @@ -479,7 +490,7 @@ func TestReconcileDatabaseSecrets(t *testing.T) { // Verify no secret was created secretList := &corev1.SecretList{} - err = client.List(ctx, secretList) + err = fakeClient.List(ctx, secretList) if err != nil { t.Fatalf("Failed to list secrets: %v", err) } @@ -524,6 +535,14 @@ func TestGenerateBase64Token(t *testing.T) { if len(token) != expectedBase64Len { t.Errorf("Expected base64 length %d, got %d", expectedBase64Len, len(token)) } + + decoded, decodeErr := base64.StdEncoding.DecodeString(token) + if decodeErr != nil { + t.Fatalf("Token is not valid base64: %v", decodeErr) + } + if len(decoded) != expectedLen { + t.Errorf("Expected decoded token length %d, got %d", expectedLen, len(decoded)) + } }) } } @@ -777,20 +796,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { } t.Run("CreatesStatefulSetAndService", func(t *testing.T) { - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(app). - Build() - - r := &HeliosAppReconciler{ - Client: fakeClient, - Scheme: fullScheme, - } + r, fakeClient := newControllerTestReconciler(t, app) ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, app) @@ -859,20 +865,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { }, } - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(appWithoutDB). - Build() - - r := &HeliosAppReconciler{ - Client: fakeClient, - Scheme: fullScheme, - } + r, fakeClient := newControllerTestReconciler(t, appWithoutDB) ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, appWithoutDB) @@ -925,20 +918,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { }, } - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(appWithRedis). - Build() - - r := &HeliosAppReconciler{ - Client: fakeClient, - Scheme: fullScheme, - } + r, fakeClient := newControllerTestReconciler(t, appWithRedis) ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, appWithRedis) @@ -955,11 +935,6 @@ func TestReconcileDatabaseInstance(t *testing.T) { }) t.Run("UpdatesExistingStatefulSetAndService", func(t *testing.T) { - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - existingSts := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "api-server-db", Namespace: app.Namespace}, Spec: appsv1.StatefulSetSpec{ @@ -991,12 +966,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { }, } - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(app, existingSts, existingSvc). - Build() - - r := &HeliosAppReconciler{Client: fakeClient, Scheme: fullScheme} + r, fakeClient := newControllerTestReconciler(t, app, existingSts, existingSvc) ctx := t.Context() err := r.reconcileDatabaseInstance(ctx, app) @@ -1024,11 +994,6 @@ func TestReconcileDatabaseInstance(t *testing.T) { }) t.Run("FailsOnStatefulSetStorageDrift", func(t *testing.T) { - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - existingSts := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "api-server-db", Namespace: app.Namespace}, Spec: appsv1.StatefulSetSpec{ @@ -1049,12 +1014,7 @@ func TestReconcileDatabaseInstance(t *testing.T) { Spec: corev1.ServiceSpec{Ports: []corev1.ServicePort{{Name: "db", Port: 5432}}}, } - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(app, existingSts, existingSvc). - Build() - - r := &HeliosAppReconciler{Client: fakeClient, Scheme: fullScheme} + r, _ := newControllerTestReconciler(t, app, existingSts, existingSvc) err := r.reconcileDatabaseInstance(t.Context(), app) if err == nil { @@ -1314,6 +1274,28 @@ func TestInjectDatabaseEnvVars(t *testing.T) { t.Fatalf("Expected 4 injected DB env vars, got %d", len(deploy.Spec.Template.Spec.Containers[0].Env)) } }) + + t.Run("NoPreferredContainerUsesFallback", func(t *testing.T) { + deploy := &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "app", Image: "myregistry/app:v1"}, + }, + }, + }, + }, + } + + changed, exactMatch := InjectDatabaseEnvVarsForContainer(deploy, "app-db-secret", "", 5432) + if !changed { + t.Fatal("Expected env injection changes") + } + if exactMatch { + t.Fatal("Expected fallback semantics when preferred container is empty") + } + }) } func TestValidateDatabaseSecret(t *testing.T) { @@ -1395,11 +1377,6 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { } t.Run("InjectsIntoExistingDeployment", func(t *testing.T) { - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - existingDeploy := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "api-server", @@ -1428,15 +1405,7 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { }, } - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(app, existingDeploy). - Build() - - r := &HeliosAppReconciler{ - Client: fakeClient, - Scheme: fullScheme, - } + r, fakeClient := newControllerTestReconciler(t, app, existingDeploy) ctx := t.Context() pending, err := r.reconcileDatabaseSecretInjection(ctx, app) @@ -1498,20 +1467,7 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { }, } - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(appWithoutDB). - Build() - - r := &HeliosAppReconciler{ - Client: fakeClient, - Scheme: fullScheme, - } + r, _ := newControllerTestReconciler(t, appWithoutDB) ctx := t.Context() pending, err := r.reconcileDatabaseSecretInjection(ctx, appWithoutDB) @@ -1526,20 +1482,7 @@ func TestReconcileDatabaseSecretInjection(t *testing.T) { t.Run("DeploymentNotFound_GracefulSkip", func(t *testing.T) { // When Deployment doesn't exist yet (ArgoCD hasn't synced), // the reconciler should skip without error. - fullScheme := runtime.NewScheme() - _ = corev1.AddToScheme(fullScheme) - _ = appv1alpha1.AddToScheme(fullScheme) - _ = appsv1.AddToScheme(fullScheme) - - fakeClient := fake.NewClientBuilder(). - WithScheme(fullScheme). - WithObjects(app). - Build() - - r := &HeliosAppReconciler{ - Client: fakeClient, - Scheme: fullScheme, - } + r, _ := newControllerTestReconciler(t, app) ctx := t.Context() pending, err := r.reconcileDatabaseSecretInjection(ctx, app) diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx index e7843e6..c1137de 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx @@ -9,7 +9,7 @@ import { } from '@material-ui/core'; // Define the exact state structure expected by PhuocHoan's CUE schema -interface DatabaseConfig { +export interface DatabaseConfig { dbType: string; dbName?: string; } diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts index fece3a4..264c2f9 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -2,7 +2,7 @@ import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; import { DatabasePicker } from './DatabasePicker'; -export const DatabasePickerExtension: any = scaffolderPlugin.provide( +export const DatabasePickerExtension = scaffolderPlugin.provide( createScaffolderFieldExtension({ name: 'DatabasePicker', component: DatabasePicker, diff --git a/docs/APP_STARTUP_GUIDE.md b/docs/APP_STARTUP_GUIDE.md index b2ea580..94ba5b7 100644 --- a/docs/APP_STARTUP_GUIDE.md +++ b/docs/APP_STARTUP_GUIDE.md @@ -146,7 +146,6 @@ kubectl rollout restart deployment tekton-pipelines-controller -n tekton-pipelin > [!WARNING] > **Only apply this patch if your k3s node version is below 1.28.** - > k3d typically ships with a recent k3s version (1.28+), so this patch is rarely needed. Check with `kubectl version`. ```bash From a98a30bf345a9c65eddd5d2eb9cb56f3b2d425ca Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 22 Mar 2026 22:23:55 +0700 Subject: [PATCH 18/19] fix: address latest CodeRabbit follow-up comments --- Taskfile.yml | 54 +++++++++++-------- .../internal/controller/database_resources.go | 14 +++++ .../DatabasePicker.tsx | 12 +++-- .../DatabasePickerExtension/extension.ts | 8 ++- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 64f5060..cadfab4 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -82,11 +82,11 @@ tasks: desc: Install Tekton Pipeline, Triggers, and Interceptors cmds: - echo "Installing Tekton Pipeline..." - - kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml + - kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml --request-timeout=120s - echo "Installing Tekton Triggers..." - - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml + - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml --request-timeout=120s - echo "Installing Tekton Interceptors..." - - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml + - kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml --request-timeout=120s - echo "Waiting for Tekton Pipelines controller..." - kubectl rollout status deployment/tekton-pipelines-controller -n tekton-pipelines --timeout=600s - echo "Waiting for Tekton Pipelines webhook..." @@ -132,25 +132,37 @@ tasks: setup:credentials: desc: Create Docker registry secret and link to pipeline SA - preconditions: - - sh: '[ -n "$DOCKER_USERNAME" ]' - msg: "DOCKER_USERNAME is not set in .env" - - sh: '[ -n "$DOCKER_PASSWORD" ]' - msg: "DOCKER_PASSWORD is not set in .env" cmds: - - >- - kubectl create secret docker-registry docker-credentials - --docker-server=${DOCKER_SERVER:-https://index.docker.io/v1/} - --docker-username=$DOCKER_USERNAME - --docker-password=$DOCKER_PASSWORD - --docker-email=${DOCKER_EMAIL:-dev@helios.io} - --dry-run=client -o yaml | kubectl apply -f - - - >- - if kubectl get sa pipeline >/dev/null 2>&1; then - kubectl patch sa pipeline -p '{"secrets": [{"name": "docker-credentials"}]}' - else - echo "pipeline ServiceAccount not found yet; skipping patch (will be created by Tekton)" - fi + - cmd: | + if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_PASSWORD" ]; then + echo "DOCKER_USERNAME and DOCKER_PASSWORD must be set in .env" >&2 + exit 1 + fi + platforms: [linux, darwin] + - cmd: | + powershell -Command "if ([string]::IsNullOrEmpty($env:DOCKER_USERNAME) -or [string]::IsNullOrEmpty($env:DOCKER_PASSWORD)) { Write-Error 'DOCKER_USERNAME and DOCKER_PASSWORD must be set in .env'; exit 1 }" + platforms: [windows] + - cmd: | + kubectl create secret docker-registry docker-credentials \ + --docker-server=${DOCKER_SERVER:-https://index.docker.io/v1/} \ + --docker-username=$DOCKER_USERNAME \ + --docker-password=$DOCKER_PASSWORD \ + --docker-email=${DOCKER_EMAIL:-dev@helios.io} \ + --dry-run=client -o yaml | kubectl apply -f - + platforms: [linux, darwin] + - cmd: | + powershell -Command "$server = if ([string]::IsNullOrEmpty($env:DOCKER_SERVER)) { 'https://index.docker.io/v1/' } else { $env:DOCKER_SERVER }; $email = if ([string]::IsNullOrEmpty($env:DOCKER_EMAIL)) { 'dev@helios.io' } else { $env:DOCKER_EMAIL }; kubectl create secret docker-registry docker-credentials --docker-server=$server --docker-username=$env:DOCKER_USERNAME --docker-password=$env:DOCKER_PASSWORD --docker-email=$email --dry-run=client -o yaml | kubectl apply -f -" + platforms: [windows] + - cmd: | + if kubectl get sa pipeline >/dev/null 2>&1; then + kubectl patch sa pipeline -p '{"secrets": [{"name": "docker-credentials"}]}' + else + echo "pipeline ServiceAccount not found yet; skipping patch (will be created by Tekton)" + fi + platforms: [linux, darwin] + - cmd: | + powershell -Command "kubectl get sa pipeline *> $null; if ($LASTEXITCODE -eq 0) { kubectl patch sa pipeline -p '{\"secrets\": [{\"name\": \"docker-credentials\"}]}' } else { Write-Host 'pipeline ServiceAccount not found yet; skipping patch (will be created by Tekton)' }" + platforms: [windows] setup:portal-deps: desc: Install Backstage portal dependencies diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index a291c4c..f339f5b 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -185,6 +185,8 @@ func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { ComponentName string Properties DatabaseTraitProperties } { + log := logf.Log.WithName("database-traits") + var dbTraits []struct { ComponentName string Properties DatabaseTraitProperties @@ -197,6 +199,10 @@ func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { if trait.Properties != nil && trait.Properties.Raw != nil { if err := json.Unmarshal(trait.Properties.Raw, &props); err != nil { // Log error but continue - don't fail the entire reconciliation + log.Error(err, "Failed to parse database trait properties, skipping trait", + "component", component.Name, + "traitType", trait.Type, + "rawPreview", truncateForLog(trait.Properties.Raw, 200)) continue } } @@ -214,6 +220,14 @@ func ExtractDatabaseTraits(app *appv1alpha1.HeliosApp) []struct { return dbTraits } +func truncateForLog(raw []byte, maxLen int) string { + if maxLen <= 0 || len(raw) <= maxLen { + return string(raw) + } + + return string(raw[:maxLen]) + "..." +} + // reconcileDatabaseSecrets ensures database credential secrets exist for all // components with database traits. If a secret already exists, it is not modified // to preserve existing credentials. diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx index c1137de..0b9f600 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx @@ -9,8 +9,10 @@ import { } from '@material-ui/core'; // Define the exact state structure expected by PhuocHoan's CUE schema +export type DatabaseType = 'none' | 'postgres'; + export interface DatabaseConfig { - dbType: string; + dbType: DatabaseType; dbName?: string; } @@ -19,15 +21,15 @@ export const DatabasePicker = ({ rawErrors, required, formData, -}: FieldExtensionComponentProps) => { +}: FieldExtensionComponentProps) => { // Default to 'none' if no data is present - const dbType = formData?.dbType || 'none'; + const dbType: DatabaseType = formData?.dbType === 'postgres' ? 'postgres' : 'none'; const dbName = formData?.dbName || ''; const handleTypeChange = ( event: ChangeEvent<{ name?: string; value: unknown }>, ) => { - const newType = event.target.value as string; + const newType: DatabaseType = event.target.value === 'postgres' ? 'postgres' : 'none'; onChange({ dbType: newType, // Clear the dbName if they switch back to "No Database" @@ -61,7 +63,7 @@ export const DatabasePicker = ({ margin="normal" required helperText="The name of the database to create (e.g., my_custom_db)" - error={rawErrors?.length > 0 && !dbName} + error={rawErrors?.length > 0 && !dbName.trim()} /> )} diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts index 264c2f9..890802a 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -1,12 +1,16 @@ import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; -import { DatabasePicker } from './DatabasePicker'; +import { DatabasePicker, type DatabaseConfig } from './DatabasePicker'; + +interface ValidationContext { + addError: (message: string) => void; +} export const DatabasePickerExtension = scaffolderPlugin.provide( createScaffolderFieldExtension({ name: 'DatabasePicker', component: DatabasePicker, - validation: (value: any, validation: any) => { + validation: (value: DatabaseConfig | undefined, validation: ValidationContext) => { // Custom validation: If postgres is selected, dbName MUST be provided if (value?.dbType === 'postgres') { const dbName = From 9966067acd46960862710e629b74282ef952b927 Mon Sep 17 00:00:00 2001 From: Ho Phuoc Hoan Date: Sun, 22 Mar 2026 22:41:17 +0700 Subject: [PATCH 19/19] fix: resolve latest CodeRabbit findings and portal-ci formatting --- Taskfile.yml | 13 ++++++++++--- .../internal/controller/database_resources.go | 12 ++++++++---- .../internal/controller/database_resources_test.go | 8 +++++++- .../DatabasePickerExtension/DatabasePicker.tsx | 6 ++++-- .../scaffolder/DatabasePickerExtension/extension.ts | 5 ++++- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index cadfab4..64e3aa1 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -71,7 +71,7 @@ tasks: setup:cluster: desc: Create a k3d cluster (idempotent) status: - - k3d cluster list 2>/dev/null | grep -q '{{.CLUSTER_NAME}}' + - k3d kubeconfig get {{.CLUSTER_NAME}} cmds: - echo "Creating k3d cluster '{{.CLUSTER_NAME}}'..." - k3d cluster create {{.CLUSTER_NAME}} --agents 1 --wait --api-port 127.0.0.1:6550 @@ -125,8 +125,15 @@ tasks: desc: Grant Tekton Triggers SA the permissions to create PipelineRuns cmds: - >- - kubectl create clusterrolebinding tekton-triggers-sa-admin - --clusterrole=cluster-admin + kubectl create role tekton-triggers-pipelinerun-runner + --namespace default + --verb=get,list,watch,create,update,patch + --resource=pipelineruns.tekton.dev,taskruns.tekton.dev + --dry-run=client -o yaml | kubectl apply -f - + - >- + kubectl create rolebinding tekton-triggers-pipelinerun-runner + --namespace default + --role=tekton-triggers-pipelinerun-runner --serviceaccount=default:tekton-triggers-sa --dry-run=client -o yaml | kubectl apply -f - diff --git a/apps/operator/internal/controller/database_resources.go b/apps/operator/internal/controller/database_resources.go index f339f5b..174c475 100644 --- a/apps/operator/internal/controller/database_resources.go +++ b/apps/operator/internal/controller/database_resources.go @@ -671,11 +671,15 @@ func (r *HeliosAppReconciler) reconcileDatabaseSecretInjection(ctx context.Conte // Inject env vars if not already present. changed, exactContainerMatch := InjectDatabaseEnvVarsForContainer(deploy, secretName, dbTrait.ComponentName, int32(port)) if !exactContainerMatch { + fallbackContainer := "" + if len(deploy.Spec.Template.Spec.Containers) > 0 { + fallbackContainer = deploy.Spec.Template.Spec.Containers[0].Name + } log.Info("Preferred application container not found, using first container for DB env injection", "component", dbTrait.ComponentName, "deployment", deployName, "preferredContainer", dbTrait.ComponentName, - "fallbackContainer", deploy.Spec.Template.Spec.Containers[0].Name) + "fallbackContainer", fallbackContainer) } if !changed { @@ -715,7 +719,7 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s "helios.io/db-type": "postgres", } - probeCommand := fmt.Sprintf("pg_isready -U \"$POSTGRES_USER\" -d %q -p \"$PGPORT\"", dbName) + probeCommand := `pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" -p "$PGPORT"` return &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ @@ -773,9 +777,9 @@ func GenerateDatabaseStatefulSet(namespace, name, secretName, dbName, version, s }, { // PGDATA tells Postgres where to store cluster data. - // Must match volumeMount + subPath to avoid lost+found conflicts. + // Use mount path directly; subPath already selects the PVC folder. Name: "PGDATA", - Value: PostgresDataPath + "/" + PostgresDataSubPath, + Value: PostgresDataPath, }, { // Ensure consistent UTF-8 encoding for all databases. diff --git a/apps/operator/internal/controller/database_resources_test.go b/apps/operator/internal/controller/database_resources_test.go index 93fc59d..6529487 100644 --- a/apps/operator/internal/controller/database_resources_test.go +++ b/apps/operator/internal/controller/database_resources_test.go @@ -645,7 +645,7 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { for _, env := range container.Env { if env.Name == "PGDATA" { foundPGDATA = true - expectedPGDATA := PostgresDataPath + "/" + PostgresDataSubPath + expectedPGDATA := PostgresDataPath if env.Value != expectedPGDATA { t.Errorf("Expected PGDATA value %q, got %q", expectedPGDATA, env.Value) } @@ -691,6 +691,9 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { if !strings.Contains(cmdStr, `-p "$PGPORT"`) { t.Errorf("LivenessProbe command missing custom port flag. Got: %s", cmdStr) } + if !strings.Contains(cmdStr, `-d "$POSTGRES_DB"`) { + t.Errorf("LivenessProbe command should reference POSTGRES_DB env var. Got: %s", cmdStr) + } } // Verify readinessProbe uses custom port @@ -704,6 +707,9 @@ func TestGenerateDatabaseStatefulSet(t *testing.T) { if !strings.Contains(cmdStr, `-p "$PGPORT"`) { t.Errorf("ReadinessProbe command missing custom port flag. Got: %s", cmdStr) } + if !strings.Contains(cmdStr, `-d "$POSTGRES_DB"`) { + t.Errorf("ReadinessProbe command should reference POSTGRES_DB env var. Got: %s", cmdStr) + } } // Verify volume claim template diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx index 0b9f600..fc30f93 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/DatabasePicker.tsx @@ -23,13 +23,15 @@ export const DatabasePicker = ({ formData, }: FieldExtensionComponentProps) => { // Default to 'none' if no data is present - const dbType: DatabaseType = formData?.dbType === 'postgres' ? 'postgres' : 'none'; + const dbType: DatabaseType = + formData?.dbType === 'postgres' ? 'postgres' : 'none'; const dbName = formData?.dbName || ''; const handleTypeChange = ( event: ChangeEvent<{ name?: string; value: unknown }>, ) => { - const newType: DatabaseType = event.target.value === 'postgres' ? 'postgres' : 'none'; + const newType: DatabaseType = + event.target.value === 'postgres' ? 'postgres' : 'none'; onChange({ dbType: newType, // Clear the dbName if they switch back to "No Database" diff --git a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts index 890802a..80b43ab 100644 --- a/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts +++ b/apps/portal/packages/app/src/scaffolder/DatabasePickerExtension/extension.ts @@ -10,7 +10,10 @@ export const DatabasePickerExtension = scaffolderPlugin.provide( createScaffolderFieldExtension({ name: 'DatabasePicker', component: DatabasePicker, - validation: (value: DatabaseConfig | undefined, validation: ValidationContext) => { + validation: ( + value: DatabaseConfig | undefined, + validation: ValidationContext, + ) => { // Custom validation: If postgres is selected, dbName MUST be provided if (value?.dbType === 'postgres') { const dbName =