diff --git a/.golangci.yaml b/.golangci.yaml
index 2fedf1ead..4659ba471 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -5,6 +5,7 @@ run:
linters:
default: none
enable:
+ - gocritic
- bodyclose
- copyloopvar
- depguard
@@ -76,6 +77,13 @@ linters:
desc: use github.com/Masterminds/semver/v3
- pkg: github.com/hashicorp/go-version
desc: use github.com/Masterminds/semver/v3
+ gocritic:
+ disable-all: true
+ enabled-checks:
+ - ruleguard
+ settings:
+ ruleguard:
+ rules: "./analysis/ssrf.go"
forbidigo:
forbid:
- pattern: ^tls\.Config$
@@ -93,6 +101,11 @@ linters:
- comments
- common-false-positives
- legacy
+ rules:
+ - path: pkg/libhttp/ssrf
+ linters:
+ - gocritic
+ text: ruleguard
paths:
- third_party$
- builtin$
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1e34f82b2..1b70dafc5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -147,7 +147,9 @@ When adding a new route to an existing handler use the following as a template (
// @router /{id} [get]
```
-explanation about each line can be found (here)[https://github.com/swaggo/swag#api-operation]
+explanation about each line can be found [here](https://github.com/swaggo/swag#api-operation)
+
+After changing these annotations, regenerate the TypeScript API client and types — see [Generating API types](./README.md#generating-api-types).
## Licensing
diff --git a/Makefile b/Makefile
index adb4a36a2..e72b77abf 100644
--- a/Makefile
+++ b/Makefile
@@ -109,7 +109,7 @@ dev-extension: build-server build-client ## Run the extension in development mod
.PHONY: docs-build docs-validate docs-clean docs-validate-clean
docs-build: init-dist ## Build docs
go mod download
- cd api && $(SWAG) init -o "../dist/docs" -ot "yaml" -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 -p pascalcase --markdownFiles ./
+ cd api && $(SWAG) init -o "../dist/docs" -ot "yaml" -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 -p pascalcase --markdownFiles ./ --overridesFile .swaggo
docs-validate: docs-build ## Validate docs
pnpm swagger2openapi --warnOnly dist/docs/swagger.yaml -o dist/docs/openapi.yaml
diff --git a/README.md b/README.md
index accfa31ee..883e4181e 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,32 @@ You can join the Portainer Community by visiting [https://www.portainer.io/join-
- Want to report a bug or request a feature? Please open [an issue](https://github.com/portainer/portainer/issues/new).
- Want to help us build **_portainer_**? Follow our [contribution guidelines](https://docs.portainer.io/contribute/contribute) to build it locally and make a pull request.
+## Generating API types
+
+The frontend consumes a TypeScript API client (SDK functions and request/response types) that is generated from the Go API's Swagger annotations. Regenerate it after any API change — a new endpoint, a changed request/response shape, or a removed endpoint:
+
+```bash
+make generate-api
+```
+
+This runs the following pipeline:
+
+```
+Go Swagger annotations
+ → dist/docs/swagger.yaml (make docs-build, via swaggo/swag)
+ → dist/docs/openapi.yaml (swagger2openapi + validation)
+ → app/react/portainer/generated-api/portainer/ (hey-api/openapi-ts)
+```
+
+The generator is configured in [`openapi-ts.config.ts`](./openapi-ts.config.ts), which controls the output path, plugins, and tag filters (for example, `deprecated` endpoints and `edge_agent`-tagged routes are excluded).
+
+The generated files live in `app/react/portainer/generated-api/portainer/` and must **not** be edited by hand — your changes would be overwritten on the next run. Import the generated SDK functions and types instead of writing direct HTTP calls:
+
+- `@api/sdk.gen` — SDK functions
+- `@api/types.gen` — request/response types
+
+See [Adding api docs](./CONTRIBUTING.md#adding-api-docs) for how to annotate handlers so they are picked up by the generator.
+
## Security
For information about reporting security vulnerabilities, please see our [Security Policy](SECURITY.md).
diff --git a/analysis/ssrf.go b/analysis/ssrf.go
new file mode 100644
index 000000000..3af95653b
--- /dev/null
+++ b/analysis/ssrf.go
@@ -0,0 +1,29 @@
+//go:build ignore
+
+package gorules
+
+import "github.com/quasilyte/go-ruleguard/dsl"
+
+// unwrappedHTTPTransport flags http.Transport composite literals that are not
+// the direct argument to ssrf.WrapTransport.
+func unwrappedHTTPTransport(m dsl.Matcher) {
+ // Inline construction passed to a function call.
+ m.Match(`$f(&http.Transport{$*_})`).
+ Where(m["f"].Text != "ssrf.WrapTransport" && m["f"].Text != "WrapTransport" &&
+ m["f"].Text != "ssrf.WrapTransportInternal" && m["f"].Text != "WrapTransportInternal").
+ Report(`$f receives a bare *http.Transport; wrap with ssrf.WrapTransport() to enforce the SSRF protection policy`)
+
+ // Variable assigned a bare transport (cannot be tracked to a later WrapTransport call).
+ m.Match(`$_ := &http.Transport{$*_}`).
+ Report(`bare *http.Transport variable; use ssrf.WrapTransport(&http.Transport{...}) inline instead`)
+}
+
+// internalTransportMisuse flags calls to WrapTransportInternal outside the four proxy
+// factory files where Chisel-tunnel and in-cluster K8s destinations are valid exemptions.
+func internalTransportMisuse(m dsl.Matcher) {
+ m.Match(`ssrf.WrapTransportInternal($*_)`).
+ Where(
+ !(m.File().PkgPath.Matches(`proxy/factory`) &&
+ m.File().Name.Matches(`^(docker|agent|local_transport|edge_transport)\.go$`))).
+ Report(`WrapTransportInternal bypasses SSRF validation; only valid in the kubernetes local/edge transport constructors and the docker/agent proxy factories`)
+}
diff --git a/analysis/tools.go b/analysis/tools.go
new file mode 100644
index 000000000..55f01d8f8
--- /dev/null
+++ b/analysis/tools.go
@@ -0,0 +1,5 @@
+//go:build tools
+
+package gorules
+
+import _ "github.com/quasilyte/go-ruleguard/dsl"
diff --git a/api/.swaggo b/api/.swaggo
new file mode 100644
index 000000000..3f6089489
--- /dev/null
+++ b/api/.swaggo
@@ -0,0 +1 @@
+replace k8s.io/apimachinery/pkg/apis/meta/v1.Duration string
diff --git a/api/cli/cli.go b/api/cli/cli.go
index 5f262759d..5e1148ee5 100644
--- a/api/cli/cli.go
+++ b/api/cli/cli.go
@@ -56,6 +56,10 @@ func CLIFlags() *portainer.CLIFlags {
TrustedOrigins: kingpin.Flag("trusted-origins", "List of trusted origins for CSRF protection. Separate multiple origins with a comma.").Envar(portainer.TrustedOriginsEnvVar).String(),
CSP: kingpin.Flag("csp", "Content Security Policy (CSP) header").Envar(portainer.CSPEnvVar).Default("true").Bool(),
CompactDB: kingpin.Flag("compact-db", "Enable database compaction on startup").Envar(portainer.CompactDBEnvVar).Default("false").Bool(),
+ SSRFMode: kingpin.Flag("ssrf-mode", "SSRF protection mode: off (disabled), audit (log violations but allow), enforce (block violations)").Envar("PORTAINER_SSRF_MODE").Default("off").Enum("off", "audit", "enforce"),
+ SSRFAllowedHosts: kingpin.Flag("ssrf-allowed-hosts", "Allowlist of hostnames (with optional wildcards), IPs, or CIDRs permitted for outbound requests. When empty and mode is enforce, all outbound connections are blocked").Envar("PORTAINER_SSRF_ALLOWED_HOSTS").Strings(),
+ NoSetupToken: kingpin.Flag("no-setup-token", "Disable the setup token requirement for admin initialization and restore on an uninitialized instance").Envar(portainer.NoSetupTokenEnvVar).Bool(),
+ SetupToken: kingpin.Flag("setup-token", "Set a custom setup token for admin initialization and restore on an uninitialized instance (overrides auto-generation)").Envar(portainer.SetupTokenEnvVar).String(),
}
}
diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go
index 3e2432f8d..1d77814e4 100644
--- a/api/cmd/portainer/main.go
+++ b/api/cmd/portainer/main.go
@@ -4,6 +4,7 @@ import (
"cmp"
"context"
"crypto/sha256"
+ nethttp "net/http"
"os"
"path"
"strings"
@@ -29,6 +30,7 @@ import (
"github.com/portainer/portainer/api/http"
"github.com/portainer/portainer/api/http/proxy"
kubeproxy "github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
+ "github.com/portainer/portainer/api/http/security/setuptoken"
"github.com/portainer/portainer/api/internal/authorization"
"github.com/portainer/portainer/api/internal/edge/edgestacks"
"github.com/portainer/portainer/api/internal/endpointutils"
@@ -51,10 +53,12 @@ import (
"github.com/portainer/portainer/pkg/featureflags"
"github.com/portainer/portainer/pkg/fips"
"github.com/portainer/portainer/pkg/libhelm"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/portainer/portainer/pkg/libstack/compose"
libswarm "github.com/portainer/portainer/pkg/libstack/swarm"
"github.com/portainer/portainer/pkg/validate"
+ gogithttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
@@ -225,6 +229,32 @@ func initSnapshotService(
return snapshotService, nil
}
+func resolveSetupToken(tx dataservices.DataStoreTx, providedToken string) (string, error) {
+ admins, err := tx.User().UsersByRole(portainer.AdministratorRole)
+ if err != nil {
+ return "", err
+ }
+ if len(admins) > 0 {
+ return "", nil
+ }
+
+ if providedToken != "" {
+ log.Info().Msg("using custom setup token; admin initialization and backup restore require this token in the X-Setup-Token header")
+ return providedToken, nil
+ }
+
+ token, err := setuptoken.Generate()
+ if err != nil {
+ return "", err
+ }
+
+ log.Info().
+ Str("setup_token", token).
+ Msg("no administrator account configured; admin initialization and backup restore require this setup token in the X-Setup-Token header. Start with --no-setup-token to disable.")
+
+ return token, nil
+}
+
func initStatus(instanceID string) *portainer.Status {
return &portainer.Status{
Version: portainer.APIVersion,
@@ -357,6 +387,19 @@ func buildServer(flags *portainer.CLIFlags, shutdownCtx context.Context, shutdow
// -ce can not ever be run in FIPS mode
fips.InitFIPS(false)
+ ssrf.Configure(ssrf.Policy{
+ Mode: ssrf.Mode(*flags.SSRFMode),
+ AllowedHosts: *flags.SSRFAllowedHosts,
+ })
+
+ if ssrf.IsEnabled() {
+ if dt, ok := nethttp.DefaultTransport.(*nethttp.Transport); ok {
+ nethttp.DefaultTransport = ssrf.WrapTransport(dt)
+ }
+
+ gogithttp.DefaultClient = gogithttp.NewClient(&nethttp.Client{Transport: nethttp.DefaultTransport})
+ }
+
fileService := initFileService(*flags.Data)
encryptionKey := loadEncryptionSecretKey(dbSecretPath(*flags.SecretKeyName))
if encryptionKey == nil {
@@ -510,6 +553,17 @@ func buildServer(flags *portainer.CLIFlags, shutdownCtx context.Context, shutdow
}
}
+ setupToken := ""
+ if adminPasswordHash == "" && !*flags.NoSetupToken {
+ if err := dataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
+ var txErr error
+ setupToken, txErr = resolveSetupToken(tx, *flags.SetupToken)
+ return txErr
+ }); err != nil {
+ log.Fatal().Err(err).Msg("failed initializing setup token")
+ }
+ }
+
if err := reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotService); err != nil {
log.Fatal().Err(err).Msg("failed starting tunnel server")
}
@@ -601,6 +655,7 @@ func buildServer(flags *portainer.CLIFlags, shutdownCtx context.Context, shutdow
PlatformService: platformService,
PullLimitCheckDisabled: *flags.PullLimitCheckDisabled,
TrustedOrigins: trustedOrigins,
+ SetupToken: setupToken,
}
}
diff --git a/api/cmd/portainer/main_test.go b/api/cmd/portainer/main_test.go
index 34840a9d0..de9e4c104 100644
--- a/api/cmd/portainer/main_test.go
+++ b/api/cmd/portainer/main_test.go
@@ -12,6 +12,44 @@ import (
"github.com/stretchr/testify/require"
)
+func Test_resolveSetupToken(t *testing.T) {
+ t.Parallel()
+
+ t.Run("admin already exists — returns empty token", func(t *testing.T) {
+ admin := portainer.User{Role: portainer.AdministratorRole}
+ store := testhelpers.NewDatastore(testhelpers.WithUsers([]portainer.User{admin}))
+ token, err := resolveSetupToken(store, "")
+ require.NoError(t, err)
+ assert.Empty(t, token)
+ })
+
+ t.Run("no admin — generates a 64-char hex token", func(t *testing.T) {
+ store := testhelpers.NewDatastore(testhelpers.WithUsers([]portainer.User{}))
+ token, err := resolveSetupToken(store, "")
+ require.NoError(t, err)
+ assert.Len(t, token, 64)
+
+ token2, err := resolveSetupToken(store, "")
+ require.NoError(t, err)
+ assert.NotEqual(t, token, token2)
+ })
+
+ t.Run("no admin — uses provided token", func(t *testing.T) {
+ store := testhelpers.NewDatastore(testhelpers.WithUsers([]portainer.User{}))
+ token, err := resolveSetupToken(store, "mysecrettoken")
+ require.NoError(t, err)
+ assert.Equal(t, "mysecrettoken", token)
+ })
+
+ t.Run("admin already exists — ignores provided token", func(t *testing.T) {
+ admin := portainer.User{Role: portainer.AdministratorRole}
+ store := testhelpers.NewDatastore(testhelpers.WithUsers([]portainer.User{admin}))
+ token, err := resolveSetupToken(store, "mysecrettoken")
+ require.NoError(t, err)
+ assert.Empty(t, token)
+ })
+}
+
const secretFileName = "secret.txt"
func createPasswordFile(t *testing.T, secretPath, password string) string {
diff --git a/api/crypto/nonce.go b/api/crypto/nonce.go
index af1cc898b..4753374a6 100644
--- a/api/crypto/nonce.go
+++ b/api/crypto/nonce.go
@@ -4,6 +4,7 @@ import (
"crypto/rand"
"errors"
"io"
+ "slices"
)
type Nonce struct {
@@ -45,7 +46,7 @@ func (n *Nonce) Value() []byte {
func (n *Nonce) Increment() error {
// Start incrementing from the least significant byte
- for i := len(n.val) - 1; i >= 0; i-- {
+ for i := range slices.Backward(n.val) {
// Increment the current byte
n.val[i]++
diff --git a/api/datastore/migrate_data.go b/api/datastore/migrate_data.go
index 74a033334..4a2e9de7e 100644
--- a/api/datastore/migrate_data.go
+++ b/api/datastore/migrate_data.go
@@ -88,6 +88,7 @@ func (store *Store) newMigratorParameters(version *models.Version, flags *portai
EdgeGroupService: store.EdgeGroupService,
TunnelServerService: store.TunnelServerService,
PendingActionsService: store.PendingActionsService,
+ CustomTemplateService: store.CustomTemplateService,
SourceService: store.SourceService,
WorkflowService: store.WorkflowService,
}
diff --git a/api/datastore/migrator/migrate_2_43_0.go b/api/datastore/migrator/migrate_2_43_0.go
index 54a2f38a5..6455ee61b 100644
--- a/api/datastore/migrator/migrate_2_43_0.go
+++ b/api/datastore/migrator/migrate_2_43_0.go
@@ -112,8 +112,8 @@ func (m *Migrator) migrateGitConfigToSources_2_43_0() error {
sourcesByKey := make(map[sourceDedupeKey]portainer.SourceID, len(existingSources))
for _, src := range existingSources {
- if src.GitConfig != nil {
- sourcesByKey[gitSourceKey(src.GitConfig)] = src.ID
+ if src.Git != nil {
+ sourcesByKey[gitSourceKey(src.Git)] = src.ID
}
}
@@ -133,9 +133,9 @@ func (m *Migrator) migrateGitConfigToSources_2_43_0() error {
if !exists {
src := &portainer.Source{
- Name: gittypes.RepoName(cfg.URL),
- Type: portainer.SourceTypeGit,
- GitConfig: cfg,
+ Name: gittypes.RepoName(cfg.URL),
+ Type: portainer.SourceTypeGit,
+ Git: cfg,
}
if err := m.sourceService.Tx(tx).Create(src); err != nil {
return fmt.Errorf("failed to create source for stack %d: %w", ls.ID, err)
@@ -151,14 +151,14 @@ func (m *Migrator) migrateGitConfigToSources_2_43_0() error {
wf := &portainer.Workflow{
Name: liveStack.Name,
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- ReferenceName: cfg.ReferenceName,
- ConfigFilePath: cfg.ConfigFilePath,
- ConfigHash: cfg.ConfigHash,
- StackID: portainer.StackID(ls.ID),
- },
- SourceIDs: []portainer.SourceID{srcID},
+ Artifacts: []portainer.Artifact{{
+ StackID: portainer.StackID(ls.ID),
+ Files: []portainer.ArtifactFile{{
+ SourceID: srcID,
+ Path: cfg.ConfigFilePath,
+ Ref: cfg.ReferenceName,
+ Hash: cfg.ConfigHash,
+ }},
}},
}
if err := m.workflowService.Tx(tx).Create(wf); err != nil {
@@ -180,3 +180,86 @@ func (m *Migrator) migrateGitConfigToSources_2_43_0() error {
return nil
}
+
+func (m *Migrator) migrateCustomTemplateGitConfigToSources_2_43_0() error {
+ log.Info().Msg("migrating git-backed custom templates to Source records")
+
+ templates, err := m.customTemplateService.ReadAll()
+ if err != nil {
+ return err
+ }
+
+ existingSources, err := m.sourceService.ReadAll()
+ if err != nil {
+ return err
+ }
+
+ sourcesByKey := make(map[sourceDedupeKey]portainer.SourceID, len(existingSources))
+ for _, src := range existingSources {
+ if src.Git != nil {
+ sourcesByKey[gitSourceKey(src.Git)] = src.ID
+ }
+ }
+
+ for i := range templates {
+ t := &templates[i]
+ if t.GitConfig == nil || t.Artifact != nil {
+ continue
+ }
+
+ cfg := &gittypes.RepoConfig{
+ URL: gittypes.SanitizeURL(t.GitConfig.URL),
+ Authentication: t.GitConfig.Authentication,
+ TLSSkipVerify: t.GitConfig.TLSSkipVerify,
+ }
+
+ if cfg.Authentication != nil && cfg.Authentication.GitCredentialID != 0 {
+ log.Warn().
+ Int("git_credential_id", cfg.Authentication.GitCredentialID).
+ Msg("custom template has a GitCredentialID reference which is not supported in CE; credential reference will be dropped during migration")
+
+ cfg.Authentication.GitCredentialID = 0
+ }
+
+ key := gitSourceKey(cfg)
+
+ var newSrcID portainer.SourceID
+
+ if err := m.stackService.Connection.UpdateTx(func(tx portainer.Transaction) error {
+ srcID, exists := sourcesByKey[key]
+
+ if !exists {
+ src := &portainer.Source{
+ Name: gittypes.RepoName(cfg.URL),
+ Type: portainer.SourceTypeGit,
+ Git: cfg,
+ }
+ if err := m.sourceService.Tx(tx).Create(src); err != nil {
+ return fmt.Errorf("failed to create source for custom template %d: %w", t.ID, err)
+ }
+ srcID = src.ID
+ newSrcID = src.ID
+ }
+
+ t.Artifact = &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{
+ SourceID: srcID,
+ Path: t.GitConfig.ConfigFilePath,
+ Ref: t.GitConfig.ReferenceName,
+ Hash: t.GitConfig.ConfigHash,
+ }},
+ }
+ t.GitConfig = nil
+
+ return m.customTemplateService.Tx(tx).Update(t.ID, t)
+ }); err != nil {
+ return fmt.Errorf("failed to migrate custom template %d: %w", t.ID, err)
+ }
+
+ if newSrcID != 0 {
+ sourcesByKey[key] = newSrcID
+ }
+ }
+
+ return nil
+}
diff --git a/api/datastore/migrator/migrate_2_43_0_test.go b/api/datastore/migrator/migrate_2_43_0_test.go
index 9b6c72224..4c333e478 100644
--- a/api/datastore/migrator/migrate_2_43_0_test.go
+++ b/api/datastore/migrator/migrate_2_43_0_test.go
@@ -5,6 +5,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/database/boltdb"
+ "github.com/portainer/portainer/api/dataservices/customtemplate"
"github.com/portainer/portainer/api/dataservices/source"
"github.com/portainer/portainer/api/dataservices/stack"
"github.com/portainer/portainer/api/dataservices/workflow"
@@ -58,13 +59,13 @@ func TestMigrateGitConfigToSources_2_43_0_GitStackMigrated(t *testing.T) {
wf, err := workflowSvc.Read(migrated.WorkflowID)
require.NoError(t, err)
require.Len(t, wf.Artifacts, 1)
- require.Len(t, wf.Artifacts[0].SourceIDs, 1)
+ require.Len(t, wf.Artifacts[0].Files, 1)
- src, err := sourceSvc.Read(wf.Artifacts[0].SourceIDs[0])
+ src, err := sourceSvc.Read(wf.Artifacts[0].Files[0].SourceID)
require.NoError(t, err)
require.Equal(t, portainer.SourceTypeGit, src.Type)
- require.Equal(t, gitStack.GitConfig.URL, src.GitConfig.URL)
- require.Equal(t, gitStack.GitConfig.ReferenceName, src.GitConfig.ReferenceName)
+ require.Equal(t, gitStack.GitConfig.URL, src.Git.URL)
+ require.Equal(t, gitStack.GitConfig.ReferenceName, src.Git.ReferenceName)
}
func TestMigrateGitConfigToSources_2_43_0_NonGitStackUntouched(t *testing.T) {
@@ -170,8 +171,8 @@ func TestMigrateGitConfigToSources_2_43_0_DuplicateSourcesDeduped(t *testing.T)
sharedSourceID := sources[0].ID
for _, wf := range workflows {
require.Len(t, wf.Artifacts, 1)
- require.Len(t, wf.Artifacts[0].SourceIDs, 1)
- require.Equal(t, sharedSourceID, wf.Artifacts[0].SourceIDs[0])
+ require.Len(t, wf.Artifacts[0].Files, 1)
+ require.Equal(t, sharedSourceID, wf.Artifacts[0].Files[0].SourceID)
}
}
@@ -221,3 +222,241 @@ func TestMigrateGitConfigToSources_2_43_0_Idempotent(t *testing.T) {
require.NoError(t, err)
require.Len(t, workflows, 1)
}
+
+func TestMigrateCustomTemplateGitConfigToSources_2_43_0_GitTemplateMigrated(t *testing.T) {
+ t.Parallel()
+
+ conn := &boltdb.DbConnection{Path: t.TempDir()}
+ err := conn.Open()
+ require.NoError(t, err)
+ defer logs.CloseAndLogErr(conn)
+
+ stackSvc, err := stack.NewService(conn)
+ require.NoError(t, err)
+ sourceSvc, err := source.NewService(conn)
+ require.NoError(t, err)
+ customTemplateSvc, err := customtemplate.NewService(conn)
+ require.NoError(t, err)
+
+ m := NewMigrator(&MigratorParameters{
+ StackService: stackSvc,
+ SourceService: sourceSvc,
+ CustomTemplateService: customTemplateSvc,
+ })
+
+ tmpl := &portainer.CustomTemplate{
+ ID: 1,
+ GitConfig: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ ReferenceName: "refs/heads/main",
+ ConfigFilePath: "docker-compose.yml",
+ ConfigHash: "abc123",
+ },
+ }
+ err = conn.CreateObjectWithId(customtemplate.BucketName, int(tmpl.ID), tmpl)
+ require.NoError(t, err)
+
+ err = m.migrateCustomTemplateGitConfigToSources_2_43_0()
+ require.NoError(t, err)
+
+ migrated, err := customTemplateSvc.Read(tmpl.ID)
+ require.NoError(t, err)
+ require.NotNil(t, migrated.Artifact)
+ require.Nil(t, migrated.GitConfig)
+ require.Len(t, migrated.Artifact.Files, 1)
+ require.Equal(t, "refs/heads/main", migrated.Artifact.Files[0].Ref)
+ require.Equal(t, "docker-compose.yml", migrated.Artifact.Files[0].Path)
+ require.Equal(t, "abc123", migrated.Artifact.Files[0].Hash)
+
+ src, err := sourceSvc.Read(migrated.Artifact.Files[0].SourceID)
+ require.NoError(t, err)
+ require.Equal(t, portainer.SourceTypeGit, src.Type)
+ require.Equal(t, "https://github.com/example/repo", src.Git.URL)
+}
+
+func TestMigrateCustomTemplateGitConfigToSources_2_43_0_NonGitTemplateUntouched(t *testing.T) {
+ t.Parallel()
+
+ conn := &boltdb.DbConnection{Path: t.TempDir()}
+ err := conn.Open()
+ require.NoError(t, err)
+ defer logs.CloseAndLogErr(conn)
+
+ stackSvc, err := stack.NewService(conn)
+ require.NoError(t, err)
+ sourceSvc, err := source.NewService(conn)
+ require.NoError(t, err)
+ customTemplateSvc, err := customtemplate.NewService(conn)
+ require.NoError(t, err)
+
+ m := NewMigrator(&MigratorParameters{
+ StackService: stackSvc,
+ SourceService: sourceSvc,
+ CustomTemplateService: customTemplateSvc,
+ })
+
+ tmpl := &portainer.CustomTemplate{ID: 1, Title: "plain-template"}
+ err = conn.CreateObjectWithId(customtemplate.BucketName, int(tmpl.ID), tmpl)
+ require.NoError(t, err)
+
+ err = m.migrateCustomTemplateGitConfigToSources_2_43_0()
+ require.NoError(t, err)
+
+ result, err := customTemplateSvc.Read(tmpl.ID)
+ require.NoError(t, err)
+ require.Nil(t, result.Artifact)
+ require.Nil(t, result.GitConfig)
+
+ sources, err := sourceSvc.ReadAll()
+ require.NoError(t, err)
+ require.Empty(t, sources)
+}
+
+func TestMigrateCustomTemplateGitConfigToSources_2_43_0_AlreadyMigratedSkipped(t *testing.T) {
+ t.Parallel()
+
+ conn := &boltdb.DbConnection{Path: t.TempDir()}
+ err := conn.Open()
+ require.NoError(t, err)
+ defer logs.CloseAndLogErr(conn)
+
+ stackSvc, err := stack.NewService(conn)
+ require.NoError(t, err)
+ sourceSvc, err := source.NewService(conn)
+ require.NoError(t, err)
+ customTemplateSvc, err := customtemplate.NewService(conn)
+ require.NoError(t, err)
+
+ m := NewMigrator(&MigratorParameters{
+ StackService: stackSvc,
+ SourceService: sourceSvc,
+ CustomTemplateService: customTemplateSvc,
+ })
+
+ // Template already has Artifact set (already migrated)
+ srcID := portainer.SourceID(99)
+ tmpl := &portainer.CustomTemplate{
+ ID: 1,
+ GitConfig: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ },
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{SourceID: srcID}},
+ },
+ }
+ err = conn.CreateObjectWithId(customtemplate.BucketName, int(tmpl.ID), tmpl)
+ require.NoError(t, err)
+
+ err = m.migrateCustomTemplateGitConfigToSources_2_43_0()
+ require.NoError(t, err)
+
+ sources, err := sourceSvc.ReadAll()
+ require.NoError(t, err)
+ require.Empty(t, sources, "no new sources should be created for already-migrated templates")
+}
+
+func TestMigrateCustomTemplateGitConfigToSources_2_43_0_DuplicateSourcesDeduped(t *testing.T) {
+ t.Parallel()
+
+ conn := &boltdb.DbConnection{Path: t.TempDir()}
+ err := conn.Open()
+ require.NoError(t, err)
+ defer logs.CloseAndLogErr(conn)
+
+ stackSvc, err := stack.NewService(conn)
+ require.NoError(t, err)
+ sourceSvc, err := source.NewService(conn)
+ require.NoError(t, err)
+ customTemplateSvc, err := customtemplate.NewService(conn)
+ require.NoError(t, err)
+
+ m := NewMigrator(&MigratorParameters{
+ StackService: stackSvc,
+ SourceService: sourceSvc,
+ CustomTemplateService: customTemplateSvc,
+ })
+
+ sharedURL := "https://github.com/example/shared-repo"
+
+ tmpl1 := &portainer.CustomTemplate{
+ ID: 1,
+ Title: "template-a",
+ GitConfig: &gittypes.RepoConfig{
+ URL: sharedURL,
+ ReferenceName: "refs/heads/main",
+ },
+ }
+ tmpl2 := &portainer.CustomTemplate{
+ ID: 2,
+ Title: "template-b",
+ GitConfig: &gittypes.RepoConfig{
+ URL: sharedURL,
+ ReferenceName: "refs/heads/develop",
+ },
+ }
+ err = conn.CreateObjectWithId(customtemplate.BucketName, int(tmpl1.ID), tmpl1)
+ require.NoError(t, err)
+ err = conn.CreateObjectWithId(customtemplate.BucketName, int(tmpl2.ID), tmpl2)
+ require.NoError(t, err)
+
+ err = m.migrateCustomTemplateGitConfigToSources_2_43_0()
+ require.NoError(t, err)
+
+ sources, err := sourceSvc.ReadAll()
+ require.NoError(t, err)
+ require.Len(t, sources, 1, "two templates with the same URL must share one Source")
+
+ sharedSrcID := sources[0].ID
+
+ migrated1, err := customTemplateSvc.Read(tmpl1.ID)
+ require.NoError(t, err)
+ require.NotNil(t, migrated1.Artifact)
+ require.Equal(t, sharedSrcID, migrated1.Artifact.Files[0].SourceID)
+
+ migrated2, err := customTemplateSvc.Read(tmpl2.ID)
+ require.NoError(t, err)
+ require.NotNil(t, migrated2.Artifact)
+ require.Equal(t, sharedSrcID, migrated2.Artifact.Files[0].SourceID)
+}
+
+func TestMigrateCustomTemplateGitConfigToSources_2_43_0_Idempotent(t *testing.T) {
+ t.Parallel()
+
+ conn := &boltdb.DbConnection{Path: t.TempDir()}
+ err := conn.Open()
+ require.NoError(t, err)
+ defer logs.CloseAndLogErr(conn)
+
+ stackSvc, err := stack.NewService(conn)
+ require.NoError(t, err)
+ sourceSvc, err := source.NewService(conn)
+ require.NoError(t, err)
+ customTemplateSvc, err := customtemplate.NewService(conn)
+ require.NoError(t, err)
+
+ m := NewMigrator(&MigratorParameters{
+ StackService: stackSvc,
+ SourceService: sourceSvc,
+ CustomTemplateService: customTemplateSvc,
+ })
+
+ tmpl := &portainer.CustomTemplate{
+ ID: 1,
+ GitConfig: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ },
+ }
+ err = conn.CreateObjectWithId(customtemplate.BucketName, int(tmpl.ID), tmpl)
+ require.NoError(t, err)
+
+ err = m.migrateCustomTemplateGitConfigToSources_2_43_0()
+ require.NoError(t, err)
+
+ // Second run must not create duplicate Source records
+ err = m.migrateCustomTemplateGitConfigToSources_2_43_0()
+ require.NoError(t, err)
+
+ sources, err := sourceSvc.ReadAll()
+ require.NoError(t, err)
+ require.Len(t, sources, 1)
+}
diff --git a/api/datastore/migrator/migrator.go b/api/datastore/migrator/migrator.go
index 3a4bfa91c..f7fd38fdc 100644
--- a/api/datastore/migrator/migrator.go
+++ b/api/datastore/migrator/migrator.go
@@ -5,6 +5,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/database/models"
+ "github.com/portainer/portainer/api/dataservices/customtemplate"
"github.com/portainer/portainer/api/dataservices/dockerhub"
"github.com/portainer/portainer/api/dataservices/edgegroup"
"github.com/portainer/portainer/api/dataservices/edgejob"
@@ -66,6 +67,7 @@ type (
edgeGroupService *edgegroup.Service
TunnelServerService *tunnelserver.Service
pendingActionsService *pendingactions.Service
+ customTemplateService *customtemplate.Service
sourceService *source.Service
workflowService *workflow.Service
}
@@ -98,6 +100,7 @@ type (
EdgeGroupService *edgegroup.Service
TunnelServerService *tunnelserver.Service
PendingActionsService *pendingactions.Service
+ CustomTemplateService *customtemplate.Service
SourceService *source.Service
WorkflowService *workflow.Service
}
@@ -132,6 +135,7 @@ func NewMigrator(parameters *MigratorParameters) *Migrator {
edgeGroupService: parameters.EdgeGroupService,
TunnelServerService: parameters.TunnelServerService,
pendingActionsService: parameters.PendingActionsService,
+ customTemplateService: parameters.CustomTemplateService,
sourceService: parameters.SourceService,
workflowService: parameters.WorkflowService,
}
@@ -268,7 +272,10 @@ func (m *Migrator) initMigrations() {
m.addMigrations("2.40.0", m.migrateRegistryAccessSASecrets_2_40_0)
- m.addMigrations("2.43.0", m.migrateGitConfigToSources_2_43_0)
+ m.addMigrations("2.43.0",
+ m.migrateGitConfigToSources_2_43_0,
+ m.migrateCustomTemplateGitConfigToSources_2_43_0,
+ )
// WARNING: do not change migrations that have already been released!
diff --git a/api/datastore/test_data/output_24_to_latest.json b/api/datastore/test_data/output_24_to_latest.json
index ca4cb9c2c..309a41095 100644
--- a/api/datastore/test_data/output_24_to_latest.json
+++ b/api/datastore/test_data/output_24_to_latest.json
@@ -920,7 +920,7 @@
}
],
"version": {
- "VERSION": "{\"SchemaVersion\":\"2.43.0\",\"MigratorCount\":1,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
+ "VERSION": "{\"SchemaVersion\":\"2.43.0\",\"MigratorCount\":2,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
},
"webhooks": null,
"workflows": null
diff --git a/api/docker/client/client.go b/api/docker/client/client.go
index 86cc1fa4b..6ef575b75 100644
--- a/api/docker/client/client.go
+++ b/api/docker/client/client.go
@@ -11,6 +11,8 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
+
"github.com/rs/zerolog/log"
"github.com/docker/docker/api/types/image"
@@ -184,17 +186,20 @@ func (t *NodeNameTransport) RoundTrip(req *http.Request) (*http.Response, error)
}
func httpClient(endpoint *portainer.Endpoint, timeout *time.Duration) (*http.Client, error) {
- transport := &NodeNameTransport{
- Transport: &http.Transport{},
- }
-
+ var transport *NodeNameTransport
if endpoint.TLSConfig.TLS {
tlsConfig, err := crypto.CreateTLSConfigurationFromDisk(endpoint.TLSConfig)
if err != nil {
return nil, err
}
- transport.TLSClientConfig = tlsConfig
+ transport = &NodeNameTransport{
+ Transport: ssrf.WrapTransport(&http.Transport{TLSClientConfig: tlsConfig}),
+ }
+ } else {
+ transport = &NodeNameTransport{
+ Transport: ssrf.WrapTransport(&http.Transport{}),
+ }
}
clientTimeout := defaultDockerRequestTimeout
diff --git a/api/exec/common.go b/api/exec/common.go
index 5d2b96078..593b2042c 100644
--- a/api/exec/common.go
+++ b/api/exec/common.go
@@ -38,7 +38,7 @@ func fetchEndpointProxy(proxyManager *proxy.Manager, endpoint *portainer.Endpoin
// portainerRegistriesToAuthConfigs converts registries to Docker auth configs.
// Callers must ensure ECR tokens are valid before calling this function (e.g. via
-// registryutils.ValidateRegistriesECRTokens with a real DataStoreTx). This function
+// registryutils.RefreshAndPersistECRTokens with a real DataStoreTx). This function
// intentionally performs no DB writes to avoid write-lock contention when called inside
// an active BoltDB write transaction.
func portainerRegistriesToAuthConfigs(registries []portainer.Registry) []types.AuthConfig {
diff --git a/api/git/azure.go b/api/git/azure.go
index 79e5c340c..2acf14b23 100644
--- a/api/git/azure.go
+++ b/api/git/azure.go
@@ -14,12 +14,13 @@ import (
"github.com/portainer/portainer/api/crypto"
gittypes "github.com/portainer/portainer/api/git/types"
"github.com/portainer/portainer/api/logs"
- "github.com/rs/zerolog/log"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/filemode"
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/pkg/errors"
+ "github.com/rs/zerolog/log"
"github.com/segmentio/encoding/json"
)
@@ -64,15 +65,13 @@ func NewAzureClient() *azureClient {
}
func newHttpClientForAzure(insecureSkipVerify bool) *http.Client {
- httpsCli := &http.Client{
- Transport: &http.Transport{
+ return &http.Client{
+ Transport: ssrf.WrapTransport(&http.Transport{
TLSClientConfig: crypto.CreateTLSConfiguration(insecureSkipVerify),
Proxy: http.ProxyFromEnvironment,
- },
+ }),
Timeout: 300 * time.Second,
}
-
- return httpsCli
}
func (a *azureClient) Download(ctx context.Context, destination string, opt *git.CloneOptions) error {
diff --git a/api/gitops/workflows/fetch.go b/api/gitops/workflows/fetch.go
index f657498ba..a8a018845 100644
--- a/api/gitops/workflows/fetch.go
+++ b/api/gitops/workflows/fetch.go
@@ -9,8 +9,6 @@ import (
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/kubernetes/cli"
"github.com/portainer/portainer/api/set"
-
- "github.com/rs/zerolog/log"
)
// FetchWorkflows returns all GitOps workflows visible to the given user.
@@ -43,69 +41,35 @@ func FetchWorkflows(
// First pass: filter by endpoint/stack-type match and collect workflow IDs.
preFiltered := make([]portainer.Stack, 0, len(stacks))
- workflowIDSet := make(map[portainer.WorkflowID]struct{}, len(stacks))
+ workflowIDSet := make(set.Set[portainer.WorkflowID], len(stacks))
for _, stack := range stacks {
if ep, ok := endpointMap[stack.EndpointID]; ok && !EndpointMatchesStackType(ep, stack.Type) {
continue
}
preFiltered = append(preFiltered, stack)
- workflowIDSet[stack.WorkflowID] = struct{}{}
+ workflowIDSet.Add(stack.WorkflowID)
}
- // Batch-load all needed workflows in one scan.
- wfs, err := tx.Workflow().ReadAll(func(wf portainer.Workflow) bool {
- _, ok := workflowIDSet[wf.ID]
- return ok
- })
+ workflowMap, sourceMap, err := LoadWorkflowAndSourceMaps(tx, workflowIDSet)
if err != nil {
return nil, err
}
- workflowMap := make(map[portainer.WorkflowID]portainer.Workflow, len(wfs))
- var allArtifacts []portainer.ArtifactSources
- for _, wf := range wfs {
- workflowMap[wf.ID] = wf
- allArtifacts = append(allArtifacts, wf.Artifacts...)
- }
- sourceSet := ArtifactsToSourceSet(allArtifacts...)
-
- // Batch-load all needed sources in one scan.
- srcs, err := tx.Source().ReadAll(func(src portainer.Source) bool {
- return sourceSet.Contains(src.ID)
- })
- if err != nil {
- return nil, err
- }
-
- sourceMap := make(map[portainer.SourceID]portainer.Source, len(srcs))
- for _, src := range srcs {
- sourceMap[src.ID] = src
- }
-
// Second pass: build filtered list using in-memory lookups.
var filtered []portainer.Stack
for _, stack := range preFiltered {
- wf, ok := workflowMap[stack.WorkflowID]
- if !ok {
- log.Warn().Int("stackID", int(stack.ID)).Msg("workflow record missing for stack, skipping")
- continue
- }
+ wf := workflowMap[stack.WorkflowID]
outer:
for _, as := range wf.Artifacts {
- if as.Artifact.StackID != stack.ID {
+ if as.StackID != stack.ID {
continue
}
- for _, srcID := range as.SourceIDs {
- src, ok := sourceMap[srcID]
- if !ok {
- log.Warn().Int("stackID", int(stack.ID)).Msg("source record missing for stack, skipping")
- break outer
- }
-
+ for _, f := range as.Files {
+ src := sourceMap[f.SourceID]
if src.Type == portainer.SourceTypeGit {
- gitConfigs[stack.ID] = MergeSourceAndArtifact(&src, &as.Artifact)
+ gitConfigs[stack.ID] = MergeSourceAndFile(&src, &f)
break outer
}
}
@@ -169,28 +133,27 @@ func FetchSourceStats(
return nil, nil, err
}
- workflowIDSet := make(map[portainer.WorkflowID]struct{}, len(allStacks))
+ workflowIDSet := make(set.Set[portainer.WorkflowID], len(allStacks))
preFiltered := make([]portainer.Stack, 0, len(allStacks))
for _, stack := range allStacks {
if ep, ok := endpointMap[stack.EndpointID]; ok && !EndpointMatchesStackType(ep, stack.Type) {
continue
}
preFiltered = append(preFiltered, stack)
- workflowIDSet[stack.WorkflowID] = struct{}{}
+ workflowIDSet.Add(stack.WorkflowID)
}
- wfs, err := tx.Workflow().ReadAll(func(wf portainer.Workflow) bool {
- _, ok := workflowIDSet[wf.ID]
- return ok
- })
+ wfMap, err := LoadWorkflowMap(tx, workflowIDSet)
if err != nil {
return nil, nil, err
}
- wfSources := make(map[portainer.WorkflowID][]portainer.SourceID, len(wfs))
- for _, wf := range wfs {
+ wfSources := make(map[portainer.WorkflowID][]portainer.SourceID, len(wfMap))
+ for id, wf := range wfMap {
for _, as := range wf.Artifacts {
- wfSources[wf.ID] = append(wfSources[wf.ID], as.SourceIDs...)
+ for _, f := range as.Files {
+ wfSources[id] = append(wfSources[id], f.SourceID)
+ }
}
}
diff --git a/api/gitops/workflows/fetch_test.go b/api/gitops/workflows/fetch_test.go
index fdfbedd26..481eca78d 100644
--- a/api/gitops/workflows/fetch_test.go
+++ b/api/gitops/workflows/fetch_test.go
@@ -23,12 +23,12 @@ func mustCreateGitWorkflow(t *testing.T, tx dataservices.DataStoreTx, stack *por
cfg := stack.GitConfig
- src := &portainer.Source{Type: portainer.SourceTypeGit, GitConfig: cfg}
+ src := &portainer.Source{Type: portainer.SourceTypeGit, Git: cfg}
require.NoError(t, tx.Source().Create(src))
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: stack.ID},
- SourceIDs: []portainer.SourceID{src.ID},
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{
+ StackID: stack.ID,
+ Files: []portainer.ArtifactFile{{SourceID: src.ID}},
}}}
require.NoError(t, tx.Workflow().Create(wf))
@@ -228,7 +228,7 @@ func TestFetchSourceStats_TracksWorkflowCountAndEndpoints(t *testing.T) {
srcID = src.ID
for i := 1; i <= 2; i++ {
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{SourceIDs: []portainer.SourceID{srcID}}}}
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: srcID}}}}}
require.NoError(t, tx.Workflow().Create(wf))
require.NoError(t, tx.Stack().Create(&portainer.Stack{
ID: portainer.StackID(i),
diff --git a/api/gitops/workflows/mapping.go b/api/gitops/workflows/mapping.go
index fec1c70ec..df0eca4be 100644
--- a/api/gitops/workflows/mapping.go
+++ b/api/gitops/workflows/mapping.go
@@ -1,6 +1,8 @@
package workflows
import (
+ "slices"
+
portainer "github.com/portainer/portainer/api"
gittypes "github.com/portainer/portainer/api/git/types"
"github.com/portainer/portainer/api/set"
@@ -61,9 +63,9 @@ func MapEdgeStackToWorkflow(es portainer.EdgeStack, gitConfig *gittypes.RepoConf
}
func StackLastSyncDate(s portainer.Stack) int64 {
- for i := len(s.DeploymentStatus) - 1; i >= 0; i-- {
- if s.DeploymentStatus[i].Status == portainer.StackStatusActive {
- return s.DeploymentStatus[i].Time
+ for _, ds := range slices.Backward(s.DeploymentStatus) {
+ if ds.Status == portainer.StackStatusActive {
+ return ds.Time
}
}
return 0
@@ -84,9 +86,9 @@ func edgeStackLastSyncDate(statuses []portainer.EdgeStackStatusForEnv) int64 {
}
func endpointLastSyncDate(epStatus portainer.EdgeStackStatusForEnv) int64 {
- for i := len(epStatus.Status) - 1; i >= 0; i-- {
- if isEdgeStackHealthyStatus(epStatus.Status[i].Type) {
- return epStatus.Status[i].Time
+ for _, s := range slices.Backward(epStatus.Status) {
+ if isEdgeStackHealthyStatus(s.Type) {
+ return s.Time
}
}
return 0
@@ -115,18 +117,6 @@ func isEdgeStackHealthyStatus(t portainer.EdgeStackStatusType) bool {
return false
}
-// ArtifactsToSourceSet returns the set of all SourceIDs referenced by the given artifact-source mappings
-func ArtifactsToSourceSet(artifacts ...portainer.ArtifactSources) set.Set[portainer.SourceID] {
- s := make(set.Set[portainer.SourceID])
- for _, a := range artifacts {
- for _, sid := range a.SourceIDs {
- s.Add(sid)
- }
- }
-
- return s
-}
-
func resolveEdgeGroupEndpoints(groups []portainer.EdgeGroupID, groupEndpoints map[portainer.EdgeGroupID][]portainer.EndpointID) []portainer.EndpointID {
seen := set.Set[portainer.EndpointID]{}
for _, gid := range groups {
diff --git a/api/gitops/workflows/source_artifact.go b/api/gitops/workflows/source_artifact.go
index e42ac66ad..c0681707b 100644
--- a/api/gitops/workflows/source_artifact.go
+++ b/api/gitops/workflows/source_artifact.go
@@ -6,6 +6,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
gittypes "github.com/portainer/portainer/api/git/types"
+ "github.com/portainer/portainer/api/set"
)
// gitSourceStore is the minimal intersection of CE and EE DataStoreTx that these functions need.
@@ -14,11 +15,11 @@ type gitSourceStore interface {
Source() dataservices.SourceService
}
-// GitSourceAndArtifactForStack returns the git Source and the Artifact matching stackID
+// GitSourceAndArtifactForStack returns the git Source and the ArtifactFile matching stackID
// from the workflow identified by workflowID.
-// Source carries the shared fields (URL, auth, TLS); Artifact carries the stack-specific fields (ref, path, hash).
+// Source carries the shared fields (URL, auth, TLS); ArtifactFile carries the file-specific fields (ref, path, hash).
// Returns nil, nil, nil when workflowID is 0 or no matching entry is found.
-func GitSourceAndArtifactForStack(tx gitSourceStore, workflowID portainer.WorkflowID, stackID portainer.StackID) (*portainer.Source, *portainer.Artifact, error) {
+func GitSourceAndArtifactForStack(tx gitSourceStore, workflowID portainer.WorkflowID, stackID portainer.StackID) (*portainer.Source, *portainer.ArtifactFile, error) {
if workflowID == 0 {
return nil, nil, nil
}
@@ -28,19 +29,24 @@ func GitSourceAndArtifactForStack(tx gitSourceStore, workflowID portainer.Workfl
return nil, nil, err
}
+ sourceMap, err := loadWorkflowSources(tx, wf)
+ if err != nil {
+ return nil, nil, err
+ }
+
for i, as := range wf.Artifacts {
- if as.Artifact.StackID != stackID {
+ if as.StackID != stackID {
continue
}
- for _, srcID := range as.SourceIDs {
- src, err := tx.Source().Read(srcID)
- if err != nil {
- return nil, nil, err
+ for j, file := range as.Files {
+ src, ok := sourceMap[file.SourceID]
+ if !ok {
+ continue
}
if src.Type == portainer.SourceTypeGit {
- return src, &wf.Artifacts[i].Artifact, nil
+ return &src, &wf.Artifacts[i].Files[j], nil
}
}
}
@@ -48,9 +54,9 @@ func GitSourceAndArtifactForStack(tx gitSourceStore, workflowID portainer.Workfl
return nil, nil, nil
}
-// GitSourceAndArtifactForEdgeStack returns the git Source and the Artifact matching edgeStackID.
+// GitSourceAndArtifactForEdgeStack returns the git Source and the ArtifactFile matching edgeStackID.
// Returns nil, nil, nil when workflowID is 0 or no matching entry is found.
-func GitSourceAndArtifactForEdgeStack(tx gitSourceStore, workflowID portainer.WorkflowID, edgeStackID portainer.EdgeStackID) (*portainer.Source, *portainer.Artifact, error) {
+func GitSourceAndArtifactForEdgeStack(tx gitSourceStore, workflowID portainer.WorkflowID, edgeStackID portainer.EdgeStackID) (*portainer.Source, *portainer.ArtifactFile, error) {
if workflowID == 0 {
return nil, nil, nil
}
@@ -60,19 +66,24 @@ func GitSourceAndArtifactForEdgeStack(tx gitSourceStore, workflowID portainer.Wo
return nil, nil, err
}
+ sourceMap, err := loadWorkflowSources(tx, wf)
+ if err != nil {
+ return nil, nil, err
+ }
+
for i, as := range wf.Artifacts {
- if as.Artifact.EdgeStackID != edgeStackID {
+ if as.EdgeStackID != edgeStackID {
continue
}
- for _, srcID := range as.SourceIDs {
- src, err := tx.Source().Read(srcID)
- if err != nil {
- return nil, nil, err
+ for j, file := range as.Files {
+ src, ok := sourceMap[file.SourceID]
+ if !ok {
+ continue
}
if src.Type == portainer.SourceTypeGit {
- return src, &wf.Artifacts[i].Artifact, nil
+ return &src, &wf.Artifacts[i].Files[j], nil
}
}
}
@@ -80,60 +91,74 @@ func GitSourceAndArtifactForEdgeStack(tx gitSourceStore, workflowID portainer.Wo
return nil, nil, nil
}
-// MergeSourceAndArtifact builds a RepoConfig by combining shared fields from src (URL, auth, TLS)
-// with stack-specific fields from artifact (ref, path, hash).
-func MergeSourceAndArtifact(src *portainer.Source, artifact *portainer.Artifact) *gittypes.RepoConfig {
- if src == nil || src.GitConfig == nil {
+// MergeSourceAndFile builds a RepoConfig by combining shared fields from src (URL, auth, TLS)
+// with file-specific fields from file (ref, path, hash).
+func MergeSourceAndFile(src *portainer.Source, file *portainer.ArtifactFile) *gittypes.RepoConfig {
+ if src == nil || src.Git == nil {
return nil
}
cfg := &gittypes.RepoConfig{
- URL: src.GitConfig.URL,
- Authentication: src.GitConfig.Authentication,
- TLSSkipVerify: src.GitConfig.TLSSkipVerify,
+ URL: src.Git.URL,
+ Authentication: src.Git.Authentication,
+ TLSSkipVerify: src.Git.TLSSkipVerify,
}
- if artifact != nil {
- cfg.ReferenceName = artifact.ReferenceName
- cfg.ConfigFilePath = artifact.ConfigFilePath
- cfg.ConfigHash = artifact.ConfigHash
+ if file != nil {
+ cfg.ReferenceName = file.Ref
+ cfg.ConfigFilePath = file.Path
+ cfg.ConfigHash = file.Hash
}
return cfg
}
-// UpdateArtifactForStack finds the Artifact matching stackID in the workflow and applies fn to it,
-// then persists the updated Workflow. A no-op if no matching Artifact is found.
-func UpdateArtifactForStack(tx gitSourceStore, workflowID portainer.WorkflowID, stackID portainer.StackID, fn func(*portainer.Artifact)) error {
+// UpdateArtifactFileForStack finds the ArtifactFile matching stackID and sourceID in the workflow
+// and applies fn to it, then persists the updated Workflow.
+// A no-op if no matching artifact or file is found.
+func UpdateArtifactFileForStack(tx gitSourceStore, workflowID portainer.WorkflowID, stackID portainer.StackID, sourceID portainer.SourceID, fn func(*portainer.ArtifactFile)) error {
wf, err := tx.Workflow().Read(workflowID)
if err != nil {
return err
}
for i, as := range wf.Artifacts {
- if as.Artifact.StackID == stackID {
- fn(&wf.Artifacts[i].Artifact)
+ if as.StackID != stackID {
+ continue
+ }
- return tx.Workflow().Update(workflowID, wf)
+ for j, file := range as.Files {
+ if file.SourceID == sourceID {
+ fn(&wf.Artifacts[i].Files[j])
+
+ return tx.Workflow().Update(workflowID, wf)
+ }
}
}
return nil
}
-// UpdateArtifactForEdgeStack finds the Artifact matching edgeStackID in the workflow and applies fn to it,
-// then persists the updated Workflow. A no-op if no matching Artifact is found.
-func UpdateArtifactForEdgeStack(tx gitSourceStore, workflowID portainer.WorkflowID, edgeStackID portainer.EdgeStackID, fn func(*portainer.Artifact)) error {
+// UpdateArtifactFileForEdgeStack finds the ArtifactFile matching edgeStackID and sourceID in the workflow
+// and applies fn to it, then persists the updated Workflow.
+// A no-op if no matching artifact or file is found.
+func UpdateArtifactFileForEdgeStack(tx gitSourceStore, workflowID portainer.WorkflowID, edgeStackID portainer.EdgeStackID, sourceID portainer.SourceID, fn func(*portainer.ArtifactFile)) error {
wf, err := tx.Workflow().Read(workflowID)
if err != nil {
return err
}
for i, as := range wf.Artifacts {
- if as.Artifact.EdgeStackID == edgeStackID {
- fn(&wf.Artifacts[i].Artifact)
+ if as.EdgeStackID != edgeStackID {
+ continue
+ }
- return tx.Workflow().Update(workflowID, wf)
+ for j, file := range as.Files {
+ if file.SourceID == sourceID {
+ fn(&wf.Artifacts[i].Files[j])
+
+ return tx.Workflow().Update(workflowID, wf)
+ }
}
}
@@ -144,13 +169,13 @@ func UpdateArtifactForEdgeStack(tx gitSourceStore, workflowID portainer.Workflow
// or creates a new one. Only URL, authentication, and TLSSkipVerify are stored on the Source;
// per-stack fields (ReferenceName, ConfigFilePath, ConfigHash) belong in the Artifact.
func FindOrCreateGitSource(tx gitSourceStore, src *portainer.Source) (*portainer.Source, error) {
- src.GitConfig.URL = gittypes.SanitizeURL(src.GitConfig.URL)
+ src.Git.URL = gittypes.SanitizeURL(src.Git.URL)
existing, err := tx.Source().ReadAll(func(s portainer.Source) bool {
return s.Type == portainer.SourceTypeGit &&
- s.GitConfig != nil &&
- s.GitConfig.URL == src.GitConfig.URL &&
- gitAuthMatches(s.GitConfig.Authentication, src.GitConfig.Authentication)
+ s.Git != nil &&
+ s.Git.URL == src.Git.URL &&
+ gitAuthMatches(s.Git.Authentication, src.Git.Authentication)
})
if err != nil {
return nil, err
@@ -163,10 +188,10 @@ func FindOrCreateGitSource(tx gitSourceStore, src *portainer.Source) (*portainer
toCreate := &portainer.Source{
Name: src.Name,
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
- URL: src.GitConfig.URL,
- Authentication: src.GitConfig.Authentication,
- TLSSkipVerify: src.GitConfig.TLSSkipVerify,
+ Git: &gittypes.RepoConfig{
+ URL: src.Git.URL,
+ Authentication: src.Git.Authentication,
+ TLSSkipVerify: src.Git.TLSSkipVerify,
},
}
@@ -186,17 +211,17 @@ func SaveWorkflowGitConfig(tx gitSourceStore, workflowID portainer.WorkflowID, m
return fmt.Errorf("failed to read source: %w", err)
}
- if src.GitConfig == nil {
+ if src.Git == nil {
return fmt.Errorf("source %d has no git configuration", oldSourceID)
}
newSourceID := oldSourceID
- if cfg.URL != src.GitConfig.URL {
+ if cfg.URL != src.Git.URL {
newSrc, err := FindOrCreateGitSource(tx, &portainer.Source{
- Name: gittypes.RepoName(cfg.URL),
- Type: portainer.SourceTypeGit,
- GitConfig: cfg,
+ Name: gittypes.RepoName(cfg.URL),
+ Type: portainer.SourceTypeGit,
+ Git: cfg,
})
if err != nil {
return fmt.Errorf("failed to find or create source: %w", err)
@@ -204,8 +229,8 @@ func SaveWorkflowGitConfig(tx gitSourceStore, workflowID portainer.WorkflowID, m
newSourceID = newSrc.ID
} else {
- src.GitConfig.Authentication = cfg.Authentication
- src.GitConfig.TLSSkipVerify = cfg.TLSSkipVerify
+ src.Git.Authentication = cfg.Authentication
+ src.Git.TLSSkipVerify = cfg.TLSSkipVerify
if err := tx.Source().Update(src.ID, src); err != nil {
return fmt.Errorf("failed to update source: %w", err)
@@ -218,20 +243,24 @@ func SaveWorkflowGitConfig(tx gitSourceStore, workflowID portainer.WorkflowID, m
}
for i, as := range wf.Artifacts {
- if !matchArtifact(as.Artifact) {
+ if !matchArtifact(as) {
continue
}
- wf.Artifacts[i].Artifact.ReferenceName = cfg.ReferenceName
- wf.Artifacts[i].Artifact.ConfigFilePath = cfg.ConfigFilePath
- wf.Artifacts[i].Artifact.ConfigHash = cfg.ConfigHash
-
- if newSourceID != oldSourceID {
- for j, sID := range as.SourceIDs {
- if sID == oldSourceID {
- wf.Artifacts[i].SourceIDs[j] = newSourceID
- }
+ for j, file := range as.Files {
+ if file.SourceID != oldSourceID {
+ continue
}
+
+ wf.Artifacts[i].Files[j].Ref = cfg.ReferenceName
+ wf.Artifacts[i].Files[j].Path = cfg.ConfigFilePath
+ wf.Artifacts[i].Files[j].Hash = cfg.ConfigHash
+
+ if newSourceID != oldSourceID {
+ wf.Artifacts[i].Files[j].SourceID = newSourceID
+ }
+
+ break
}
break
@@ -240,6 +269,73 @@ func SaveWorkflowGitConfig(tx gitSourceStore, workflowID portainer.WorkflowID, m
return tx.Workflow().Update(workflowID, wf)
}
+// LoadWorkflowMap fetches workflows by their IDs and returns them keyed by ID.
+func LoadWorkflowMap(tx gitSourceStore, ids set.Set[portainer.WorkflowID]) (map[portainer.WorkflowID]portainer.Workflow, error) {
+ result := make(map[portainer.WorkflowID]portainer.Workflow, len(ids))
+ for id := range ids {
+ wf, err := tx.Workflow().Read(id)
+ if err != nil {
+ return nil, err
+ }
+ result[id] = *wf
+ }
+
+ return result, nil
+}
+
+// LoadWorkflowAndSourceMaps fetches workflows by their IDs and the sources they reference,
+// collecting source IDs in a single pass over the workflows.
+func LoadWorkflowAndSourceMaps(tx gitSourceStore, ids set.Set[portainer.WorkflowID]) (map[portainer.WorkflowID]portainer.Workflow, map[portainer.SourceID]portainer.Source, error) {
+ wfMap := make(map[portainer.WorkflowID]portainer.Workflow, len(ids))
+ sourceIDs := make(set.Set[portainer.SourceID])
+ for id := range ids {
+ wf, err := tx.Workflow().Read(id)
+ if err != nil {
+ return nil, nil, err
+ }
+ wfMap[id] = *wf
+ for _, as := range wf.Artifacts {
+ for _, f := range as.Files {
+ sourceIDs.Add(f.SourceID)
+ }
+ }
+ }
+
+ srcMap, err := LoadSourceMap(tx, sourceIDs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return wfMap, srcMap, nil
+}
+
+// loadWorkflowSources collects all unique SourceIDs referenced by wf and returns them as a map.
+// This avoids reading the same Source record more than once when files share a SourceID.
+func loadWorkflowSources(tx gitSourceStore, wf *portainer.Workflow) (map[portainer.SourceID]portainer.Source, error) {
+ ids := make(set.Set[portainer.SourceID])
+ for _, as := range wf.Artifacts {
+ for _, f := range as.Files {
+ ids.Add(f.SourceID)
+ }
+ }
+
+ return LoadSourceMap(tx, ids)
+}
+
+// LoadSourceMap fetches sources by their IDs and returns them keyed by ID.
+func LoadSourceMap(tx gitSourceStore, ids set.Set[portainer.SourceID]) (map[portainer.SourceID]portainer.Source, error) {
+ result := make(map[portainer.SourceID]portainer.Source, len(ids))
+ for id := range ids {
+ src, err := tx.Source().Read(id)
+ if err != nil {
+ return nil, err
+ }
+ result[id] = *src
+ }
+
+ return result, nil
+}
+
func gitAuthMatches(a, b *gittypes.GitAuthentication) bool {
if a == nil && b == nil {
return true
@@ -260,11 +356,11 @@ func ValidateUniqueSourceURL(tx gitSourceStore, url string, sourceID portainer.S
}
existing, err := tx.Source().ReadAll(func(s portainer.Source) bool {
- if s.ID == sourceID || s.Type != portainer.SourceTypeGit || s.GitConfig == nil {
+ if s.ID == sourceID || s.Type != portainer.SourceTypeGit || s.Git == nil {
return false
}
- normalized, err := gittypes.NormalizeURL(gittypes.SanitizeURL(s.GitConfig.URL))
+ normalized, err := gittypes.NormalizeURL(gittypes.SanitizeURL(s.Git.URL))
return err == nil && normalized == normalizedURL
})
diff --git a/api/gitops/workflows/source_artifact_test.go b/api/gitops/workflows/source_artifact_test.go
index c7173bee9..58b96936d 100644
--- a/api/gitops/workflows/source_artifact_test.go
+++ b/api/gitops/workflows/source_artifact_test.go
@@ -11,24 +11,24 @@ import (
"github.com/stretchr/testify/require"
)
-func TestMergeSourceAndArtifact_NilSourceReturnsNil(t *testing.T) {
+func TestMergeSourceAndFile_NilSourceReturnsNil(t *testing.T) {
t.Parallel()
- require.Nil(t, MergeSourceAndArtifact(nil, nil))
+ require.Nil(t, MergeSourceAndFile(nil, nil))
}
-func TestMergeSourceAndArtifact_NilGitConfigReturnsNil(t *testing.T) {
+func TestMergeSourceAndFile_NilGitConfigReturnsNil(t *testing.T) {
t.Parallel()
src := &portainer.Source{Type: portainer.SourceTypeGit}
- require.Nil(t, MergeSourceAndArtifact(src, nil))
+ require.Nil(t, MergeSourceAndFile(src, nil))
}
-func TestMergeSourceAndArtifact_NilArtifactLeavesPerStackFieldsEmpty(t *testing.T) {
+func TestMergeSourceAndFile_NilFileLeaveFileFieldsEmpty(t *testing.T) {
t.Parallel()
src := &portainer.Source{
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
TLSSkipVerify: true,
Authentication: &gittypes.GitAuthentication{
@@ -38,7 +38,7 @@ func TestMergeSourceAndArtifact_NilArtifactLeavesPerStackFieldsEmpty(t *testing.
},
}
- cfg := MergeSourceAndArtifact(src, nil)
+ cfg := MergeSourceAndFile(src, nil)
require.NotNil(t, cfg)
require.Equal(t, "https://github.com/example/repo", cfg.URL)
require.True(t, cfg.TLSSkipVerify)
@@ -48,22 +48,22 @@ func TestMergeSourceAndArtifact_NilArtifactLeavesPerStackFieldsEmpty(t *testing.
require.Empty(t, cfg.ConfigHash)
}
-func TestMergeSourceAndArtifact_MergesAllFieldsFromArtifact(t *testing.T) {
+func TestMergeSourceAndFile_MergesAllFieldsFromFile(t *testing.T) {
t.Parallel()
src := &portainer.Source{
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
TLSSkipVerify: true,
},
}
- artifact := &portainer.Artifact{
- ReferenceName: "refs/heads/main",
- ConfigFilePath: "docker-compose.yml",
- ConfigHash: "abc123",
+ file := &portainer.ArtifactFile{
+ Path: "docker-compose.yml",
+ Ref: "refs/heads/main",
+ Hash: "abc123",
}
- cfg := MergeSourceAndArtifact(src, artifact)
+ cfg := MergeSourceAndFile(src, file)
require.NotNil(t, cfg)
require.Equal(t, "https://github.com/example/repo", cfg.URL)
require.True(t, cfg.TLSSkipVerify)
@@ -77,39 +77,39 @@ func TestGitSourceAndArtifactForStack_ZeroWorkflowIDReturnsNil(t *testing.T) {
_, store := datastore.MustNewTestStore(t, false, true)
var src *portainer.Source
- var artifact *portainer.Artifact
+ var file *portainer.ArtifactFile
err := store.ViewTx(func(tx dataservices.DataStoreTx) error {
var txErr error
- src, artifact, txErr = GitSourceAndArtifactForStack(tx, 0, 1)
+ src, file, txErr = GitSourceAndArtifactForStack(tx, 0, 1)
return txErr
})
require.NoError(t, err)
require.Nil(t, src)
- require.Nil(t, artifact)
+ require.Nil(t, file)
}
-func TestGitSourceAndArtifactForStack_ReturnsMatchingSourceAndArtifact(t *testing.T) {
+func TestGitSourceAndArtifactForStack_ReturnsMatchingSourceAndFile(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var workflowID portainer.WorkflowID
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
gitSrc := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
}
err := tx.Source().Create(gitSrc)
require.NoError(t, err)
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- StackID: 42,
- ReferenceName: "refs/heads/main",
- ConfigFilePath: "docker-compose.yml",
- ConfigHash: "abc123",
- },
- SourceIDs: []portainer.SourceID{gitSrc.ID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 42,
+ Files: []portainer.ArtifactFile{{
+ SourceID: gitSrc.ID,
+ Path: "docker-compose.yml",
+ Ref: "refs/heads/main",
+ Hash: "abc123",
+ }},
}},
}
err = tx.Workflow().Create(wf)
@@ -121,20 +121,19 @@ func TestGitSourceAndArtifactForStack_ReturnsMatchingSourceAndArtifact(t *testin
require.NoError(t, err)
var src *portainer.Source
- var artifact *portainer.Artifact
+ var file *portainer.ArtifactFile
err = store.ViewTx(func(tx dataservices.DataStoreTx) error {
var txErr error
- src, artifact, txErr = GitSourceAndArtifactForStack(tx, workflowID, 42)
+ src, file, txErr = GitSourceAndArtifactForStack(tx, workflowID, 42)
return txErr
})
require.NoError(t, err)
require.NotNil(t, src)
require.Equal(t, portainer.SourceTypeGit, src.Type)
- require.NotNil(t, artifact)
- require.Equal(t, portainer.StackID(42), artifact.StackID)
- require.Equal(t, "refs/heads/main", artifact.ReferenceName)
- require.Equal(t, "docker-compose.yml", artifact.ConfigFilePath)
- require.Equal(t, "abc123", artifact.ConfigHash)
+ require.NotNil(t, file)
+ require.Equal(t, "refs/heads/main", file.Ref)
+ require.Equal(t, "docker-compose.yml", file.Path)
+ require.Equal(t, "abc123", file.Hash)
}
func TestGitSourceAndArtifactForStack_NoMatchingArtifactReturnsNil(t *testing.T) {
@@ -144,16 +143,16 @@ func TestGitSourceAndArtifactForStack_NoMatchingArtifactReturnsNil(t *testing.T)
var workflowID portainer.WorkflowID
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
}
err := tx.Source().Create(src)
require.NoError(t, err)
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1},
- SourceIDs: []portainer.SourceID{src.ID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: src.ID}},
}},
}
err = tx.Workflow().Create(wf)
@@ -165,15 +164,15 @@ func TestGitSourceAndArtifactForStack_NoMatchingArtifactReturnsNil(t *testing.T)
require.NoError(t, err)
var src *portainer.Source
- var artifact *portainer.Artifact
+ var file *portainer.ArtifactFile
err = store.ViewTx(func(tx dataservices.DataStoreTx) error {
var txErr error
- src, artifact, txErr = GitSourceAndArtifactForStack(tx, workflowID, 99)
+ src, file, txErr = GitSourceAndArtifactForStack(tx, workflowID, 99)
return txErr
})
require.NoError(t, err)
require.Nil(t, src)
- require.Nil(t, artifact)
+ require.Nil(t, file)
}
func TestGitSourceAndArtifactForStack_NonGitSourceSkipped(t *testing.T) {
@@ -187,9 +186,9 @@ func TestGitSourceAndArtifactForStack_NonGitSourceSkipped(t *testing.T) {
require.NoError(t, err)
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1},
- SourceIDs: []portainer.SourceID{nonGitSrc.ID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: nonGitSrc.ID}},
}},
}
err = tx.Workflow().Create(wf)
@@ -201,15 +200,15 @@ func TestGitSourceAndArtifactForStack_NonGitSourceSkipped(t *testing.T) {
require.NoError(t, err)
var src *portainer.Source
- var artifact *portainer.Artifact
+ var file *portainer.ArtifactFile
err = store.ViewTx(func(tx dataservices.DataStoreTx) error {
var txErr error
- src, artifact, txErr = GitSourceAndArtifactForStack(tx, workflowID, 1)
+ src, file, txErr = GitSourceAndArtifactForStack(tx, workflowID, 1)
return txErr
})
require.NoError(t, err)
require.Nil(t, src)
- require.Nil(t, artifact)
+ require.Nil(t, file)
}
func TestGitSourceAndArtifactForEdgeStack_ZeroWorkflowIDReturnsNil(t *testing.T) {
@@ -217,38 +216,38 @@ func TestGitSourceAndArtifactForEdgeStack_ZeroWorkflowIDReturnsNil(t *testing.T)
_, store := datastore.MustNewTestStore(t, false, true)
var src *portainer.Source
- var artifact *portainer.Artifact
+ var file *portainer.ArtifactFile
err := store.ViewTx(func(tx dataservices.DataStoreTx) error {
var txErr error
- src, artifact, txErr = GitSourceAndArtifactForEdgeStack(tx, 0, 1)
+ src, file, txErr = GitSourceAndArtifactForEdgeStack(tx, 0, 1)
return txErr
})
require.NoError(t, err)
require.Nil(t, src)
- require.Nil(t, artifact)
+ require.Nil(t, file)
}
-func TestGitSourceAndArtifactForEdgeStack_ReturnsMatchingSourceAndArtifact(t *testing.T) {
+func TestGitSourceAndArtifactForEdgeStack_ReturnsMatchingSourceAndFile(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var workflowID portainer.WorkflowID
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
gitSrc := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/edge-repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/edge-repo"},
}
err := tx.Source().Create(gitSrc)
require.NoError(t, err)
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- EdgeStackID: 5,
- ReferenceName: "refs/heads/edge",
- ConfigFilePath: "edge.yml",
- },
- SourceIDs: []portainer.SourceID{gitSrc.ID},
+ Artifacts: []portainer.Artifact{{
+ EdgeStackID: 5,
+ Files: []portainer.ArtifactFile{{
+ SourceID: gitSrc.ID,
+ Path: "edge.yml",
+ Ref: "refs/heads/edge",
+ }},
}},
}
err = tx.Workflow().Create(wf)
@@ -260,32 +259,38 @@ func TestGitSourceAndArtifactForEdgeStack_ReturnsMatchingSourceAndArtifact(t *te
require.NoError(t, err)
var src *portainer.Source
- var artifact *portainer.Artifact
+ var file *portainer.ArtifactFile
err = store.ViewTx(func(tx dataservices.DataStoreTx) error {
var txErr error
- src, artifact, txErr = GitSourceAndArtifactForEdgeStack(tx, workflowID, 5)
+ src, file, txErr = GitSourceAndArtifactForEdgeStack(tx, workflowID, 5)
return txErr
})
require.NoError(t, err)
require.NotNil(t, src)
require.Equal(t, portainer.SourceTypeGit, src.Type)
- require.NotNil(t, artifact)
- require.Equal(t, portainer.EdgeStackID(5), artifact.EdgeStackID)
- require.Equal(t, "refs/heads/edge", artifact.ReferenceName)
+ require.NotNil(t, file)
+ require.Equal(t, "refs/heads/edge", file.Ref)
}
-func TestUpdateArtifactForStack_NoMatchingArtifactIsNoOp(t *testing.T) {
+func TestUpdateArtifactFileForStack_NoMatchingArtifactIsNoOp(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var workflowID portainer.WorkflowID
+ var sourceID portainer.SourceID
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{Type: portainer.SourceTypeGit, Git: &gittypes.RepoConfig{URL: "https://example.com"}}
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ sourceID = src.ID
+
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1, ConfigHash: "original"},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: sourceID, Hash: "original"}},
}},
}
- err := tx.Workflow().Create(wf)
+ err = tx.Workflow().Create(wf)
require.NoError(t, err)
workflowID = wf.ID
@@ -294,29 +299,36 @@ func TestUpdateArtifactForStack_NoMatchingArtifactIsNoOp(t *testing.T) {
require.NoError(t, err)
err = store.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return UpdateArtifactForStack(tx, workflowID, 99, func(a *portainer.Artifact) {
- a.ConfigHash = "changed"
+ return UpdateArtifactFileForStack(tx, workflowID, 99, sourceID, func(a *portainer.ArtifactFile) {
+ a.Hash = "changed"
})
})
require.NoError(t, err)
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- require.Equal(t, "original", wf.Artifacts[0].Artifact.ConfigHash)
+ require.Equal(t, "original", wf.Artifacts[0].Files[0].Hash)
}
-func TestUpdateArtifactForStack_AppliesFnAndPersists(t *testing.T) {
+func TestUpdateArtifactFileForStack_AppliesFnAndPersists(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var workflowID portainer.WorkflowID
+ var sourceID portainer.SourceID
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{Type: portainer.SourceTypeGit, Git: &gittypes.RepoConfig{URL: "https://example.com"}}
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ sourceID = src.ID
+
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1, ConfigHash: "old-hash"},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: sourceID, Hash: "old-hash"}},
}},
}
- err := tx.Workflow().Create(wf)
+ err = tx.Workflow().Create(wf)
require.NoError(t, err)
workflowID = wf.ID
@@ -325,29 +337,36 @@ func TestUpdateArtifactForStack_AppliesFnAndPersists(t *testing.T) {
require.NoError(t, err)
err = store.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return UpdateArtifactForStack(tx, workflowID, 1, func(a *portainer.Artifact) {
- a.ConfigHash = "new-hash"
+ return UpdateArtifactFileForStack(tx, workflowID, 1, sourceID, func(a *portainer.ArtifactFile) {
+ a.Hash = "new-hash"
})
})
require.NoError(t, err)
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- require.Equal(t, "new-hash", wf.Artifacts[0].Artifact.ConfigHash)
+ require.Equal(t, "new-hash", wf.Artifacts[0].Files[0].Hash)
}
-func TestUpdateArtifactForEdgeStack_AppliesFnAndPersists(t *testing.T) {
+func TestUpdateArtifactFileForEdgeStack_AppliesFnAndPersists(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var workflowID portainer.WorkflowID
+ var sourceID portainer.SourceID
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{Type: portainer.SourceTypeGit, Git: &gittypes.RepoConfig{URL: "https://example.com"}}
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ sourceID = src.ID
+
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{EdgeStackID: 7, ConfigHash: "old-hash"},
+ Artifacts: []portainer.Artifact{{
+ EdgeStackID: 7,
+ Files: []portainer.ArtifactFile{{SourceID: sourceID, Hash: "old-hash"}},
}},
}
- err := tx.Workflow().Create(wf)
+ err = tx.Workflow().Create(wf)
require.NoError(t, err)
workflowID = wf.ID
@@ -356,15 +375,15 @@ func TestUpdateArtifactForEdgeStack_AppliesFnAndPersists(t *testing.T) {
require.NoError(t, err)
err = store.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return UpdateArtifactForEdgeStack(tx, workflowID, 7, func(a *portainer.Artifact) {
- a.ConfigHash = "new-hash"
+ return UpdateArtifactFileForEdgeStack(tx, workflowID, 7, sourceID, func(a *portainer.ArtifactFile) {
+ a.Hash = "new-hash"
})
})
require.NoError(t, err)
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- require.Equal(t, "new-hash", wf.Artifacts[0].Artifact.ConfigHash)
+ require.Equal(t, "new-hash", wf.Artifacts[0].Files[0].Hash)
}
func TestFindOrCreateGitSource_CreatesNewSource(t *testing.T) {
@@ -377,7 +396,7 @@ func TestFindOrCreateGitSource_CreatesNewSource(t *testing.T) {
src, txErr = FindOrCreateGitSource(tx, &portainer.Source{
Name: "my-repo",
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
},
})
@@ -386,7 +405,7 @@ func TestFindOrCreateGitSource_CreatesNewSource(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, src)
require.NotZero(t, src.ID)
- require.Equal(t, "https://github.com/example/repo", src.GitConfig.URL)
+ require.Equal(t, "https://github.com/example/repo", src.Git.URL)
}
func TestFindOrCreateGitSource_ReusesExistingSourceForSameURLAndAuth(t *testing.T) {
@@ -396,7 +415,7 @@ func TestFindOrCreateGitSource_ReusesExistingSourceForSameURLAndAuth(t *testing.
makeSource := func(tx dataservices.DataStoreTx) (*portainer.Source, error) {
return FindOrCreateGitSource(tx, &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
},
})
@@ -439,7 +458,7 @@ func TestFindOrCreateGitSource_DifferentAuthCreatesNewSource(t *testing.T) {
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
_, txErr := FindOrCreateGitSource(tx, &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
Authentication: &gittypes.GitAuthentication{Username: "alice", Password: "pass1"},
},
@@ -451,7 +470,7 @@ func TestFindOrCreateGitSource_DifferentAuthCreatesNewSource(t *testing.T) {
err = store.UpdateTx(func(tx dataservices.DataStoreTx) error {
_, txErr := FindOrCreateGitSource(tx, &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
Authentication: &gittypes.GitAuthentication{Username: "bob", Password: "pass2"},
},
@@ -465,7 +484,7 @@ func TestFindOrCreateGitSource_DifferentAuthCreatesNewSource(t *testing.T) {
require.Len(t, sources, 2)
}
-func TestSaveWorkflowGitConfig_UpdatesArtifactAndSourceWhenURLUnchanged(t *testing.T) {
+func TestSaveWorkflowGitConfig_UpdatesFileAndSourceWhenURLUnchanged(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
@@ -475,7 +494,7 @@ func TestSaveWorkflowGitConfig_UpdatesArtifactAndSourceWhenURLUnchanged(t *testi
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/example/repo",
TLSSkipVerify: false,
Authentication: &gittypes.GitAuthentication{
@@ -489,14 +508,14 @@ func TestSaveWorkflowGitConfig_UpdatesArtifactAndSourceWhenURLUnchanged(t *testi
sourceID = src.ID
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- StackID: 1,
- ReferenceName: "refs/heads/main",
- ConfigFilePath: "docker-compose.yml",
- ConfigHash: "old-hash",
- },
- SourceIDs: []portainer.SourceID{sourceID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{
+ SourceID: sourceID,
+ Path: "docker-compose.yml",
+ Ref: "refs/heads/main",
+ Hash: "old-hash",
+ }},
}},
}
err = tx.Workflow().Create(wf)
@@ -528,16 +547,16 @@ func TestSaveWorkflowGitConfig_UpdatesArtifactAndSourceWhenURLUnchanged(t *testi
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- require.Equal(t, "refs/heads/dev", wf.Artifacts[0].Artifact.ReferenceName)
- require.Equal(t, "compose.yml", wf.Artifacts[0].Artifact.ConfigFilePath)
- require.Equal(t, "new-hash", wf.Artifacts[0].Artifact.ConfigHash)
- require.Equal(t, sourceID, wf.Artifacts[0].SourceIDs[0])
+ require.Equal(t, "refs/heads/dev", wf.Artifacts[0].Files[0].Ref)
+ require.Equal(t, "compose.yml", wf.Artifacts[0].Files[0].Path)
+ require.Equal(t, "new-hash", wf.Artifacts[0].Files[0].Hash)
+ require.Equal(t, sourceID, wf.Artifacts[0].Files[0].SourceID)
src, err := store.Source().Read(sourceID)
require.NoError(t, err)
- require.Equal(t, "new-user", src.GitConfig.Authentication.Username)
- require.Equal(t, "new-pass", src.GitConfig.Authentication.Password)
- require.True(t, src.GitConfig.TLSSkipVerify)
+ require.Equal(t, "new-user", src.Git.Authentication.Username)
+ require.Equal(t, "new-pass", src.Git.Authentication.Password)
+ require.True(t, src.Git.TLSSkipVerify)
}
func TestSaveWorkflowGitConfig_CreatesNewSourceOnURLChange(t *testing.T) {
@@ -549,17 +568,17 @@ func TestSaveWorkflowGitConfig_CreatesNewSourceOnURLChange(t *testing.T) {
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/old-repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/old-repo"},
}
err := tx.Source().Create(src)
require.NoError(t, err)
oldSourceID = src.ID
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1},
- SourceIDs: []portainer.SourceID{oldSourceID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: oldSourceID}},
}},
}
err = tx.Workflow().Create(wf)
@@ -581,12 +600,12 @@ func TestSaveWorkflowGitConfig_CreatesNewSourceOnURLChange(t *testing.T) {
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- newSourceID := wf.Artifacts[0].SourceIDs[0]
+ newSourceID := wf.Artifacts[0].Files[0].SourceID
require.NotEqual(t, oldSourceID, newSourceID)
newSrc, err := store.Source().Read(newSourceID)
require.NoError(t, err)
- require.Equal(t, "https://github.com/example/new-repo", newSrc.GitConfig.URL)
+ require.Equal(t, "https://github.com/example/new-repo", newSrc.Git.URL)
}
func TestSaveWorkflowGitConfig_ReusesExistingSourceOnURLChange(t *testing.T) {
@@ -598,25 +617,25 @@ func TestSaveWorkflowGitConfig_ReusesExistingSourceOnURLChange(t *testing.T) {
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
old := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/old-repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/old-repo"},
}
err := tx.Source().Create(old)
require.NoError(t, err)
oldSourceID = old.ID
existing := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/shared-repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/shared-repo"},
}
err = tx.Source().Create(existing)
require.NoError(t, err)
existingSourceID = existing.ID
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1},
- SourceIDs: []portainer.SourceID{oldSourceID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: oldSourceID}},
}},
}
err = tx.Workflow().Create(wf)
@@ -638,7 +657,7 @@ func TestSaveWorkflowGitConfig_ReusesExistingSourceOnURLChange(t *testing.T) {
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- require.Equal(t, existingSourceID, wf.Artifacts[0].SourceIDs[0])
+ require.Equal(t, existingSourceID, wf.Artifacts[0].Files[0].SourceID)
sources, err := store.Source().ReadAll()
require.NoError(t, err)
@@ -659,9 +678,9 @@ func TestSaveWorkflowGitConfig_NilGitConfigReturnsError(t *testing.T) {
sourceID = src.ID
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1},
- SourceIDs: []portainer.SourceID{sourceID},
+ Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: sourceID}},
}},
}
err = tx.Workflow().Create(wf)
@@ -689,22 +708,22 @@ func TestSaveWorkflowGitConfig_OnlyMatchingArtifactUpdated(t *testing.T) {
err := store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
}
err := tx.Source().Create(src)
require.NoError(t, err)
sourceID = src.ID
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{
+ Artifacts: []portainer.Artifact{
{
- Artifact: portainer.Artifact{StackID: 1, ConfigHash: "hash-1"},
- SourceIDs: []portainer.SourceID{sourceID},
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: sourceID, Hash: "hash-1"}},
},
{
- Artifact: portainer.Artifact{StackID: 2, ConfigHash: "hash-2"},
- SourceIDs: []portainer.SourceID{sourceID},
+ StackID: 2,
+ Files: []portainer.ArtifactFile{{SourceID: sourceID, Hash: "hash-2"}},
},
},
}
@@ -728,8 +747,8 @@ func TestSaveWorkflowGitConfig_OnlyMatchingArtifactUpdated(t *testing.T) {
wf, err := store.Workflow().Read(workflowID)
require.NoError(t, err)
- require.Equal(t, "updated-hash", wf.Artifacts[0].Artifact.ConfigHash)
- require.Equal(t, "hash-2", wf.Artifacts[1].Artifact.ConfigHash)
+ require.Equal(t, "updated-hash", wf.Artifacts[0].Files[0].Hash)
+ require.Equal(t, "hash-2", wf.Artifacts[1].Files[0].Hash)
}
func TestFindOrCreateGitSource_StripsEmbeddedCredentialsFromURL(t *testing.T) {
@@ -741,12 +760,12 @@ func TestFindOrCreateGitSource_StripsEmbeddedCredentialsFromURL(t *testing.T) {
var txErr error
src, txErr = FindOrCreateGitSource(tx, &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://user:secret@github.com/example/repo",
},
})
return txErr
})
require.NoError(t, err)
- require.Equal(t, "https://github.com/example/repo", src.GitConfig.URL)
+ require.Equal(t, "https://github.com/example/repo", src.Git.URL)
}
diff --git a/api/http/client/client.go b/api/http/client/client.go
index 194131c57..68c4ecd6f 100644
--- a/api/http/client/client.go
+++ b/api/http/client/client.go
@@ -11,6 +11,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/rs/zerolog/log"
"github.com/segmentio/encoding/json"
@@ -114,18 +115,19 @@ func Get(url string, timeout int) ([]byte, error) {
// using the specified host and optional TLS configuration.
// It uses a new Http.Client for each operation.
func ExecutePingOperation(host string, tlsConfiguration portainer.TLSConfiguration) (bool, error) {
- transport := &http.Transport{}
-
scheme := "http"
+ var transport *http.Transport
if tlsConfiguration.TLS {
tlsConfig, err := crypto.CreateTLSConfigurationFromDisk(tlsConfiguration)
if err != nil {
return false, err
}
- transport.TLSClientConfig = tlsConfig
scheme = "https"
+ transport = ssrf.WrapTransport(&http.Transport{TLSClientConfig: tlsConfig})
+ } else {
+ transport = ssrf.WrapTransport(&http.Transport{})
}
client := &http.Client{
diff --git a/api/http/handler/backup/handler.go b/api/http/handler/backup/handler.go
index 8a99c5ffb..de0f35af3 100644
--- a/api/http/handler/backup/handler.go
+++ b/api/http/handler/backup/handler.go
@@ -22,6 +22,7 @@ type Handler struct {
filestorePath string
shutdownTrigger context.CancelFunc
adminMonitor *adminmonitor.Monitor
+ SetupToken string
}
// NewHandler creates an new instance of backup handler
diff --git a/api/http/handler/backup/restore.go b/api/http/handler/backup/restore.go
index 4938413ee..f8e5b7f4f 100644
--- a/api/http/handler/backup/restore.go
+++ b/api/http/handler/backup/restore.go
@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
operations "github.com/portainer/portainer/api/backup"
+ "github.com/portainer/portainer/api/http/security/setuptoken"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
)
@@ -20,15 +21,21 @@ type restorePayload struct {
// @id Restore
// @summary Triggers a system restore using provided backup file
// @description Triggers a system restore using provided backup file
-// @description **Access policy**: public
+// @description **Access policy**: public (requires the X-Setup-Token header on an uninitialized instance unless --no-setup-token is set)
// @tags backup
// @accept json
+// @param X-Setup-Token header string false "Setup token (required when instance is uninitialized and --no-setup-token is not set)"
// @param restorePayload body restorePayload true "Restore request payload"
// @success 200 "Success"
// @failure 400 "Invalid request"
+// @failure 403 "Access denied - invalid or missing setup token"
// @failure 500 "Server error"
// @router /restore [post]
func (h *Handler) restore(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
+ if err := setuptoken.Validate(r, h.SetupToken); err != nil {
+ return err
+ }
+
initialized, err := h.adminMonitor.WasInitialized()
if err != nil {
return httperror.InternalServerError("Failed to check system initialization", err)
diff --git a/api/http/handler/backup/restore_test.go b/api/http/handler/backup/restore_test.go
index 7e5bf1d7c..1953a0cc5 100644
--- a/api/http/handler/backup/restore_test.go
+++ b/api/http/handler/backup/restore_test.go
@@ -14,6 +14,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/adminmonitor"
"github.com/portainer/portainer/api/http/offlinegate"
+ "github.com/portainer/portainer/api/http/security/setuptoken"
"github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
@@ -126,6 +127,45 @@ func backup(t *testing.T, h *Handler, password string) []byte {
return archive
}
+func Test_restore_setupTokenGate(t *testing.T) {
+ t.Parallel()
+ datastore := testhelpers.NewDatastore(
+ testhelpers.WithUsers([]portainer.User{}),
+ testhelpers.WithEdgeJobs([]portainer.EdgeJob{}),
+ )
+ adminMonitor := adminmonitor.New(time.Hour, datastore)
+ h := NewHandler(
+ testhelpers.NewTestRequestBouncer(),
+ datastore,
+ offlinegate.NewOfflineGate(),
+ prepareFilestorePath(t),
+ func() {},
+ adminMonitor,
+ )
+ h.SetupToken = "secret-token"
+
+ t.Run("403 without token header", func(t *testing.T) {
+ err := h.restore(httptest.NewRecorder(), prepareMultipartRequest(t, "", []byte("x")))
+ require.Error(t, err)
+ assert.Equal(t, http.StatusForbidden, err.StatusCode)
+ })
+
+ t.Run("403 with wrong token", func(t *testing.T) {
+ r := prepareMultipartRequest(t, "", []byte("x"))
+ r.Header.Set(setuptoken.HeaderName, "wrong")
+ err := h.restore(httptest.NewRecorder(), r)
+ require.Error(t, err)
+ assert.Equal(t, http.StatusForbidden, err.StatusCode)
+ })
+
+ t.Run("passes gate with correct token", func(t *testing.T) {
+ archive := backup(t, h, "")
+ r := prepareMultipartRequest(t, "", archive)
+ r.Header.Set(setuptoken.HeaderName, "secret-token")
+ require.Nil(t, h.restore(httptest.NewRecorder(), r))
+ })
+}
+
func prepareMultipartRequest(t *testing.T, password string, file []byte) *http.Request {
var body bytes.Buffer
diff --git a/api/http/handler/customtemplates/customtemplate_create.go b/api/http/handler/customtemplates/customtemplate_create.go
index 88ad28b93..826dfe0b0 100644
--- a/api/http/handler/customtemplates/customtemplate_create.go
+++ b/api/http/handler/customtemplates/customtemplate_create.go
@@ -5,12 +5,13 @@ import (
"errors"
"net/http"
"os"
- "regexp"
"strconv"
portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
gittypes "github.com/portainer/portainer/api/git/types"
+ "github.com/portainer/portainer/api/gitops/workflows"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
"github.com/portainer/portainer/api/stacks/stackutils"
@@ -41,30 +42,39 @@ func (handler *Handler) customTemplateCreate(w http.ResponseWriter, r *http.Requ
customTemplate.CreatedByUserID = tokenData.ID
- customTemplates, err := handler.DataStore.CustomTemplate().ReadAll()
+ err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return createCustomTemplateTx(tx, customTemplate, tokenData.ID)
+ })
+
+ return response.TxResponse(w, customTemplate, err)
+}
+
+func createCustomTemplateTx(tx dataservices.DataStoreTx, customTemplate *portainer.CustomTemplate, userID portainer.UserID) error {
+ existingTemplates, err := tx.CustomTemplate().ReadAll()
if err != nil {
return httperror.InternalServerError("Unable to retrieve custom templates from the database", err)
}
- for _, existingTemplate := range customTemplates {
- if existingTemplate.Title == customTemplate.Title {
+ for _, existing := range existingTemplates {
+ if existing.Title == customTemplate.Title {
return httperror.InternalServerError("Template name must be unique", errors.New("Template name must be unique"))
}
}
- if err := handler.DataStore.CustomTemplate().Create(customTemplate); err != nil {
+ if err := tx.CustomTemplate().Create(customTemplate); err != nil {
return httperror.InternalServerError("Unable to create custom template", err)
}
- resourceControl := authorization.NewPrivateResourceControl(strconv.Itoa(int(customTemplate.ID)), portainer.CustomTemplateResourceControl, tokenData.ID)
+ resourceControl := authorization.NewPrivateResourceControl(strconv.Itoa(int(customTemplate.ID)), portainer.CustomTemplateResourceControl, userID)
- if err := handler.DataStore.ResourceControl().Create(resourceControl); err != nil {
+ if err := tx.ResourceControl().Create(resourceControl); err != nil {
return httperror.InternalServerError("Unable to persist resource control inside the database", err)
}
customTemplate.ResourceControl = resourceControl
+ populateGitConfig(tx, customTemplate)
- return response.JSON(w, customTemplate)
+ return nil
}
func (handler *Handler) createCustomTemplate(method string, r *http.Request) (*portainer.CustomTemplate, error) {
@@ -122,19 +132,11 @@ func (payload *customTemplateFromFileContentPayload) Validate(r *http.Request) e
if payload.Type != portainer.KubernetesStack && payload.Type != portainer.DockerSwarmStack && payload.Type != portainer.DockerComposeStack {
return errors.New("Invalid custom template type")
}
- if !isValidNote(payload.Note) {
+ if !IsValidNote(payload.Note) {
return errors.New("Invalid note.
tag is not supported")
}
- return validateVariablesDefinitions(payload.Variables)
-}
-
-func isValidNote(note string) bool {
- if len(note) == 0 {
- return true
- }
- match, _ := regexp.MatchString("
tag is not supported")
}
- return validateVariablesDefinitions(payload.Variables)
+ return ValidateVariablesDefinitions(payload.Variables)
}
// @id CustomTemplateCreateRepository
@@ -312,9 +314,27 @@ func (handler *Handler) createCustomTemplateFromGitRepository(r *http.Request) (
return nil, err
}
- gitConfig.ConfigHash = commitHash
- customTemplate.GitConfig = gitConfig
+ src, err := workflows.FindOrCreateGitSource(handler.DataStore, &portainer.Source{
+ Name: gittypes.RepoName(gitConfig.URL),
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{
+ URL: gitConfig.URL,
+ Authentication: gitConfig.Authentication,
+ TLSSkipVerify: gitConfig.TLSSkipVerify,
+ },
+ })
+ if err != nil {
+ return nil, err
+ }
+ customTemplate.Artifact = &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{
+ SourceID: src.ID,
+ Path: gitConfig.ConfigFilePath,
+ Ref: gitConfig.ReferenceName,
+ Hash: commitHash,
+ }},
+ }
isValidProject := true
defer func() {
if !isValidProject {
@@ -390,7 +410,7 @@ func (payload *customTemplateFromFileUploadPayload) Validate(r *http.Request) er
payload.Logo = logo
note, _ := request.RetrieveMultiPartFormValue(r, "Note", true)
- if !isValidNote(note) {
+ if !IsValidNote(note) {
return errors.New("Invalid note.
tag is not supported")
}
payload.Note = note
@@ -422,7 +442,7 @@ func (payload *customTemplateFromFileUploadPayload) Validate(r *http.Request) er
if err := json.Unmarshal([]byte(varsString), &payload.Variables); err != nil {
return errors.New("Invalid variables. Ensure that the variables are valid JSON")
}
- if err := validateVariablesDefinitions(payload.Variables); err != nil {
+ if err := ValidateVariablesDefinitions(payload.Variables); err != nil {
return err
}
}
diff --git a/api/http/handler/customtemplates/customtemplate_create_test.go b/api/http/handler/customtemplates/customtemplate_create_test.go
new file mode 100644
index 000000000..30d909213
--- /dev/null
+++ b/api/http/handler/customtemplates/customtemplate_create_test.go
@@ -0,0 +1,1037 @@
+package customtemplates
+
+import (
+ "bytes"
+ "context"
+ "mime/multipart"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/dataservices"
+ "github.com/portainer/portainer/api/filesystem"
+ "github.com/portainer/portainer/api/http/security"
+
+ "github.com/gorilla/mux"
+ "github.com/segmentio/encoding/json"
+ "github.com/stretchr/testify/require"
+)
+
+func createTemplateRequest(t *testing.T, method string, payload any, userID portainer.UserID, role portainer.UserRole) *http.Request {
+ t.Helper()
+
+ body, err := json.Marshal(payload)
+ require.NoError(t, err)
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/"+method, bytes.NewReader(body))
+ r.Header.Set("Content-Type", "application/json")
+ r = mux.SetURLVars(r, map[string]string{"method": method})
+
+ return r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: userID, Role: role}))
+}
+
+func TestCustomTemplateCreate_FromFileContent_Success(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ FileContent: "version: '3'\nservices:\n web:\n image: nginx",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "My Template", tmpl.Title)
+ require.Equal(t, "A test template", tmpl.Description)
+ require.Equal(t, portainer.UserID(1), tmpl.CreatedByUserID)
+ require.NotNil(t, tmpl.ResourceControl)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ templates, err := tx.CustomTemplate().ReadAll()
+ require.NoError(t, err)
+ require.Len(t, templates, 1)
+ require.Equal(t, "My Template", templates[0].Title)
+
+ rcs, err := tx.ResourceControl().ReadAll()
+ require.NoError(t, err)
+ require.Len(t, rcs, 1)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromFileContent_DuplicateTitle(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "Existing Template",
+ })
+ }))
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "Existing Template",
+ Description: "Another template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_MissingTitle(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_MissingDescription(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_MissingFileContent(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_InvalidPlatform(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: 0,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_NoteWithImage(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Note: `Some note with
`,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_KubernetesTypeIgnoresPlatform(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "K8s Template",
+ Description: "A kubernetes template",
+ FileContent: "apiVersion: v1\nkind: Pod",
+ Type: portainer.KubernetesStack,
+ Platform: 0,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "K8s Template", tmpl.Title)
+ require.Equal(t, portainer.KubernetesStack, tmpl.Type)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ templates, err := tx.CustomTemplate().ReadAll()
+ require.NoError(t, err)
+ require.Len(t, templates, 1)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_Success(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "Uploaded Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "Uploaded from file")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'\nservices:\n web:\n image: nginx"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "Uploaded Template", tmpl.Title)
+ require.Equal(t, filesystem.ComposeFileDefaultName, tmpl.EntryPoint)
+
+ err = ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ templates, err := tx.CustomTemplate().ReadAll()
+ require.NoError(t, err)
+ require.Len(t, templates, 1)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_InvalidMethod(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "invalid", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_InvalidType(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Type: 0,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_Variables(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "Template With Variables",
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ Variables: []portainer.CustomTemplateVariableDefinition{
+ {Name: "IMAGE", Label: "Docker image"},
+ },
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Len(t, tmpl.Variables, 1)
+ require.Equal(t, "IMAGE", tmpl.Variables[0].Name)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(tmpl.ID)
+ require.NoError(t, err)
+ require.Len(t, stored.Variables, 1)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromFileContent_InvalidVariables(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "My Template",
+ Description: "A test template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ Variables: []portainer.CustomTemplateVariableDefinition{
+ {Label: "Missing name"},
+ },
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileContent_EdgeTemplate(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ payload := customTemplateFromFileContentPayload{
+ Title: "Edge Template",
+ Description: "For edge stacks",
+ FileContent: "version: '3'\nservices:\n web:\n image: nginx",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ EdgeTemplate: true,
+ }
+
+ r := createTemplateRequest(t, "string", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.True(t, tmpl.EdgeTemplate)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(tmpl.ID)
+ require.NoError(t, err)
+ require.True(t, stored.EdgeTemplate)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_MissingTitle(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_MissingDescription(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "My Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_MissingFile(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "My Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_InvalidType(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "My Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "0")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_InvalidPlatform(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "My Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "0")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_NoteWithImage(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "My Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Note", `Some note
`)
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_KubernetesIgnoresPlatform(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "K8s Upload Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A kubernetes template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "3")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "0")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "deployment.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("apiVersion: v1\nkind: Pod"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "K8s Upload Template", tmpl.Title)
+ require.Equal(t, filesystem.ComposeFileDefaultName, tmpl.EntryPoint)
+
+ err = ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ templates, err := tx.CustomTemplate().ReadAll()
+ require.NoError(t, err)
+ require.Len(t, templates, 1)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_Variables(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ vars, err := json.Marshal([]portainer.CustomTemplateVariableDefinition{{Name: "IMAGE", Label: "Docker image"}})
+ require.NoError(t, err)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err = writer.WriteField("Title", "Template With Variables")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Variables", string(vars))
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Len(t, tmpl.Variables, 1)
+ require.Equal(t, "IMAGE", tmpl.Variables[0].Name)
+
+ err = ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(tmpl.ID)
+ require.NoError(t, err)
+ require.Len(t, stored.Variables, 1)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromFileUpload_InvalidVariables(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+
+ err := writer.WriteField("Title", "My Template")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Description", "A description")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Type", "2")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Platform", "1")
+ require.NoError(t, err)
+
+ err = writer.WriteField("Variables", "not-valid-json")
+ require.NoError(t, err)
+
+ part, err := writer.CreateFormFile("File", "docker-compose.yml")
+ require.NoError(t, err)
+
+ _, err = part.Write([]byte("version: '3'"))
+ require.NoError(t, err)
+
+ require.NoError(t, writer.Close())
+
+ r := httptest.NewRequest(http.MethodPost, "/custom_templates/create/file", &body)
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+ r = mux.SetURLVars(r, map[string]string{"method": "file"})
+ r = r.WithContext(security.StoreTokenData(r, &portainer.TokenData{ID: 1, Role: portainer.AdministratorRole}))
+
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+type gitServiceCreatingFile struct {
+ portainer.GitService
+}
+
+func (g *gitServiceCreatingFile) CloneRepository(_ context.Context, destination, _, _, _, _ string, _ bool) error {
+ if err := os.MkdirAll(destination, 0700); err != nil {
+ return err
+ }
+
+ f, err := os.Create(filesystem.JoinPaths(destination, filesystem.ComposeFileDefaultName))
+ if err != nil {
+ return err
+ }
+
+ return f.Close()
+}
+
+func (g *gitServiceCreatingFile) LatestCommitID(_ context.Context, _, _, _, _ string, _ bool) (string, error) {
+ return "deadbeef123", nil
+}
+
+func TestCustomTemplateCreate_FromRepository_Success(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+ handler.GitService = &gitServiceCreatingFile{}
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Title: "Git Template",
+ Description: "Created from git",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "Git Template", tmpl.Title)
+ require.NotNil(t, tmpl.Artifact)
+ require.Len(t, tmpl.Artifact.Files, 1)
+ require.Equal(t, "deadbeef123", tmpl.Artifact.Files[0].Hash)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(tmpl.ID)
+ require.NoError(t, err)
+ require.NotNil(t, stored.Artifact)
+
+ src, err := tx.Source().Read(stored.Artifact.Files[0].SourceID)
+ require.NoError(t, err)
+ require.Equal(t, portainer.SourceTypeGit, src.Type)
+ require.Equal(t, "https://github.com/example/repo", src.Git.URL)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromRepository_DeduplicatesSource(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+ handler.GitService = &gitServiceCreatingFile{}
+
+ makePayload := func(title string) customTemplateFromGitRepositoryPayload {
+ return customTemplateFromGitRepositoryPayload{
+ Title: title,
+ Description: "Created from git",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+ }
+
+ r1 := createTemplateRequest(t, "repository", makePayload("Template One"), 1, portainer.AdministratorRole)
+ rr1 := httptest.NewRecorder()
+ herr := handler.customTemplateCreate(rr1, r1)
+ require.Nil(t, herr)
+
+ r2 := createTemplateRequest(t, "repository", makePayload("Template Two"), 1, portainer.AdministratorRole)
+ rr2 := httptest.NewRecorder()
+ herr = handler.customTemplateCreate(rr2, r2)
+ require.Nil(t, herr)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ sources, err := tx.Source().ReadAll()
+ require.NoError(t, err)
+ require.Len(t, sources, 1, "two templates with the same URL must share one Source")
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateCreate_FromRepository_Validation_MissingTitle(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Description: "A description",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromRepository_Validation_MissingDescription(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Title: "My Template",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromRepository_Validation_InvalidURL(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Title: "My Template",
+ Description: "A description",
+ RepositoryURL: "http://",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromRepository_Validation_AuthWithoutCredentials(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Title: "My Template",
+ Description: "A description",
+ RepositoryURL: "https://github.com/example/repo",
+ RepositoryAuthentication: true,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromRepository_Validation_InvalidPlatform(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Title: "My Template",
+ Description: "A description",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: portainer.DockerComposeStack,
+ Platform: 0,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateCreate_FromRepository_Validation_InvalidType(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateFromGitRepositoryPayload{
+ Title: "My Template",
+ Description: "A description",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: 0,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := createTemplateRequest(t, "repository", payload, 1, portainer.AdministratorRole)
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateCreate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
diff --git a/api/http/handler/customtemplates/customtemplate_file.go b/api/http/handler/customtemplates/customtemplate_file.go
index 286da475d..463a953a5 100644
--- a/api/http/handler/customtemplates/customtemplate_file.go
+++ b/api/http/handler/customtemplates/customtemplate_file.go
@@ -82,8 +82,8 @@ func (handler *Handler) customTemplateFile(w http.ResponseWriter, r *http.Reques
}
entryPath := customTemplate.EntryPoint
- if customTemplate.GitConfig != nil {
- entryPath = customTemplate.GitConfig.ConfigFilePath
+ if customTemplate.Artifact != nil && len(customTemplate.Artifact.Files) > 0 {
+ entryPath = customTemplate.Artifact.Files[0].Path
}
fileContent, err := handler.FileService.GetFileContent(customTemplate.ProjectPath, entryPath)
if err != nil {
diff --git a/api/http/handler/customtemplates/customtemplate_file_test.go b/api/http/handler/customtemplates/customtemplate_file_test.go
index cc1f7fa50..d2d818dcd 100644
--- a/api/http/handler/customtemplates/customtemplate_file_test.go
+++ b/api/http/handler/customtemplates/customtemplate_file_test.go
@@ -5,11 +5,13 @@ import (
"net/http/httptest"
"testing"
- "github.com/gorilla/mux"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
+ gittypes "github.com/portainer/portainer/api/git/types"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
+
+ "github.com/gorilla/mux"
"github.com/segmentio/encoding/json"
"github.com/stretchr/testify/require"
)
@@ -33,7 +35,8 @@ func TestCustomTemplateFile(t *testing.T) {
require.NoError(t, err)
require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{ID: 2, EntryPoint: templateEntrypoint, ProjectPath: path}))
- require.NoError(t, tx.ResourceControl().Create(&portainer.ResourceControl{ID: 1, ResourceID: "2", Type: portainer.CustomTemplateResourceControl,
+ require.NoError(t, tx.ResourceControl().Create(&portainer.ResourceControl{
+ ID: 1, ResourceID: "2", Type: portainer.CustomTemplateResourceControl,
UserAccesses: []portainer.UserResourceAccess{{UserID: 2}},
TeamAccesses: []portainer.TeamResourceAccess{{TeamID: 1}},
}))
@@ -59,6 +62,7 @@ func TestCustomTemplateFile(t *testing.T) {
rr, r := test("1", &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
require.Nil(t, r)
require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
var res struct{ FileContent string }
require.NoError(t, json.NewDecoder(rr.Body).Decode(&res))
require.Equal(t, templateContent, res.FileContent)
@@ -74,6 +78,7 @@ func TestCustomTemplateFile(t *testing.T) {
rr, r := test("2", &security.RestrictedRequestContext{UserID: 2})
require.Nil(t, r)
require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
var res struct{ FileContent string }
require.NoError(t, json.NewDecoder(rr.Body).Decode(&res))
require.Equal(t, templateContent, res.FileContent)
@@ -83,6 +88,7 @@ func TestCustomTemplateFile(t *testing.T) {
rr, r := test("2", &security.RestrictedRequestContext{UserID: 3, UserMemberships: []portainer.TeamMembership{{ID: 1, UserID: 3, TeamID: 1}}})
require.Nil(t, r)
require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
var res struct{ FileContent string }
require.NoError(t, json.NewDecoder(rr.Body).Decode(&res))
require.Equal(t, templateContent, res.FileContent)
@@ -94,3 +100,46 @@ func TestCustomTemplateFile(t *testing.T) {
require.Equal(t, http.StatusForbidden, r.StatusCode)
})
}
+
+func TestCustomTemplateFile_GitTemplate(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, fs := newTestHandler(t)
+
+ templateContent := "git template content"
+ configFilePath := "docker-compose.yml"
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/example/repo"},
+ }
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+
+ path, err := fs.StoreCustomTemplateFileFromBytes("10", configFilePath, []byte(templateContent))
+ require.NoError(t, err)
+
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 10,
+ EntryPoint: "should-not-be-used.yml",
+ ProjectPath: path,
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{Path: configFilePath, SourceID: src.ID}},
+ },
+ })
+ }))
+
+ r := httptest.NewRequest(http.MethodGet, "/custom_templates/10/file", nil)
+ r = mux.SetURLVars(r, map[string]string{"id": "10"})
+ ctx := security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ r = r.WithContext(ctx)
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateFile(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
+ var res struct{ FileContent string }
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&res))
+ require.Equal(t, templateContent, res.FileContent)
+}
diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch.go b/api/http/handler/customtemplates/customtemplate_git_fetch.go
index ec7f25376..6230ab812 100644
--- a/api/http/handler/customtemplates/customtemplate_git_fetch.go
+++ b/api/http/handler/customtemplates/customtemplate_git_fetch.go
@@ -7,6 +7,7 @@ import (
"sync"
portainer "github.com/portainer/portainer/api"
+ gittypes "github.com/portainer/portainer/api/git/types"
"github.com/portainer/portainer/api/stacks/stackutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
@@ -42,8 +43,28 @@ func (handler *Handler) customTemplateGitFetch(w http.ResponseWriter, r *http.Re
return httperror.InternalServerError("Unable to find a custom template with the specified identifier inside the database", err)
}
- if customTemplate.GitConfig == nil {
- return httperror.BadRequest("Git configuration does not exist in this custom template", err)
+ if customTemplate.Artifact == nil || len(customTemplate.Artifact.Files) == 0 {
+ return httperror.BadRequest("Git configuration does not exist in this custom template", nil)
+ }
+
+ file := customTemplate.Artifact.Files[0]
+
+ src, err := handler.DataStore.Source().Read(file.SourceID)
+ if err != nil {
+ return httperror.InternalServerError("Unable to retrieve git source for custom template", err)
+ }
+
+ if src.Git == nil {
+ return httperror.InternalServerError("Source has no git configuration", nil)
+ }
+
+ gitConfig := &gittypes.RepoConfig{
+ URL: src.Git.URL,
+ Authentication: src.Git.Authentication,
+ TLSSkipVerify: src.Git.TLSSkipVerify,
+ ReferenceName: file.Ref,
+ ConfigFilePath: file.Path,
+ ConfigHash: file.Hash,
}
// If multiple users are trying to fetch the same custom template simultaneously, a lock needs to be added
@@ -68,7 +89,7 @@ func (handler *Handler) customTemplateGitFetch(w http.ResponseWriter, r *http.Re
}
}()
- commitHash, err := stackutils.DownloadGitRepository(context.TODO(), *customTemplate.GitConfig, handler.GitService, func() string {
+ commitHash, err := stackutils.DownloadGitRepository(context.TODO(), *gitConfig, handler.GitService, func() string {
return customTemplate.ProjectPath
})
if err != nil {
@@ -81,15 +102,15 @@ func (handler *Handler) customTemplateGitFetch(w http.ResponseWriter, r *http.Re
return httperror.InternalServerError("Failed to download git repository", err)
}
- if customTemplate.GitConfig.ConfigHash != commitHash {
- customTemplate.GitConfig.ConfigHash = commitHash
+ if customTemplate.Artifact.Files[0].Hash != commitHash {
+ customTemplate.Artifact.Files[0].Hash = commitHash
if err := handler.DataStore.CustomTemplate().Update(customTemplate.ID, customTemplate); err != nil {
return httperror.InternalServerError("Unable to persist custom template changes inside the database", err)
}
}
- fileContent, err := handler.FileService.GetFileContent(customTemplate.ProjectPath, customTemplate.GitConfig.ConfigFilePath)
+ fileContent, err := handler.FileService.GetFileContent(customTemplate.ProjectPath, gitConfig.ConfigFilePath)
if err != nil {
return httperror.InternalServerError("Unable to retrieve custom template file from disk", err)
}
diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go
index 7f72cca8a..9a94aca66 100644
--- a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go
+++ b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go
@@ -156,6 +156,7 @@ func singleAPIRequest(h *Handler, jwt string, expect string) error {
func Test_customTemplateGitFetch(t *testing.T) {
t.Parallel()
+
is := assert.New(t)
_, store := datastore.MustNewTestStore(t, true, true)
@@ -172,12 +173,31 @@ func Test_customTemplateGitFetch(t *testing.T) {
dir, err := os.Getwd()
require.NoError(t, err, "error to get working directory")
- template1 := &portainer.CustomTemplate{ID: 1, Title: "custom-template-1", ProjectPath: filesystem.JoinPaths(dir, "fixtures/custom_template_1"), GitConfig: &gittypes.RepoConfig{ConfigFilePath: "test-config-path.txt"}}
+ src := &portainer.Source{
+ ID: 1,
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ },
+ }
+ err = store.Source().Create(src)
+ require.NoError(t, err, "error creating source")
+
+ const configFilePath = "test-config-path.txt"
+
+ template1 := &portainer.CustomTemplate{
+ ID: 1,
+ Title: "custom-template-1",
+ ProjectPath: filesystem.JoinPaths(dir, "fixtures/custom_template_1"),
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{Path: configFilePath, SourceID: src.ID}},
+ },
+ }
err = store.CustomTemplateService.Create(template1)
require.NoError(t, err, "error creating custom template 1")
// prepare testing folder
- err = prepareTestFolder(template1.ProjectPath, template1.GitConfig.ConfigFilePath)
+ err = prepareTestFolder(template1.ProjectPath, configFilePath)
require.NoError(t, err, "error creating testing folder")
defer func() {
@@ -192,7 +212,7 @@ func Test_customTemplateGitFetch(t *testing.T) {
requestBouncer := security.NewRequestBouncer(t.Context(), store, jwtService, nil)
gitService := &TestGitService{
- targetFilePath: filesystem.JoinPaths(template1.ProjectPath, template1.GitConfig.ConfigFilePath),
+ targetFilePath: filesystem.JoinPaths(template1.ProjectPath, configFilePath),
}
fileService := &TestFileService{}
@@ -252,7 +272,7 @@ func Test_customTemplateGitFetch(t *testing.T) {
t.Run("restore git repository if it is failed to download the new git repository", func(t *testing.T) {
invalidGitService := &InvalidTestGitService{
- targetFilePath: filesystem.JoinPaths(template1.ProjectPath, template1.GitConfig.ConfigFilePath),
+ targetFilePath: filesystem.JoinPaths(template1.ProjectPath, configFilePath),
}
h := NewHandler(requestBouncer, store, fileService, invalidGitService)
@@ -274,3 +294,73 @@ func Test_customTemplateGitFetch(t *testing.T) {
assert.Equal(t, "gfedcba", string(fileContent))
})
}
+
+func TestCustomTemplateGitFetch_NilArtifactReturnsBadRequest(t *testing.T) {
+ t.Parallel()
+
+ _, store := datastore.MustNewTestStore(t, false, true)
+
+ template := &portainer.CustomTemplate{ID: 1, Title: "no-git-template"}
+ err := store.CustomTemplateService.Create(template)
+ require.NoError(t, err)
+
+ h := NewHandler(testhelpers.NewTestRequestBouncer(), store, &TestFileService{}, &TestGitService{})
+
+ req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBufferString("{}"))
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, req)
+
+ require.Equal(t, http.StatusBadRequest, rr.Code)
+}
+
+func TestCustomTemplateGitFetch_EmptySourceIDsReturnsBadRequest(t *testing.T) {
+ t.Parallel()
+
+ _, store := datastore.MustNewTestStore(t, false, true)
+
+ template := &portainer.CustomTemplate{
+ ID: 1,
+ Title: "empty-source-ids",
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{},
+ },
+ }
+ err := store.CustomTemplateService.Create(template)
+ require.NoError(t, err)
+
+ h := NewHandler(testhelpers.NewTestRequestBouncer(), store, &TestFileService{}, &TestGitService{})
+
+ req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBufferString("{}"))
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, req)
+
+ require.Equal(t, http.StatusBadRequest, rr.Code)
+}
+
+func TestCustomTemplateGitFetch_SourceWithNilGitConfigReturnsInternalError(t *testing.T) {
+ t.Parallel()
+
+ _, store := datastore.MustNewTestStore(t, false, true)
+
+ src := &portainer.Source{Type: portainer.SourceTypeGit}
+ err := store.Source().Create(src)
+ require.NoError(t, err)
+
+ template := &portainer.CustomTemplate{
+ ID: 1,
+ Title: "nil-git-config",
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{SourceID: src.ID}},
+ },
+ }
+ err = store.CustomTemplateService.Create(template)
+ require.NoError(t, err)
+
+ h := NewHandler(testhelpers.NewTestRequestBouncer(), store, &TestFileService{}, &TestGitService{})
+
+ req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBufferString("{}"))
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, req)
+
+ require.Equal(t, http.StatusInternalServerError, rr.Code)
+}
diff --git a/api/http/handler/customtemplates/customtemplate_inspect.go b/api/http/handler/customtemplates/customtemplate_inspect.go
index 716bc8395..018a492a0 100644
--- a/api/http/handler/customtemplates/customtemplate_inspect.go
+++ b/api/http/handler/customtemplates/customtemplate_inspect.go
@@ -68,11 +68,13 @@ func (handler *Handler) customTemplateInspect(w http.ResponseWriter, r *http.Req
}
- if canEdit || hasAccess {
- return nil
+ if !canEdit && !hasAccess {
+ return httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied)
}
- return httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied)
+ populateGitConfig(tx, customTemplate)
+
+ return nil
})
return response.TxResponse(w, customTemplate, err)
diff --git a/api/http/handler/customtemplates/customtemplate_inspect_test.go b/api/http/handler/customtemplates/customtemplate_inspect_test.go
index 40ec7b916..ef8a90739 100644
--- a/api/http/handler/customtemplates/customtemplate_inspect_test.go
+++ b/api/http/handler/customtemplates/customtemplate_inspect_test.go
@@ -5,14 +5,16 @@ import (
"net/http/httptest"
"testing"
- "github.com/gorilla/mux"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
"github.com/portainer/portainer/api/filesystem"
+ gittypes "github.com/portainer/portainer/api/git/types"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/testhelpers"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
+
+ "github.com/gorilla/mux"
"github.com/segmentio/encoding/json"
"github.com/stretchr/testify/require"
)
@@ -33,11 +35,13 @@ func newTestHandler(t *testing.T) (*Handler, dataservices.DataStore, portainer.F
require.NoError(t, tx.User().Create(&portainer.User{ID: 2, Username: "std2", Role: portainer.StandardUserRole}))
require.NoError(t, tx.User().Create(&portainer.User{ID: 3, Username: "std3", Role: portainer.StandardUserRole}))
require.NoError(t, tx.User().Create(&portainer.User{ID: 4, Username: "std4", Role: portainer.StandardUserRole}))
- require.NoError(t, tx.Endpoint().Create(&portainer.Endpoint{ID: 1,
+ require.NoError(t, tx.Endpoint().Create(&portainer.Endpoint{
+ ID: 1,
UserAccessPolicies: portainer.UserAccessPolicies{
2: portainer.AccessPolicy{RoleID: 0},
3: portainer.AccessPolicy{RoleID: 0},
- }}))
+ },
+ }))
require.NoError(t, tx.Team().Create(&portainer.Team{ID: 1}))
require.NoError(t, tx.TeamMembership().Create(&portainer.TeamMembership{ID: 1, UserID: 3, TeamID: 1, Role: portainer.TeamMember}))
return nil
@@ -56,7 +60,8 @@ func TestInspectHandler(t *testing.T) {
require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{ID: 1}))
require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{ID: 2}))
- require.NoError(t, tx.ResourceControl().Create(&portainer.ResourceControl{ID: 1, ResourceID: "2", Type: portainer.CustomTemplateResourceControl,
+ require.NoError(t, tx.ResourceControl().Create(&portainer.ResourceControl{
+ ID: 1, ResourceID: "2", Type: portainer.CustomTemplateResourceControl,
UserAccesses: []portainer.UserResourceAccess{{UserID: 2}},
TeamAccesses: []portainer.TeamResourceAccess{{TeamID: 1}},
}))
@@ -82,6 +87,7 @@ func TestInspectHandler(t *testing.T) {
rr, r := test("1", &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
require.Nil(t, r)
require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
var template portainer.CustomTemplate
require.NoError(t, json.NewDecoder(rr.Body).Decode(&template))
require.Equal(t, portainer.CustomTemplateID(1), template.ID)
@@ -97,6 +103,7 @@ func TestInspectHandler(t *testing.T) {
rr, r := test("2", &security.RestrictedRequestContext{UserID: 2})
require.Nil(t, r)
require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
var template portainer.CustomTemplate
require.NoError(t, json.NewDecoder(rr.Body).Decode(&template))
require.Equal(t, portainer.CustomTemplateID(2), template.ID)
@@ -106,6 +113,7 @@ func TestInspectHandler(t *testing.T) {
rr, r := test("2", &security.RestrictedRequestContext{UserID: 3, UserMemberships: []portainer.TeamMembership{{ID: 1, UserID: 3, TeamID: 1}}})
require.Nil(t, r)
require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
var template portainer.CustomTemplate
require.NoError(t, json.NewDecoder(rr.Body).Decode(&template))
require.Equal(t, portainer.CustomTemplateID(2), template.ID)
@@ -117,3 +125,53 @@ func TestInspectHandler(t *testing.T) {
require.Equal(t, http.StatusForbidden, r.StatusCode)
})
}
+
+func TestInspectHandler_GitConfigPopulatedFromSource(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ var srcID portainer.SourceID
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ TLSSkipVerify: true,
+ },
+ }
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ srcID = src.ID
+
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 10,
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{
+ Ref: "refs/heads/main",
+ Path: "docker-compose.yml",
+ Hash: "abc123",
+ SourceID: srcID,
+ }},
+ },
+ })
+ }))
+
+ r := httptest.NewRequest(http.MethodGet, "/custom_templates/10", nil)
+ r = mux.SetURLVars(r, map[string]string{"id": "10"})
+ ctx := security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ r = r.WithContext(ctx)
+ rr := httptest.NewRecorder()
+ herr := handler.customTemplateInspect(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Result().StatusCode)
+
+ var template portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&template))
+ require.NotNil(t, template.GitConfig)
+ require.Equal(t, "https://github.com/example/repo", template.GitConfig.URL)
+ require.True(t, template.GitConfig.TLSSkipVerify)
+ require.Equal(t, "refs/heads/main", template.GitConfig.ReferenceName)
+ require.Equal(t, "docker-compose.yml", template.GitConfig.ConfigFilePath)
+ require.Equal(t, "abc123", template.GitConfig.ConfigHash)
+}
diff --git a/api/http/handler/customtemplates/customtemplate_list.go b/api/http/handler/customtemplates/customtemplate_list.go
index 4c69cbb89..36bcbff9d 100644
--- a/api/http/handler/customtemplates/customtemplate_list.go
+++ b/api/http/handler/customtemplates/customtemplate_list.go
@@ -74,10 +74,7 @@ func (handler *Handler) customTemplateList(w http.ResponseWriter, r *http.Reques
}
for i := range customTemplates {
- customTemplate := &customTemplates[i]
- if customTemplate.GitConfig != nil && customTemplate.GitConfig.Authentication != nil {
- customTemplate.GitConfig.Authentication.Password = ""
- }
+ populateGitConfig(handler.DataStore, &customTemplates[i])
}
return response.JSON(w, customTemplates)
diff --git a/api/http/handler/customtemplates/customtemplate_list_test.go b/api/http/handler/customtemplates/customtemplate_list_test.go
new file mode 100644
index 000000000..6cfaf617c
--- /dev/null
+++ b/api/http/handler/customtemplates/customtemplate_list_test.go
@@ -0,0 +1,127 @@
+package customtemplates
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/dataservices"
+ gittypes "github.com/portainer/portainer/api/git/types"
+ "github.com/portainer/portainer/api/http/security"
+
+ "github.com/segmentio/encoding/json"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCustomTemplateList_PopulatesGitConfigFromSource(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ var srcID portainer.SourceID
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ TLSSkipVerify: true,
+ },
+ }
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ srcID = src.ID
+ require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{
+ Ref: "refs/heads/main",
+ Path: "docker-compose.yml",
+ Hash: "abc123",
+ SourceID: srcID,
+ }},
+ },
+ }))
+ require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{ID: 2, EntryPoint: "docker-compose.yml"}))
+
+ return nil
+ }))
+
+ r := httptest.NewRequest(http.MethodGet, "/custom_templates", nil)
+ r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true}))
+ rr := httptest.NewRecorder()
+ handler.ServeHTTP(rr, r)
+
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var templates []portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&templates))
+
+ var gitTemplate portainer.CustomTemplate
+ for _, tpl := range templates {
+ if tpl.ID == 1 {
+ gitTemplate = tpl
+ }
+ }
+
+ require.NotNil(t, gitTemplate.GitConfig)
+ require.Equal(t, "https://github.com/example/repo", gitTemplate.GitConfig.URL)
+ require.True(t, gitTemplate.GitConfig.TLSSkipVerify)
+ require.Equal(t, "refs/heads/main", gitTemplate.GitConfig.ReferenceName)
+ require.Equal(t, "docker-compose.yml", gitTemplate.GitConfig.ConfigFilePath)
+ require.Equal(t, "abc123", gitTemplate.GitConfig.ConfigHash)
+
+ var plainTemplate portainer.CustomTemplate
+ for _, tpl := range templates {
+ if tpl.ID == 2 {
+ plainTemplate = tpl
+ }
+ }
+ require.Nil(t, plainTemplate.GitConfig)
+}
+
+func TestCustomTemplateList_StripsPasswordFromGitConfig(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ var srcID portainer.SourceID
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{
+ URL: "https://github.com/example/repo",
+ Authentication: &gittypes.GitAuthentication{
+ Username: "user",
+ Password: "topsecret",
+ },
+ },
+ }
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ srcID = src.ID
+ require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{SourceID: srcID}},
+ },
+ }))
+
+ return nil
+ }))
+
+ r := httptest.NewRequest(http.MethodGet, "/custom_templates", nil)
+ r = r.WithContext(security.StoreRestrictedRequestContext(r, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true}))
+ rr := httptest.NewRecorder()
+ handler.ServeHTTP(rr, r)
+
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var templates []portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&templates))
+ require.Len(t, templates, 1)
+ require.NotNil(t, templates[0].GitConfig)
+ require.NotNil(t, templates[0].GitConfig.Authentication)
+ require.Equal(t, "user", templates[0].GitConfig.Authentication.Username)
+ require.Empty(t, templates[0].GitConfig.Authentication.Password)
+}
diff --git a/api/http/handler/customtemplates/customtemplate_update.go b/api/http/handler/customtemplates/customtemplate_update.go
index 794d06a6c..7aeea8e25 100644
--- a/api/http/handler/customtemplates/customtemplate_update.go
+++ b/api/http/handler/customtemplates/customtemplate_update.go
@@ -8,9 +8,11 @@ import (
"strconv"
portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/git"
gittypes "github.com/portainer/portainer/api/git/types"
+ "github.com/portainer/portainer/api/gitops/workflows"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
@@ -84,7 +86,7 @@ func (payload *customTemplateUpdatePayload) Validate(r *http.Request) error {
return errors.New("Invalid custom template description")
}
- if !isValidNote(payload.Note) {
+ if !IsValidNote(payload.Note) {
return errors.New("Invalid note.
tag is not supported")
}
@@ -96,7 +98,7 @@ func (payload *customTemplateUpdatePayload) Validate(r *http.Request) error {
payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName
}
- if err := validateVariablesDefinitions(payload.Variables); err != nil {
+ if err := ValidateVariablesDefinitions(payload.Variables); err != nil {
return err
}
@@ -218,8 +220,28 @@ func (handler *Handler) customTemplateUpdate(w http.ResponseWriter, r *http.Requ
return httperror.InternalServerError("Unable get latest commit id", fmt.Errorf("failed to fetch latest commit id of the template %v: %w", customTemplate.ID, err))
}
- gitConfig.ConfigHash = commitHash
- customTemplate.GitConfig = gitConfig
+ src, err := workflows.FindOrCreateGitSource(handler.DataStore, &portainer.Source{
+ Name: gittypes.RepoName(gitConfig.URL),
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{
+ URL: gitConfig.URL,
+ Authentication: gitConfig.Authentication,
+ TLSSkipVerify: gitConfig.TLSSkipVerify,
+ },
+ })
+ if err != nil {
+ return httperror.InternalServerError("Unable to find or create git source", err)
+ }
+
+ customTemplate.Artifact = &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{
+ SourceID: src.ID,
+ Path: gitConfig.ConfigFilePath,
+ Ref: gitConfig.ReferenceName,
+ Hash: commitHash,
+ }},
+ }
+
} else {
templateFolder := strconv.Itoa(customTemplateID)
projectPath, err := handler.FileService.StoreCustomTemplateFileFromBytes(templateFolder, customTemplate.EntryPoint, []byte(payload.FileContent))
@@ -228,11 +250,18 @@ func (handler *Handler) customTemplateUpdate(w http.ResponseWriter, r *http.Requ
}
customTemplate.ProjectPath = projectPath
+ customTemplate.Artifact = nil
}
- if err := handler.DataStore.CustomTemplate().Update(customTemplate.ID, customTemplate); err != nil {
- return httperror.InternalServerError("Unable to persist custom template changes inside the database", err)
- }
+ err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ if err := tx.CustomTemplate().Update(customTemplate.ID, customTemplate); err != nil {
+ return httperror.InternalServerError("Unable to persist custom template changes inside the database", err)
+ }
- return response.JSON(w, customTemplate)
+ populateGitConfig(tx, customTemplate)
+
+ return nil
+ })
+
+ return response.TxResponse(w, customTemplate, err)
}
diff --git a/api/http/handler/customtemplates/customtemplate_update_test.go b/api/http/handler/customtemplates/customtemplate_update_test.go
new file mode 100644
index 000000000..af3262ca3
--- /dev/null
+++ b/api/http/handler/customtemplates/customtemplate_update_test.go
@@ -0,0 +1,500 @@
+package customtemplates
+
+import (
+ "bytes"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/dataservices"
+ "github.com/portainer/portainer/api/filesystem"
+ "github.com/portainer/portainer/api/http/security"
+
+ "github.com/gorilla/mux"
+ "github.com/segmentio/encoding/json"
+ "github.com/stretchr/testify/require"
+)
+
+func updateTemplateRequest(t *testing.T, templateID string, payload any, ctx *security.RestrictedRequestContext) *http.Request {
+ t.Helper()
+
+ body, err := json.Marshal(payload)
+ require.NoError(t, err)
+
+ r := httptest.NewRequest(http.MethodPut, "/custom_templates/"+templateID, bytes.NewReader(body))
+ r.Header.Set("Content-Type", "application/json")
+ r = mux.SetURLVars(r, map[string]string{"id": templateID})
+
+ return r.WithContext(security.StoreRestrictedRequestContext(r, ctx))
+}
+
+func TestCustomTemplateUpdate_NotFound(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "New Title",
+ Description: "New Description",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusNotFound, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Forbidden(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "Original Title",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ CreatedByUserID: 1,
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "New Title",
+ Description: "New Description",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ // User 2 did not create this template and is not an admin
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 2})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusForbidden, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_DuplicateTitle(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ require.NoError(t, tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "Template One",
+ }))
+
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 2,
+ Title: "Template Two",
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "Template One",
+ Description: "Renamed",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "2", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusInternalServerError, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Success_FileContent(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "Original Title",
+ Description: "Original Description",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ CreatedByUserID: 1,
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "Updated Title",
+ Description: "Updated Description",
+ FileContent: "version: '3'\nservices:\n app:\n image: alpine",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "Updated Title", tmpl.Title)
+ require.Equal(t, "Updated Description", tmpl.Description)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(1)
+ require.NoError(t, err)
+ require.Equal(t, "Updated Title", stored.Title)
+ require.Equal(t, "Updated Description", stored.Description)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateUpdate_OwnerCanUpdate(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "User Template",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ CreatedByUserID: 2,
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "User Template Updated",
+ Description: "Updated by owner",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ // User 2 is the creator, not an admin
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 2})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Equal(t, "User Template Updated", tmpl.Title)
+}
+
+func TestCustomTemplateUpdate_SameTitleAllowed(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "My Template",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ Description: "Updated description",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(1)
+ require.NoError(t, err)
+ require.Equal(t, "My Template", stored.Title)
+ require.Equal(t, "Updated description", stored.Description)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateUpdate_InvalidPayload(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "My Template",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ // Title is empty - invalid
+ Description: "A description",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Validation_MissingDescription(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Validation_BothContentAndRepoMissing(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ Description: "A description",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Validation_InvalidPlatform(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ Description: "A description",
+ FileContent: "version: '3'",
+ Type: portainer.DockerComposeStack,
+ Platform: 0,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Validation_InvalidType(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ Description: "A description",
+ FileContent: "version: '3'",
+ Type: 0,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Validation_NoteWithImage(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ Description: "A description",
+ FileContent: "version: '3'",
+ Note: `Some note
`,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_Validation_AuthWithoutCredentials(t *testing.T) {
+ t.Parallel()
+
+ handler, _, _ := newTestHandler(t)
+
+ payload := customTemplateUpdatePayload{
+ Title: "My Template",
+ Description: "A description",
+ RepositoryURL: "https://github.com/example/repo",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ RepositoryAuthentication: true,
+ }
+
+ r := updateTemplateRequest(t, "99", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.NotNil(t, herr)
+ require.Equal(t, http.StatusBadRequest, herr.StatusCode)
+}
+
+func TestCustomTemplateUpdate_ClearsArtifact(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "Git Template",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ CreatedByUserID: 1,
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{},
+ },
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "Git Template",
+ Description: "Updated with file content",
+ FileContent: "version: '3'\nservices:\n app:\n image: alpine",
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.Nil(t, tmpl.Artifact)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(1)
+ require.NoError(t, err)
+ require.Nil(t, stored.Artifact)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
+
+func TestCustomTemplateUpdate_GitRepository_Success(t *testing.T) {
+ t.Parallel()
+
+ handler, ds, _ := newTestHandler(t)
+ handler.GitService = &gitServiceCreatingFile{}
+
+ projectDir := t.TempDir()
+
+ require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ return tx.CustomTemplate().Create(&portainer.CustomTemplate{
+ ID: 1,
+ Title: "Git Template",
+ EntryPoint: filesystem.ComposeFileDefaultName,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ CreatedByUserID: 1,
+ ProjectPath: projectDir,
+ })
+ }))
+
+ payload := customTemplateUpdatePayload{
+ Title: "Git Template",
+ Description: "Updated via git",
+ RepositoryURL: "https://github.com/example/repo",
+ RepositoryReferenceName: "refs/heads/main",
+ ComposeFilePathInRepository: filesystem.ComposeFileDefaultName,
+ Type: portainer.DockerComposeStack,
+ Platform: portainer.CustomTemplatePlatformLinux,
+ }
+
+ r := updateTemplateRequest(t, "1", payload, &security.RestrictedRequestContext{UserID: 1, IsAdmin: true})
+ rr := httptest.NewRecorder()
+
+ herr := handler.customTemplateUpdate(rr, r)
+ require.Nil(t, herr)
+ require.Equal(t, http.StatusOK, rr.Code)
+
+ var tmpl portainer.CustomTemplate
+ require.NoError(t, json.NewDecoder(rr.Body).Decode(&tmpl))
+ require.NotNil(t, tmpl.Artifact)
+ require.Len(t, tmpl.Artifact.Files, 1)
+ require.Equal(t, "deadbeef123", tmpl.Artifact.Files[0].Hash)
+
+ err := ds.ViewTx(func(tx dataservices.DataStoreTx) error {
+ stored, err := tx.CustomTemplate().Read(1)
+ require.NoError(t, err)
+ require.NotNil(t, stored.Artifact)
+
+ src, err := tx.Source().Read(stored.Artifact.Files[0].SourceID)
+ require.NoError(t, err)
+ require.Equal(t, portainer.SourceTypeGit, src.Type)
+ require.Equal(t, "https://github.com/example/repo", src.Git.URL)
+
+ return nil
+ })
+ require.NoError(t, err)
+}
diff --git a/api/http/handler/customtemplates/utils.go b/api/http/handler/customtemplates/utils.go
index 7ae060d49..721bfabe5 100644
--- a/api/http/handler/customtemplates/utils.go
+++ b/api/http/handler/customtemplates/utils.go
@@ -2,11 +2,49 @@ package customtemplates
import (
"errors"
+ "regexp"
portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/dataservices"
)
-func validateVariablesDefinitions(variables []portainer.CustomTemplateVariableDefinition) error {
+func populateGitConfig(tx dataservices.DataStoreTx, template *portainer.CustomTemplate) {
+ if template.Artifact == nil || len(template.Artifact.Files) == 0 {
+ return
+ }
+
+ file := template.Artifact.Files[0]
+
+ src, err := tx.Source().Read(file.SourceID)
+ if err != nil || src.Git == nil {
+ return
+ }
+
+ cfg := *src.Git
+ cfg.ReferenceName = file.Ref
+ cfg.ConfigFilePath = file.Path
+ cfg.ConfigHash = file.Hash
+
+ if cfg.Authentication != nil {
+ sanitized := *cfg.Authentication
+ sanitized.Password = ""
+ cfg.Authentication = &sanitized
+ }
+
+ template.GitConfig = &cfg
+}
+
+// IsValidNote reports whether note is safe to display. Notes containing
tags are rejected.
+func IsValidNote(note string) bool {
+ if len(note) == 0 {
+ return true
+ }
+ match, _ := regexp.MatchString("
0 {
+ return ErrSourceInUse
+ }
+
return tx.Source().Delete(portainer.SourceID(sourceID))
}); h.dataStore.IsErrObjectNotFound(err) {
return httperror.NotFound("Unable to find a source with the specified identifier", err)
} else if errors.Is(err, ErrSourceInUse) {
- return httperror.Conflict("Source is used by one or more workflows", err)
+ return httperror.Conflict("Source is used by one or more workflows or custom templates", err)
} else if err != nil {
return httperror.InternalServerError("Unable to delete source", err)
}
diff --git a/api/http/handler/gitops/sources/delete_test.go b/api/http/handler/gitops/sources/delete_test.go
index f148e962e..49ba8fac5 100644
--- a/api/http/handler/gitops/sources/delete_test.go
+++ b/api/http/handler/gitops/sources/delete_test.go
@@ -14,6 +14,7 @@ import (
func TestSourceDelete_Success(t *testing.T) {
t.Parallel()
+
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
@@ -35,6 +36,7 @@ func TestSourceDelete_Success(t *testing.T) {
func TestSourceDelete_NotFound(t *testing.T) {
t.Parallel()
+
_, store := datastore.MustNewTestStore(t, false, true)
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
@@ -50,6 +52,7 @@ func TestSourceDelete_NotFound(t *testing.T) {
func TestSourceDelete_InUse(t *testing.T) {
t.Parallel()
+
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
@@ -59,7 +62,7 @@ func TestSourceDelete_InUse(t *testing.T) {
require.NoError(t, err)
srcID = src.ID
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{SourceIDs: []portainer.SourceID{src.ID}}}}
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: src.ID}}}}}
err = tx.Workflow().Create(wf)
require.NoError(t, err)
@@ -75,6 +78,7 @@ func TestSourceDelete_InUse(t *testing.T) {
func TestSourceDelete_NonNumericID(t *testing.T) {
t.Parallel()
+
_, store := datastore.MustNewTestStore(t, false, true)
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
@@ -87,3 +91,34 @@ func TestSourceDelete_NonNumericID(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rr.Code)
}
+
+func TestSourceDelete_InUseByCustomTemplate(t *testing.T) {
+ t.Parallel()
+
+ _, store := datastore.MustNewTestStore(t, false, true)
+
+ var srcID portainer.SourceID
+ require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
+ src := &portainer.Source{Name: "in-use-by-template", Type: portainer.SourceTypeGit}
+ err := tx.Source().Create(src)
+ require.NoError(t, err)
+ srcID = src.ID
+
+ ct := &portainer.CustomTemplate{
+ ID: 1,
+ Artifact: &portainer.Artifact{
+ Files: []portainer.ArtifactFile{{SourceID: src.ID}},
+ },
+ }
+ err = tx.CustomTemplate().Create(ct)
+ require.NoError(t, err)
+
+ return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
+ }))
+
+ h := newTestHandler(t, store)
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, buildDeleteReq(t, 1, int(srcID)))
+
+ require.Equal(t, http.StatusConflict, rr.Code)
+}
diff --git a/api/http/handler/gitops/sources/fetch.go b/api/http/handler/gitops/sources/fetch.go
index 654ff02f7..c97af8f26 100644
--- a/api/http/handler/gitops/sources/fetch.go
+++ b/api/http/handler/gitops/sources/fetch.go
@@ -13,8 +13,10 @@ import (
// FetchSourceWorkflows returns the workflows and stats for a single source.
func FetchSourceWorkflows(tx dataservices.DataStoreTx, src *portainer.Source) ([]ce.Workflow, ce.SourceStats, error) {
wfs, err := tx.Workflow().ReadAll(func(wf portainer.Workflow) bool {
- return slices.ContainsFunc(wf.Artifacts, func(artifact portainer.ArtifactSources) bool {
- return slices.Contains(artifact.SourceIDs, src.ID)
+ return slices.ContainsFunc(wf.Artifacts, func(artifact portainer.Artifact) bool {
+ return slices.ContainsFunc(artifact.Files, func(f portainer.ArtifactFile) bool {
+ return f.SourceID == src.ID
+ })
})
})
if err != nil {
@@ -40,7 +42,7 @@ func FetchSourceWorkflows(tx dataservices.DataStoreTx, src *portainer.Source) ([
stats := ce.SourceStats{EndpointIDs: set.Set[portainer.EndpointID]{}}
for _, stacks := range stacks {
- items = append(items, ce.MapStackToWorkflow(stacks, src.GitConfig, unknown, unknown))
+ items = append(items, ce.MapStackToWorkflow(stacks, src.Git, unknown, unknown))
stats.WorkflowCount++
if stacks.EndpointID != 0 {
stats.EndpointIDs.Add(stacks.EndpointID)
diff --git a/api/http/handler/gitops/sources/get.go b/api/http/handler/gitops/sources/get.go
index 07e7848e5..ebb7c6b86 100644
--- a/api/http/handler/gitops/sources/get.go
+++ b/api/http/handler/gitops/sources/get.go
@@ -80,7 +80,7 @@ func (h *Handler) getSource(w http.ResponseWriter, r *http.Request) *httperror.H
return httperror.InternalServerError("Unable to retrieve source", err)
}
- detail := BuildSourceDetail(h.buildSource(r.Context(), source, stats), source.GitConfig, sourceWfs)
+ detail := BuildSourceDetail(h.buildSource(r.Context(), source, stats), source.Git, sourceWfs)
return response.JSON(w, detail)
}
diff --git a/api/http/handler/gitops/sources/helpers_test.go b/api/http/handler/gitops/sources/helpers_test.go
index 6acb04975..5a0780cd1 100644
--- a/api/http/handler/gitops/sources/helpers_test.go
+++ b/api/http/handler/gitops/sources/helpers_test.go
@@ -23,20 +23,20 @@ func createGitWorkflow(t *testing.T, tx dataservices.DataStoreTx, stack *portain
t.Helper()
src := &portainer.Source{
- Name: gittypes.RepoName(cfg.URL),
- Type: portainer.SourceTypeGit,
- GitConfig: cfg,
+ Name: gittypes.RepoName(cfg.URL),
+ Type: portainer.SourceTypeGit,
+ Git: cfg,
}
require.NoError(t, tx.Source().Create(src))
wf := &portainer.Workflow{
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- StackID: stack.ID,
- ReferenceName: cfg.ReferenceName,
- ConfigFilePath: cfg.ConfigFilePath,
- },
- SourceIDs: []portainer.SourceID{src.ID},
+ Artifacts: []portainer.Artifact{{
+ StackID: stack.ID,
+ Files: []portainer.ArtifactFile{{
+ SourceID: src.ID,
+ Path: cfg.ConfigFilePath,
+ Ref: cfg.ReferenceName,
+ }},
}},
}
require.NoError(t, tx.Workflow().Create(wf))
diff --git a/api/http/handler/gitops/sources/list_test.go b/api/http/handler/gitops/sources/list_test.go
index f81097520..7d8c40117 100644
--- a/api/http/handler/gitops/sources/list_test.go
+++ b/api/http/handler/gitops/sources/list_test.go
@@ -19,12 +19,13 @@ func TestSourcesList_GroupsByURLAndCredentials(t *testing.T) {
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
cfg := gitCfg("https://github.com/org/repo")
- src := &portainer.Source{Name: "repo", Type: portainer.SourceTypeGit, GitConfig: cfg}
+ src := &portainer.Source{Name: "repo", Type: portainer.SourceTypeGit, Git: cfg}
require.NoError(t, tx.Source().Create(src))
- wfA := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{SourceIDs: []portainer.SourceID{src.ID}}}}
+ wfA := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: src.ID}}}}}
require.NoError(t, tx.Workflow().Create(wfA))
- wfB := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{SourceIDs: []portainer.SourceID{src.ID}}}}
+
+ wfB := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: src.ID}}}}}
require.NoError(t, tx.Workflow().Create(wfB))
require.NoError(t, tx.Stack().Create(&portainer.Stack{ID: 1, Name: "stack-a", WorkflowID: wfA.ID}))
diff --git a/api/http/handler/gitops/sources/source_connection.go b/api/http/handler/gitops/sources/source_connection.go
index 3bff390a9..ae8b68aea 100644
--- a/api/http/handler/gitops/sources/source_connection.go
+++ b/api/http/handler/gitops/sources/source_connection.go
@@ -58,11 +58,11 @@ func (h *Handler) sourceTestConnection(w http.ResponseWriter, r *http.Request) *
return httperror.InternalServerError("Unable to apply source changes", err)
}
- if src.GitConfig == nil {
+ if src.Git == nil {
return httperror.InternalServerError("Source has no git configuration", nil)
}
- result := testSourceConnection(r.Context(), h.gitService, src.GitConfig)
+ result := testSourceConnection(r.Context(), h.gitService, src.Git)
return response.JSON(w, result)
}
diff --git a/api/http/handler/gitops/sources/update_git.go b/api/http/handler/gitops/sources/update_git.go
index 54cd50c3e..f6c2a725f 100644
--- a/api/http/handler/gitops/sources/update_git.go
+++ b/api/http/handler/gitops/sources/update_git.go
@@ -104,7 +104,7 @@ func (h *Handler) gitSourceUpdate(w http.ResponseWriter, r *http.Request) *httpe
return httperror.InternalServerError("Unable to update source", err)
}
- src.GitConfig = gittypes.SanitizeRepoConfig(src.GitConfig)
+ src.Git = gittypes.SanitizeRepoConfig(src.Git)
return response.JSON(w, src)
}
@@ -119,7 +119,7 @@ func ApplyGitSourceChanges(src *portainer.Source, payload GitSourceUpdatePayload
src.Name = *payload.Name
}
- gitConfig := src.GitConfig
+ gitConfig := src.Git
if gitConfig == nil {
gitConfig = &gittypes.RepoConfig{}
}
@@ -167,7 +167,7 @@ func ApplyGitSourceChanges(src *portainer.Source, payload GitSourceUpdatePayload
}
gitConfig.Authentication = auth
- src.GitConfig = gitConfig
+ src.Git = gitConfig
return nil
}
diff --git a/api/http/handler/gitops/sources/update_git_test.go b/api/http/handler/gitops/sources/update_git_test.go
index 12d72e3d4..31ff2f58f 100644
--- a/api/http/handler/gitops/sources/update_git_test.go
+++ b/api/http/handler/gitops/sources/update_git_test.go
@@ -45,8 +45,8 @@ func TestGitSourceUpdate_Success(t *testing.T) {
err = json.NewDecoder(rr.Body).Decode(&src)
require.NoError(t, err)
require.Equal(t, "new-name", src.Name)
- require.NotNil(t, src.GitConfig)
- require.Equal(t, "https://github.com/org/new.git", src.GitConfig.URL)
+ require.NotNil(t, src.Git)
+ require.Equal(t, "https://github.com/org/new.git", src.Git.URL)
}
func TestGitSourceUpdate_PreservesAuthWhenNotProvided(t *testing.T) {
@@ -58,7 +58,7 @@ func TestGitSourceUpdate_PreservesAuthWhenNotProvided(t *testing.T) {
src := &portainer.Source{
Name: "auth-source",
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/org/repo.git",
Authentication: &gittypes.GitAuthentication{
Username: "alice",
@@ -92,10 +92,10 @@ func TestGitSourceUpdate_PreservesAuthWhenNotProvided(t *testing.T) {
stored, err = tx.Source().Read(srcID)
return err
}))
- require.NotNil(t, stored.GitConfig)
- require.NotNil(t, stored.GitConfig.Authentication)
- require.Equal(t, "alice", stored.GitConfig.Authentication.Username)
- require.Equal(t, "secret", stored.GitConfig.Authentication.Password)
+ require.NotNil(t, stored.Git)
+ require.NotNil(t, stored.Git.Authentication)
+ require.Equal(t, "alice", stored.Git.Authentication.Username)
+ require.Equal(t, "secret", stored.Git.Authentication.Password)
}
func TestGitSourceUpdate_ClearsAuthWhenRequested(t *testing.T) {
@@ -107,7 +107,7 @@ func TestGitSourceUpdate_ClearsAuthWhenRequested(t *testing.T) {
src := &portainer.Source{
Name: "auth-source",
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/org/repo.git",
Authentication: &gittypes.GitAuthentication{
Username: "alice",
@@ -141,8 +141,8 @@ func TestGitSourceUpdate_ClearsAuthWhenRequested(t *testing.T) {
stored, err = tx.Source().Read(srcID)
return err
}))
- require.NotNil(t, stored.GitConfig)
- require.Nil(t, stored.GitConfig.Authentication)
+ require.NotNil(t, stored.Git)
+ require.Nil(t, stored.Git.Authentication)
}
func TestGitSourceUpdate_ReplacesAuthWhenProvided(t *testing.T) {
@@ -154,7 +154,7 @@ func TestGitSourceUpdate_ReplacesAuthWhenProvided(t *testing.T) {
src := &portainer.Source{
Name: "auth-source",
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/org/repo.git",
Authentication: &gittypes.GitAuthentication{
Username: "alice",
@@ -191,10 +191,10 @@ func TestGitSourceUpdate_ReplacesAuthWhenProvided(t *testing.T) {
stored, err = tx.Source().Read(srcID)
return err
}))
- require.NotNil(t, stored.GitConfig)
- require.NotNil(t, stored.GitConfig.Authentication)
- require.Equal(t, "bob", stored.GitConfig.Authentication.Username)
- require.Equal(t, "new-secret", stored.GitConfig.Authentication.Password)
+ require.NotNil(t, stored.Git)
+ require.NotNil(t, stored.Git.Authentication)
+ require.Equal(t, "bob", stored.Git.Authentication.Username)
+ require.Equal(t, "new-secret", stored.Git.Authentication.Password)
}
func TestGitSourceUpdate_NotFound(t *testing.T) {
@@ -225,7 +225,7 @@ func TestGitSourceUpdate_ConflictOnDuplicateURL(t *testing.T) {
existing := &portainer.Source{
Name: "existing",
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/org/existing.git",
},
}
diff --git a/api/http/handler/gitops/sources/utils.go b/api/http/handler/gitops/sources/utils.go
index 9dce04bb3..5811ad7b0 100644
--- a/api/http/handler/gitops/sources/utils.go
+++ b/api/http/handler/gitops/sources/utils.go
@@ -11,8 +11,8 @@ import (
func (h *Handler) buildSource(ctx context.Context, src *portainer.Source, stats ce.SourceStats) Source {
var status ce.Status
var sourceErr string
- if src.GitConfig != nil {
- phase, _ := ce.ComputeGitPhasesForConfig(ctx, h.gitService, src.GitConfig)
+ if src.Git != nil {
+ phase, _ := ce.ComputeGitPhasesForConfig(ctx, h.gitService, src.Git)
status = phase.Status
sourceErr = phase.Error
} else {
@@ -21,10 +21,10 @@ func (h *Handler) buildSource(ctx context.Context, src *portainer.Source, stats
url := ""
var provider gittypes.GitProvider
- if src.GitConfig != nil {
- url = gittypes.SanitizeURL(src.GitConfig.URL)
- if src.GitConfig.Authentication != nil {
- provider = src.GitConfig.Authentication.Provider
+ if src.Git != nil {
+ url = gittypes.SanitizeURL(src.Git.URL)
+ if src.Git.Authentication != nil {
+ provider = src.Git.Authentication.Provider
}
}
diff --git a/api/http/handler/gitops/workflows/helpers_test.go b/api/http/handler/gitops/workflows/helpers_test.go
index 289ed877d..73b53dc8e 100644
--- a/api/http/handler/gitops/workflows/helpers_test.go
+++ b/api/http/handler/gitops/workflows/helpers_test.go
@@ -47,17 +47,17 @@ func createGitStack(t *testing.T, tx dataservices.DataStoreTx, stack *portainer.
t.Helper()
if stack.GitConfig != nil {
- src := &portainer.Source{GitConfig: stack.GitConfig, Type: portainer.SourceTypeGit}
+ src := &portainer.Source{Git: stack.GitConfig, Type: portainer.SourceTypeGit}
require.NoError(t, tx.Source().Create(src))
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- StackID: stack.ID,
- ReferenceName: stack.GitConfig.ReferenceName,
- ConfigFilePath: stack.GitConfig.ConfigFilePath,
- ConfigHash: stack.GitConfig.ConfigHash,
- },
- SourceIDs: []portainer.SourceID{src.ID},
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{
+ StackID: stack.ID,
+ Files: []portainer.ArtifactFile{{
+ SourceID: src.ID,
+ Path: stack.GitConfig.ConfigFilePath,
+ Ref: stack.GitConfig.ReferenceName,
+ Hash: stack.GitConfig.ConfigHash,
+ }},
}}}
require.NoError(t, tx.Workflow().Create(wf))
diff --git a/api/http/handler/settings/handler.go b/api/http/handler/settings/handler.go
index 841a8b488..81a9f586c 100644
--- a/api/http/handler/settings/handler.go
+++ b/api/http/handler/settings/handler.go
@@ -20,11 +20,12 @@ func hideFields(settings *portainer.Settings) {
// Handler is the HTTP handler used to handle settings operations.
type Handler struct {
*mux.Router
- DataStore dataservices.DataStore
- FileService portainer.FileService
- JWTService portainer.JWTService
- LDAPService portainer.LDAPService
- SnapshotService portainer.SnapshotService
+ DataStore dataservices.DataStore
+ FileService portainer.FileService
+ JWTService portainer.JWTService
+ LDAPService portainer.LDAPService
+ SnapshotService portainer.SnapshotService
+ SetupTokenRequired bool
}
// NewHandler creates a handler to manage settings operations.
diff --git a/api/http/handler/settings/settings_public.go b/api/http/handler/settings/settings_public.go
index 7267d94da..3943961d3 100644
--- a/api/http/handler/settings/settings_public.go
+++ b/api/http/handler/settings/settings_public.go
@@ -44,6 +44,8 @@ type publicSettingsResponse struct {
}
IsDockerDesktopExtension bool `json:"IsDockerDesktopExtension" example:"false"`
+ // Whether the setup wizard must send the X-Setup-Token header for admin init / restore
+ RequiresSetupToken bool `json:"RequiresSetupToken" example:"false"`
}
// @id SettingsPublic
@@ -62,6 +64,7 @@ func (handler *Handler) settingsPublic(w http.ResponseWriter, r *http.Request) *
}
publicSettings := generatePublicSettings(settings)
+ publicSettings.RequiresSetupToken = handler.SetupTokenRequired
return response.JSON(w, publicSettings)
}
diff --git a/api/http/handler/stacks/response.go b/api/http/handler/stacks/response.go
index 5f0316031..e0eafc5e3 100644
--- a/api/http/handler/stacks/response.go
+++ b/api/http/handler/stacks/response.go
@@ -10,12 +10,12 @@ import (
// loadGitConfigForStack reads the merged GitConfig (Source URL/auth/TLS + Artifact ref/path/hash)
// and the SourceID for the given stack.
func loadGitConfigForStack(tx dataservices.DataStoreTx, workflowID portainer.WorkflowID, stackID portainer.StackID) (*gittypes.RepoConfig, portainer.SourceID, error) {
- src, artifact, err := workflows.GitSourceAndArtifactForStack(tx, workflowID, stackID)
+ src, file, err := workflows.GitSourceAndArtifactForStack(tx, workflowID, stackID)
if err != nil || src == nil {
return nil, 0, err
}
- return workflows.MergeSourceAndArtifact(src, artifact), src.ID, nil
+ return workflows.MergeSourceAndFile(src, file), src.ID, nil
}
func saveStackGitConfig(tx dataservices.DataStoreTx, workflowID portainer.WorkflowID, stackID portainer.StackID, oldSourceID portainer.SourceID, cfg *gittypes.RepoConfig) error {
diff --git a/api/http/handler/stacks/stack_file_test.go b/api/http/handler/stacks/stack_file_test.go
index 5a931017d..fe1afed45 100644
--- a/api/http/handler/stacks/stack_file_test.go
+++ b/api/http/handler/stacks/stack_file_test.go
@@ -40,16 +40,16 @@ func TestStackFile_GitPendingRedeploy_Returns409(t *testing.T) {
src := &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "https://github.com/portainer/portainer.git",
ConfigFilePath: "docker-compose.yml",
},
}
require.NoError(t, store.Source().Create(src))
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: stackID},
- SourceIDs: []portainer.SourceID{src.ID},
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{
+ StackID: stackID,
+ Files: []portainer.ArtifactFile{{SourceID: src.ID}},
}}}
require.NoError(t, store.Workflow().Create(wf))
diff --git a/api/http/handler/stacks/stack_stop.go b/api/http/handler/stacks/stack_stop.go
index 6f15e8d9c..447642db8 100644
--- a/api/http/handler/stacks/stack_stop.go
+++ b/api/http/handler/stacks/stack_stop.go
@@ -18,8 +18,8 @@ import (
)
// @id StackStop
-// @summary Stops a stopped Stack
-// @description Stops a stopped Stack.
+// @summary Stop a running Stack
+// @description Stop a running Stack.
// @description **Access policy**: authenticated
// @tags stacks
// @security ApiKeyAuth
diff --git a/api/http/handler/stacks/stack_update_git_redeploy.go b/api/http/handler/stacks/stack_update_git_redeploy.go
index ff9be2597..65304bdbd 100644
--- a/api/http/handler/stacks/stack_update_git_redeploy.go
+++ b/api/http/handler/stacks/stack_update_git_redeploy.go
@@ -236,8 +236,8 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request)
return err
}
- return workflows.UpdateArtifactForStack(tx, stack.WorkflowID, stack.ID, func(a *portainer.Artifact) {
- a.ConfigHash = oldConfigHash
+ return workflows.UpdateArtifactFileForStack(tx, stack.WorkflowID, stack.ID, sourceID, func(a *portainer.ArtifactFile) {
+ a.Hash = oldConfigHash
})
}); err != nil {
log.Error().Err(err).Int("stack_id", int(stack.ID)).Msg("failed to revert config hash after failed redeploy")
diff --git a/api/http/handler/stacks/stack_update_git_test.go b/api/http/handler/stacks/stack_update_git_test.go
index 786acfa8d..576160173 100644
--- a/api/http/handler/stacks/stack_update_git_test.go
+++ b/api/http/handler/stacks/stack_update_git_test.go
@@ -37,29 +37,29 @@ func TestStackUpdateGitWebhookUniqueness(t *testing.T) {
const stack2ID = portainer.StackID(457)
src1 := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/portainer/portainer.git"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/portainer/portainer.git"},
}
err = store.Source().Create(src1)
require.NoError(t, err)
- wf1 := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: stack1ID},
- SourceIDs: []portainer.SourceID{src1.ID},
+ wf1 := &portainer.Workflow{Artifacts: []portainer.Artifact{{
+ StackID: stack1ID,
+ Files: []portainer.ArtifactFile{{SourceID: src1.ID}},
}}}
err = store.Workflow().Create(wf1)
require.NoError(t, err)
src2 := &portainer.Source{
- Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{URL: "https://github.com/portainer/portainer.git"},
+ Type: portainer.SourceTypeGit,
+ Git: &gittypes.RepoConfig{URL: "https://github.com/portainer/portainer.git"},
}
err = store.Source().Create(src2)
require.NoError(t, err)
- wf2 := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: stack2ID},
- SourceIDs: []portainer.SourceID{src2.ID},
+ wf2 := &portainer.Workflow{Artifacts: []portainer.Artifact{{
+ StackID: stack2ID,
+ Files: []portainer.ArtifactFile{{SourceID: src2.ID}},
}}}
err = store.Workflow().Create(wf2)
require.NoError(t, err)
diff --git a/api/http/handler/users/admin_init.go b/api/http/handler/users/admin_init.go
index f7d75931f..dd31f7427 100644
--- a/api/http/handler/users/admin_init.go
+++ b/api/http/handler/users/admin_init.go
@@ -6,6 +6,7 @@ import (
"strings"
portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/http/security/setuptoken"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
@@ -31,17 +32,23 @@ func (payload *adminInitPayload) Validate(r *http.Request) error {
// @id UserAdminInit
// @summary Initialize administrator account
// @description Initialize the 'admin' user account.
-// @description **Access policy**: public
+// @description **Access policy**: public (requires the X-Setup-Token header on an uninitialized instance unless --no-setup-token is set)
// @tags users
// @accept json
// @produce json
+// @param X-Setup-Token header string false "Setup token (required when instance is uninitialized and --no-setup-token is not set)"
// @param body body adminInitPayload true "User details"
// @success 200 {object} portainer.User "Success"
// @failure 400 "Invalid request"
+// @failure 403 "Access denied - invalid or missing setup token"
// @failure 409 "Admin user already initialized"
// @failure 500 "Server error"
// @router /users/admin/init [post]
func (handler *Handler) adminInit(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
+ if err := setuptoken.Validate(r, handler.SetupToken); err != nil {
+ return err
+ }
+
var payload adminInitPayload
err := request.DecodeAndValidateJSONPayload(r, &payload)
if err != nil {
diff --git a/api/http/handler/users/admin_init_test.go b/api/http/handler/users/admin_init_test.go
new file mode 100644
index 000000000..62691495d
--- /dev/null
+++ b/api/http/handler/users/admin_init_test.go
@@ -0,0 +1,65 @@
+package users
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/portainer/portainer/api/apikey"
+ "github.com/portainer/portainer/api/datastore"
+ "github.com/portainer/portainer/api/http/security"
+ "github.com/portainer/portainer/api/http/security/setuptoken"
+ "github.com/portainer/portainer/api/internal/testhelpers"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func newAdminInitHandler(t *testing.T) *Handler {
+ t.Helper()
+ _, store := datastore.MustNewTestStore(t, true, false)
+ rateLimiter := security.NewRateLimiter(10, 1*time.Second, 1*time.Hour)
+ apiKeyService := apikey.NewAPIKeyService(store.APIKeyRepository(), store.User())
+ h := NewHandler(testhelpers.NewTestRequestBouncer(), rateLimiter, apiKeyService, mockPasswordStrengthChecker{})
+ h.DataStore = store
+ h.CryptoService = testhelpers.NewCryptoService()
+ h.AdminCreationDone = make(chan struct{}, 1)
+ return h
+}
+
+func Test_adminInit_setupTokenGate(t *testing.T) {
+ t.Parallel()
+
+ t.Run("403 without token header", func(t *testing.T) {
+ handler := newAdminInitHandler(t)
+ handler.SetupToken = "secret-token"
+ body := strings.NewReader(`{"Username":"admin","Password":"abcdefgh12"}`)
+ r := httptest.NewRequest(http.MethodPost, "/users/admin/init", body)
+ err := handler.adminInit(httptest.NewRecorder(), r)
+ require.NotNil(t, err)
+ assert.Equal(t, http.StatusForbidden, err.StatusCode)
+ })
+
+ t.Run("403 with wrong token", func(t *testing.T) {
+ handler := newAdminInitHandler(t)
+ handler.SetupToken = "secret-token"
+ body := strings.NewReader(`{"Username":"admin","Password":"abcdefgh12"}`)
+ r := httptest.NewRequest(http.MethodPost, "/users/admin/init", body)
+ r.Header.Set(setuptoken.HeaderName, "wrong")
+ err := handler.adminInit(httptest.NewRecorder(), r)
+ require.NotNil(t, err)
+ assert.Equal(t, http.StatusForbidden, err.StatusCode)
+ })
+
+ t.Run("succeeds with correct token", func(t *testing.T) {
+ handler := newAdminInitHandler(t)
+ handler.SetupToken = "secret-token"
+ body := strings.NewReader(`{"Username":"admin","Password":"abcdefgh12"}`)
+ r := httptest.NewRequest(http.MethodPost, "/users/admin/init", body)
+ r.Header.Set(setuptoken.HeaderName, "secret-token")
+ err := handler.adminInit(httptest.NewRecorder(), r)
+ assert.Nil(t, err)
+ })
+}
diff --git a/api/http/handler/users/handler.go b/api/http/handler/users/handler.go
index 60af6e05a..1c2d6eb3a 100644
--- a/api/http/handler/users/handler.go
+++ b/api/http/handler/users/handler.go
@@ -35,6 +35,7 @@ type Handler struct {
passwordStrengthChecker security.PasswordStrengthChecker
AdminCreationDone chan<- struct{}
FileService portainer.FileService
+ SetupToken string
}
// NewHandler creates a handler to manage user operations.
diff --git a/api/http/proxy/factory/agent.go b/api/http/proxy/factory/agent.go
index 821b7ada0..ac6d6892e 100644
--- a/api/http/proxy/factory/agent.go
+++ b/api/http/proxy/factory/agent.go
@@ -10,6 +10,7 @@ import (
"github.com/portainer/portainer/api/http/proxy/factory/agent"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/url"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
@@ -40,21 +41,30 @@ func (factory *ProxyFactory) NewAgentProxy(endpoint *portainer.Endpoint) (*Proxy
}
endpointURL.Scheme = "http"
- httpTransport := &http.Transport{}
+ var innerTransport *http.Transport
if endpoint.TLSConfig.TLS || endpoint.TLSConfig.TLSSkipVerify {
- config, err := crypto.CreateTLSConfigurationFromDisk(endpoint.TLSConfig)
+ tlsConfig, err := crypto.CreateTLSConfigurationFromDisk(endpoint.TLSConfig)
if err != nil {
return nil, errors.WithMessage(err, "failed generating tls configuration")
}
- httpTransport.TLSClientConfig = config
endpointURL.Scheme = "https"
+
+ if endpointutils.IsEdgeEndpoint(endpoint) {
+ innerTransport = ssrf.WrapTransportInternal(&http.Transport{TLSClientConfig: tlsConfig})
+ } else {
+ innerTransport = ssrf.WrapTransport(&http.Transport{TLSClientConfig: tlsConfig})
+ }
+ } else if endpointutils.IsEdgeEndpoint(endpoint) {
+ innerTransport = ssrf.WrapTransportInternal(&http.Transport{})
+ } else {
+ innerTransport = ssrf.WrapTransport(&http.Transport{})
}
proxy := NewSingleHostReverseProxyWithHostHeader(endpointURL)
- proxy.Transport = agent.NewTransport(factory.signatureService, httpTransport)
+ proxy.Transport = agent.NewTransport(factory.signatureService, innerTransport)
proxyServer := &ProxyServer{
server: &http.Server{
diff --git a/api/http/proxy/factory/docker.go b/api/http/proxy/factory/docker.go
index 0db22aa4c..f49b09374 100644
--- a/api/http/proxy/factory/docker.go
+++ b/api/http/proxy/factory/docker.go
@@ -8,8 +8,10 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/http/proxy/factory/docker"
+ "github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/url"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/rs/zerolog/log"
)
@@ -47,17 +49,6 @@ func (factory *ProxyFactory) newDockerHTTPProxy(endpoint *portainer.Endpoint) (h
}
endpointURL.Scheme = "http"
- httpTransport := &http.Transport{}
-
- if endpoint.TLSConfig.TLS || endpoint.TLSConfig.TLSSkipVerify {
- config, err := crypto.CreateTLSConfigurationFromDisk(endpoint.TLSConfig)
- if err != nil {
- return nil, err
- }
-
- httpTransport.TLSClientConfig = config
- endpointURL.Scheme = "https"
- }
transportParameters := &docker.TransportParameters{
Endpoint: endpoint,
@@ -67,7 +58,27 @@ func (factory *ProxyFactory) newDockerHTTPProxy(endpoint *portainer.Endpoint) (h
DockerClientFactory: factory.dockerClientFactory,
}
- dockerTransport, err := docker.NewTransport(transportParameters, httpTransport, factory.gitService, factory.snapshotService)
+ var innerTransport *http.Transport
+ if endpoint.TLSConfig.TLS || endpoint.TLSConfig.TLSSkipVerify {
+ tlsConfig, err := crypto.CreateTLSConfigurationFromDisk(endpoint.TLSConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ endpointURL.Scheme = "https"
+
+ if endpointutils.IsEdgeEndpoint(endpoint) {
+ innerTransport = ssrf.WrapTransportInternal(&http.Transport{TLSClientConfig: tlsConfig})
+ } else {
+ innerTransport = ssrf.WrapTransport(&http.Transport{TLSClientConfig: tlsConfig})
+ }
+ } else if endpointutils.IsEdgeEndpoint(endpoint) {
+ innerTransport = ssrf.WrapTransportInternal(&http.Transport{})
+ } else {
+ innerTransport = ssrf.WrapTransport(&http.Transport{})
+ }
+
+ dockerTransport, err := docker.NewTransport(transportParameters, innerTransport, factory.gitService, factory.snapshotService)
if err != nil {
return nil, err
}
diff --git a/api/http/proxy/factory/github/client.go b/api/http/proxy/factory/github/client.go
index e075e12b9..d455d79f2 100644
--- a/api/http/proxy/factory/github/client.go
+++ b/api/http/proxy/factory/github/client.go
@@ -8,6 +8,8 @@ import (
"strings"
"time"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
+
"github.com/rs/zerolog/log"
"github.com/segmentio/encoding/json"
"oras.land/oras-go/v2/registry/remote/retry"
@@ -92,7 +94,7 @@ func NewHTTPClient(token string) *http.Client {
return &http.Client{
Transport: &tokenTransport{
token: token,
- transport: retry.NewTransport(&http.Transport{}), // Use ORAS retry transport for consistent rate limiting and error handling
+ transport: retry.NewTransport(ssrf.WrapTransport(&http.Transport{})), // Use ORAS retry transport for consistent rate limiting and error handling
},
Timeout: 1 * time.Minute,
}
diff --git a/api/http/proxy/factory/gitlab/client.go b/api/http/proxy/factory/gitlab/client.go
index 050dab881..d4deb18f3 100644
--- a/api/http/proxy/factory/gitlab/client.go
+++ b/api/http/proxy/factory/gitlab/client.go
@@ -8,6 +8,8 @@ import (
"net/http"
"time"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
+
"github.com/rs/zerolog/log"
"github.com/segmentio/encoding/json"
"oras.land/oras-go/v2/registry/remote/retry"
@@ -92,7 +94,7 @@ type Transport struct {
// interface for proxying requests to the Gitlab API.
func NewTransport() *Transport {
return &Transport{
- httpTransport: &http.Transport{},
+ httpTransport: ssrf.WrapTransport(&http.Transport{}),
}
}
@@ -117,7 +119,7 @@ func NewHTTPClient(token string) *http.Client {
return &http.Client{
Transport: &tokenTransport{
token: token,
- transport: retry.NewTransport(&http.Transport{}), // Use ORAS retry transport for consistent rate limiting and error handling
+ transport: retry.NewTransport(ssrf.WrapTransport(&http.Transport{})), // Use ORAS retry transport for consistent rate limiting and error handling
},
Timeout: 1 * time.Minute,
}
diff --git a/api/http/proxy/factory/kubernetes/agent_transport.go b/api/http/proxy/factory/kubernetes/agent_transport.go
index 4a62e2367..c8232b1b8 100644
--- a/api/http/proxy/factory/kubernetes/agent_transport.go
+++ b/api/http/proxy/factory/kubernetes/agent_transport.go
@@ -8,6 +8,7 @@ import (
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/kubernetes/cli"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
)
type agentTransport struct {
@@ -24,9 +25,9 @@ func NewAgentTransport(signatureService portainer.DigitalSignatureService, token
transport := &agentTransport{
baseTransport: newBaseTransport(
- &http.Transport{
+ ssrf.WrapTransport(&http.Transport{
TLSClientConfig: tlsConfig,
- },
+ }),
tokenManager,
endpoint,
k8sClientFactory,
diff --git a/api/http/proxy/factory/kubernetes/edge_transport.go b/api/http/proxy/factory/kubernetes/edge_transport.go
index 73946114e..cd817eb6b 100644
--- a/api/http/proxy/factory/kubernetes/edge_transport.go
+++ b/api/http/proxy/factory/kubernetes/edge_transport.go
@@ -7,6 +7,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/kubernetes/cli"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
)
type edgeTransport struct {
@@ -21,7 +22,7 @@ func NewEdgeTransport(dataStore dataservices.DataStore, signatureService portain
reverseTunnelService: reverseTunnelService,
signatureService: signatureService,
baseTransport: newBaseTransport(
- &http.Transport{},
+ ssrf.WrapTransportInternal(&http.Transport{}),
tokenManager,
endpoint,
k8sClientFactory,
diff --git a/api/http/proxy/factory/kubernetes/edge_transport_test.go b/api/http/proxy/factory/kubernetes/edge_transport_test.go
new file mode 100644
index 000000000..ddf40d839
--- /dev/null
+++ b/api/http/proxy/factory/kubernetes/edge_transport_test.go
@@ -0,0 +1,14 @@
+package kubernetes
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestNewEdgeTransport(t *testing.T) {
+ t.Parallel()
+
+ transport := NewEdgeTransport(nil, nil, nil, nil, nil, nil, nil)
+ require.NotNil(t, transport)
+}
diff --git a/api/http/proxy/factory/kubernetes/local_transport.go b/api/http/proxy/factory/kubernetes/local_transport.go
index bc832f35c..6c22c5dc5 100644
--- a/api/http/proxy/factory/kubernetes/local_transport.go
+++ b/api/http/proxy/factory/kubernetes/local_transport.go
@@ -7,6 +7,7 @@ import (
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/kubernetes/cli"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
)
type localTransport struct {
@@ -22,9 +23,9 @@ func NewLocalTransport(tokenManager *tokenManager, endpoint *portainer.Endpoint,
transport := &localTransport{
baseTransport: newBaseTransport(
- &http.Transport{
+ ssrf.WrapTransportInternal(&http.Transport{
TLSClientConfig: config,
- },
+ }),
tokenManager,
endpoint,
k8sClientFactory,
diff --git a/api/http/proxy/factory/transport_test.go b/api/http/proxy/factory/transport_test.go
new file mode 100644
index 000000000..d6c623a66
--- /dev/null
+++ b/api/http/proxy/factory/transport_test.go
@@ -0,0 +1,200 @@
+package factory
+
+import (
+ "context"
+ "net/http/httputil"
+ "testing"
+ "time"
+
+ portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/api/http/proxy/factory/docker"
+ "github.com/portainer/portainer/pkg/fips"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
+
+ "github.com/stretchr/testify/require"
+)
+
+func init() {
+ fips.InitFIPS(false)
+}
+
+type stubTunnelService struct{}
+
+func (s *stubTunnelService) StartTunnelServer(addr, port string, snapshotService portainer.SnapshotService) error {
+ return nil
+}
+
+func (s *stubTunnelService) StopTunnelServer() error { return nil }
+
+func (s *stubTunnelService) GenerateEdgeKey(apiURL, tunnelAddr string, endpointIdentifier int) string {
+ return ""
+}
+
+func (s *stubTunnelService) Open(endpoint *portainer.Endpoint) error { return nil }
+
+func (s *stubTunnelService) Config(endpointID portainer.EndpointID) portainer.TunnelDetails {
+ return portainer.TunnelDetails{}
+}
+
+func (s *stubTunnelService) TunnelAddr(endpoint *portainer.Endpoint) (string, error) {
+ return "127.0.0.1:9999", nil
+}
+
+func (s *stubTunnelService) UpdateLastActivity(endpointID portainer.EndpointID) {}
+
+func (s *stubTunnelService) KeepTunnelAlive(endpointID portainer.EndpointID, ctx context.Context, maxKeepAlive time.Duration) {
+}
+
+func enableSSRF(t *testing.T) {
+ t.Helper()
+ ssrf.Configure(ssrf.Policy{Mode: ssrf.ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { ssrf.Configure(ssrf.Policy{}) })
+}
+
+// TestNewDockerHTTPProxy_NonEdgeNoTLS verifies that a plain non-edge endpoint
+// uses WrapTransport, setting DialContext on the inner transport.
+func TestNewDockerHTTPProxy_NonEdgeNoTLS(t *testing.T) {
+ enableSSRF(t)
+
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.DockerEnvironment,
+ URL: "tcp://192.168.1.100:2376",
+ }
+
+ handler, err := f.newDockerHTTPProxy(endpoint)
+ require.NoError(t, err)
+
+ proxy := handler.(*httputil.ReverseProxy)
+ dt := proxy.Transport.(*docker.Transport)
+ require.NotNil(t, dt.HTTPTransport.DialContext)
+}
+
+// TestNewDockerHTTPProxy_NonEdgeTLS verifies that a TLS non-edge endpoint
+// uses WrapTransport, setting DialContext on the inner transport.
+func TestNewDockerHTTPProxy_NonEdgeTLS(t *testing.T) {
+ enableSSRF(t)
+
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.DockerEnvironment,
+ URL: "tcp://192.168.1.100:2376",
+ TLSConfig: portainer.TLSConfiguration{
+ TLS: true,
+ TLSSkipVerify: true,
+ },
+ }
+
+ handler, err := f.newDockerHTTPProxy(endpoint)
+ require.NoError(t, err)
+
+ proxy := handler.(*httputil.ReverseProxy)
+ dt := proxy.Transport.(*docker.Transport)
+ require.NotNil(t, dt.HTTPTransport.DialContext)
+}
+
+// TestNewDockerHTTPProxy_EdgeNoTLS verifies that an edge endpoint without TLS
+// uses WrapTransportInternal, leaving DialContext nil.
+func TestNewDockerHTTPProxy_EdgeNoTLS(t *testing.T) {
+ enableSSRF(t)
+
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.EdgeAgentOnDockerEnvironment,
+ URL: "tcp://192.168.1.100:2376",
+ }
+
+ handler, err := f.newDockerHTTPProxy(endpoint)
+ require.NoError(t, err)
+
+ proxy := handler.(*httputil.ReverseProxy)
+ dt := proxy.Transport.(*docker.Transport)
+ require.Nil(t, dt.HTTPTransport.DialContext)
+}
+
+// TestNewDockerHTTPProxy_EdgeTLS verifies that an edge endpoint with TLS
+// uses WrapTransportInternal, leaving DialContext nil.
+func TestNewDockerHTTPProxy_EdgeTLS(t *testing.T) {
+ enableSSRF(t)
+
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.EdgeAgentOnDockerEnvironment,
+ URL: "tcp://192.168.1.100:2376",
+ TLSConfig: portainer.TLSConfiguration{
+ TLS: true,
+ TLSSkipVerify: true,
+ },
+ }
+
+ handler, err := f.newDockerHTTPProxy(endpoint)
+ require.NoError(t, err)
+
+ proxy := handler.(*httputil.ReverseProxy)
+ dt := proxy.Transport.(*docker.Transport)
+ require.Nil(t, dt.HTTPTransport.DialContext)
+}
+
+func TestNewAgentProxy_NonEdgeNoTLS(t *testing.T) {
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.AgentOnDockerEnvironment,
+ URL: "tcp://192.168.1.100:9001",
+ }
+
+ proxyServer, err := f.NewAgentProxy(endpoint)
+ require.NoError(t, err)
+ defer proxyServer.Close()
+
+ require.Positive(t, proxyServer.Port)
+}
+
+func TestNewAgentProxy_NonEdgeTLS(t *testing.T) {
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.AgentOnDockerEnvironment,
+ URL: "tcp://192.168.1.100:9001",
+ TLSConfig: portainer.TLSConfiguration{
+ TLS: true,
+ TLSSkipVerify: true,
+ },
+ }
+
+ proxyServer, err := f.NewAgentProxy(endpoint)
+ require.NoError(t, err)
+ defer proxyServer.Close()
+
+ require.Positive(t, proxyServer.Port)
+}
+
+func TestNewAgentProxy_EdgeNoTLS(t *testing.T) {
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.EdgeAgentOnDockerEnvironment,
+ URL: "tcp://192.168.1.100:9001",
+ }
+
+ proxyServer, err := f.NewAgentProxy(endpoint)
+ require.NoError(t, err)
+ defer proxyServer.Close()
+
+ require.Positive(t, proxyServer.Port)
+}
+
+func TestNewAgentProxy_EdgeTLS(t *testing.T) {
+ f := &ProxyFactory{reverseTunnelService: &stubTunnelService{}}
+ endpoint := &portainer.Endpoint{
+ Type: portainer.EdgeAgentOnDockerEnvironment,
+ URL: "tcp://192.168.1.100:9001",
+ TLSConfig: portainer.TLSConfiguration{
+ TLS: true,
+ TLSSkipVerify: true,
+ },
+ }
+
+ proxyServer, err := f.NewAgentProxy(endpoint)
+ require.NoError(t, err)
+ defer proxyServer.Close()
+
+ require.Positive(t, proxyServer.Port)
+}
diff --git a/api/http/security/setuptoken/setuptoken.go b/api/http/security/setuptoken/setuptoken.go
new file mode 100644
index 000000000..ec1af9927
--- /dev/null
+++ b/api/http/security/setuptoken/setuptoken.go
@@ -0,0 +1,48 @@
+// Package setuptoken provides a one-time setup token used to protect the
+// public initialization endpoints (admin account creation and backup restore)
+// on an uninitialized Portainer instance.
+package setuptoken
+
+import (
+ "crypto/rand"
+ "crypto/subtle"
+ "encoding/hex"
+ "errors"
+ "net/http"
+
+ httperror "github.com/portainer/portainer/pkg/libhttp/error"
+)
+
+// HeaderName is the HTTP header that carries the setup token.
+const HeaderName = "X-Setup-Token"
+
+// tokenByteLength is the number of random bytes before hex-encoding (256 bits).
+const tokenByteLength = 32
+
+var errInvalidSetupToken = errors.New("invalid or missing setup token")
+
+// Generate returns a cryptographically random, hex-encoded setup token.
+func Generate() (string, error) {
+ b := make([]byte, tokenByteLength)
+ if _, err := rand.Read(b); err != nil {
+ return "", err
+ }
+
+ return hex.EncodeToString(b), nil
+}
+
+// Validate checks that the request carries the expected setup token in the
+// X-Setup-Token header. When expected is empty the gate is disabled and the
+// request is always allowed. Comparison is constant-time.
+func Validate(r *http.Request, expected string) *httperror.HandlerError {
+ if expected == "" {
+ return nil
+ }
+
+ provided := r.Header.Get(HeaderName)
+ if subtle.ConstantTimeCompare([]byte(provided), []byte(expected)) != 1 {
+ return httperror.Forbidden("Invalid or missing setup token. Provide the X-Setup-Token header with the token printed in the server logs at startup.", errInvalidSetupToken)
+ }
+
+ return nil
+}
diff --git a/api/http/security/setuptoken/setuptoken_test.go b/api/http/security/setuptoken/setuptoken_test.go
new file mode 100644
index 000000000..589fa327b
--- /dev/null
+++ b/api/http/security/setuptoken/setuptoken_test.go
@@ -0,0 +1,46 @@
+package setuptoken
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func Test_Generate(t *testing.T) {
+ token, err := Generate()
+ require.NoError(t, err)
+ assert.Len(t, token, 64, "hex-encoded 32 bytes should be 64 characters")
+
+ token2, err := Generate()
+ require.NoError(t, err)
+ assert.NotEqual(t, token, token2, "two generated tokens should differ")
+}
+
+func Test_Validate_alwaysPassesWhenExpectedEmpty(t *testing.T) {
+ r := httptest.NewRequest(http.MethodPost, "/", nil)
+ assert.Nil(t, Validate(r, ""))
+}
+
+func Test_Validate_missingHeader(t *testing.T) {
+ r := httptest.NewRequest(http.MethodPost, "/", nil)
+ herr := Validate(r, "secret")
+ require.NotNil(t, herr)
+ assert.Equal(t, http.StatusForbidden, herr.StatusCode)
+}
+
+func Test_Validate_wrongToken(t *testing.T) {
+ r := httptest.NewRequest(http.MethodPost, "/", nil)
+ r.Header.Set(HeaderName, "wrong")
+ herr := Validate(r, "secret")
+ require.NotNil(t, herr)
+ assert.Equal(t, http.StatusForbidden, herr.StatusCode)
+}
+
+func Test_Validate_correctToken(t *testing.T) {
+ r := httptest.NewRequest(http.MethodPost, "/", nil)
+ r.Header.Set(HeaderName, "secret")
+ assert.Nil(t, Validate(r, "secret"))
+}
diff --git a/api/http/server.go b/api/http/server.go
index 4f960a7e8..c56d1cd33 100644
--- a/api/http/server.go
+++ b/api/http/server.go
@@ -113,6 +113,7 @@ type Server struct {
PlatformService platform.Service
PullLimitCheckDisabled bool
TrustedOrigins []string
+ SetupToken string
}
// Start starts the HTTP server
@@ -149,6 +150,7 @@ func (server *Server) Start(ctx context.Context) error {
server.ShutdownTrigger,
adminMonitor,
)
+ backupHandler.SetupToken = server.SetupToken
var roleHandler = roles.NewHandler(requestBouncer)
roleHandler.DataStore = server.DataStore
@@ -235,6 +237,7 @@ func (server *Server) Start(ctx context.Context) error {
settingsHandler.JWTService = server.JWTService
settingsHandler.LDAPService = server.LDAPService
settingsHandler.SnapshotService = server.SnapshotService
+ settingsHandler.SetupTokenRequired = server.SetupToken != ""
var sslHandler = sslhandler.NewHandler(requestBouncer)
sslHandler.SSLService = server.SSLService
@@ -282,6 +285,7 @@ func (server *Server) Start(ctx context.Context) error {
userHandler.CryptoService = server.CryptoService
userHandler.AdminCreationDone = server.AdminCreationDone
userHandler.FileService = server.FileService
+ userHandler.SetupToken = server.SetupToken
var websocketHandler = websocket.NewHandler(server.KubernetesTokenCacheManager, requestBouncer)
websocketHandler.DataStore = server.DataStore
diff --git a/api/internal/registryutils/ecr_reg_token.go b/api/internal/registryutils/ecr_reg_token.go
index 107204d29..a4387a8e7 100644
--- a/api/internal/registryutils/ecr_reg_token.go
+++ b/api/internal/registryutils/ecr_reg_token.go
@@ -38,9 +38,9 @@ func doGetRegToken(tx dataservices.DataStoreTx, registry *portainer.Registry) er
return tx.Registry().Update(registry.ID, registry)
}
-// ValidateRegistriesECRTokens refreshes and persists ECR tokens for all registries that need it.
+// RefreshAndPersistECRTokens refreshes and persists ECR tokens for all registries that need it.
// Must be called with a real DataStoreTx (not a top-level DataStore) to avoid write-lock contention.
-func ValidateRegistriesECRTokens(tx dataservices.DataStoreTx, registries []portainer.Registry) error {
+func RefreshAndPersistECRTokens(tx dataservices.DataStoreTx, registries []portainer.Registry) {
for i := range registries {
reg := ®istries[i]
if reg.Type != portainer.EcrRegistry {
@@ -50,11 +50,12 @@ func ValidateRegistriesECRTokens(tx dataservices.DataStoreTx, registries []porta
continue
}
if err := doGetRegToken(tx, reg); err != nil {
- return fmt.Errorf("ECR registry %q credentials are invalid or expired. Error: %w", reg.Name, err)
+ log.Warn().
+ Err(err).
+ Str("RegistryName", reg.Name).
+ Msg("Failed to get valid ECR registry token. Skip logging with this registry.")
}
}
-
- return nil
}
func EnsureRegTokenValid(tx dataservices.DataStoreTx, registry *portainer.Registry) error {
diff --git a/api/internal/registryutils/ecr_reg_token_test.go b/api/internal/registryutils/ecr_reg_token_test.go
index 70a8ce1b8..2c9b9a303 100644
--- a/api/internal/registryutils/ecr_reg_token_test.go
+++ b/api/internal/registryutils/ecr_reg_token_test.go
@@ -1,6 +1,8 @@
package registryutils_test
import (
+ "io"
+ "strings"
"testing"
"time"
@@ -8,6 +10,7 @@ import (
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
"github.com/portainer/portainer/api/internal/registryutils"
+ zerolog "github.com/rs/zerolog/log"
"github.com/stretchr/testify/require"
)
@@ -24,75 +27,40 @@ func newECRRegistry(id portainer.RegistryID, accessToken string, expiry int64) p
}
}
-func TestValidateRegistriesECRTokens(t *testing.T) {
- t.Parallel()
-
- t.Run("skips non-ECR registries without error", func(t *testing.T) {
- t.Parallel()
- _, ds := datastore.MustNewTestStore(t, true, false)
- registries := []portainer.Registry{
- {ID: 1, Type: portainer.DockerHubRegistry, Name: "dockerhub"},
- {ID: 2, Type: portainer.CustomRegistry, Name: "custom"},
- }
- require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return registryutils.ValidateRegistriesECRTokens(tx, registries)
- }))
- })
-
- t.Run("skips ECR registries with valid tokens", func(t *testing.T) {
- t.Parallel()
+func TestRefreshAndPersistECRTokens(t *testing.T) {
+ t.Run("does not modify token for ECR registry with valid token", func(t *testing.T) {
_, ds := datastore.MustNewTestStore(t, true, false)
reg := newECRRegistry(1, "valid-token", time.Now().Add(time.Hour).Unix())
require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return registryutils.ValidateRegistriesECRTokens(tx, []portainer.Registry{reg})
+ registryutils.RefreshAndPersistECRTokens(tx, []portainer.Registry{reg})
+ return nil
}))
+ require.Equal(t, "valid-token", reg.AccessToken)
})
- t.Run("returns nil for empty registry list", func(t *testing.T) {
- t.Parallel()
- _, ds := datastore.MustNewTestStore(t, true, false)
- require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return registryutils.ValidateRegistriesECRTokens(tx, []portainer.Registry{})
- }))
- })
+ t.Run("does not block and leaves token empty when ECR token refresh fails", func(t *testing.T) {
+ var logOutput strings.Builder
+ setupLogOutput(t, &logOutput)
- t.Run("returns error for ECR registry with missing token", func(t *testing.T) {
- t.Parallel()
_, ds := datastore.MustNewTestStore(t, true, false)
reg := newECRRegistry(1, "", 0)
require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return tx.Registry().Create(®)
- }))
-
- var validateErr error
- _ = ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- validateErr = registryutils.ValidateRegistriesECRTokens(tx, []portainer.Registry{reg})
+ registryutils.RefreshAndPersistECRTokens(tx, []portainer.Registry{reg})
return nil
- })
- require.Error(t, validateErr)
- require.Contains(t, validateErr.Error(), "test-ecr")
+ }))
+ require.Empty(t, reg.AccessToken)
+ require.Contains(t, logOutput.String(), "test-ecr")
+ require.Contains(t, logOutput.String(), "Failed to get valid ECR registry token")
})
+}
- t.Run("stops on first invalid ECR registry and includes its name in error", func(t *testing.T) {
- t.Parallel()
- _, ds := datastore.MustNewTestStore(t, true, false)
+func setupLogOutput(t *testing.T, w io.Writer) {
+ t.Helper()
- validECR := newECRRegistry(1, "valid-token", time.Now().Add(time.Hour).Unix())
- invalidECR := newECRRegistry(2, "", 0)
- invalidECR.Name = "invalid-ecr"
- nonECR := portainer.Registry{ID: 3, Type: portainer.DockerHubRegistry}
-
- require.NoError(t, ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- return tx.Registry().Create(&invalidECR)
- }))
-
- var validateErr error
- _ = ds.UpdateTx(func(tx dataservices.DataStoreTx) error {
- validateErr = registryutils.ValidateRegistriesECRTokens(tx, []portainer.Registry{validECR, invalidECR, nonECR})
- return nil
- })
- require.Error(t, validateErr)
- require.Contains(t, validateErr.Error(), "invalid-ecr")
+ oldLogger := zerolog.Logger
+ zerolog.Logger = zerolog.Output(w)
+ t.Cleanup(func() {
+ zerolog.Logger = oldLogger
})
}
diff --git a/api/portainer.go b/api/portainer.go
index aca2dc475..e30f1a7b6 100644
--- a/api/portainer.go
+++ b/api/portainer.go
@@ -111,6 +111,10 @@ type (
KubectlShellImageSet bool
PullLimitCheckDisabled *bool
TrustedOrigins *string
+ SSRFMode *string
+ SSRFAllowedHosts *[]string
+ NoSetupToken *bool
+ SetupToken *string
}
// CustomTemplateVariableDefinition
@@ -150,6 +154,7 @@ type (
ResourceControl *ResourceControl `json:"ResourceControl"`
Variables []CustomTemplateVariableDefinition
GitConfig *gittypes.RepoConfig `json:"GitConfig"`
+ Artifact *Artifact `json:"artifact,omitempty"`
// IsComposeFormat indicates if the Kubernetes template is created from a Docker Compose file
IsComposeFormat bool `example:"false"`
// EdgeTemplate indicates if this template purpose for Edge Stack
@@ -558,8 +563,9 @@ type (
}
PolicyChartSummary struct {
- ChartName string `json:"ChartName"`
- Fingerprint string `json:"Fingerprint"`
+ ChartName string `json:"ChartName"`
+ Fingerprint string `json:"Fingerprint"`
+ PolicyID PolicyID `json:"PolicyID,omitempty"` // 0 when server hasn't populated the field
}
PolicyChartStatus struct {
@@ -581,17 +587,19 @@ type (
}
PolicyChartBundle struct {
- PolicyChartSummary `mapstructure:",squash"`
- EncodedTgz string `json:"EncodedTgz"`
- Namespace string `json:"Namespace"`
- ReleaseName string `json:"ReleaseName,omitempty"`
+ PolicyChartSummary `mapstructure:",squash"`
+ EncodedTgz string `json:"EncodedTgz"`
+ Namespace string `json:"Namespace"`
+ ReleaseName string `json:"ReleaseName,omitempty"`
+ // Base64 YAML kubectl-applied by the agent before Helm install when set (e.g. Gatekeeper gatekeeper-system namespace + PSA labels).
PreReleaseManifest string `json:"PreReleaseManifest,omitempty"`
EncodedValues string `json:"EncodedValues"`
PreInstallDeletions []ResourceDeletion `json:"PreInstallDeletions,omitempty"`
PreInstallAdoptions []ResourceAdoption `json:"PreInstallAdoptions,omitempty"`
+ // WaitForCRDs lists CRD names that must be registered in API discovery after
+ // this chart installs before the agent proceeds to the next chart.
+ WaitForCRDs []string `json:"WaitForCRDs,omitempty"`
// NoWait disables waiting for pods to be ready after install.
- // Set to true for externally sourced charts whose startup timing cannot be controlled,
- // to avoid leaving the release stuck in pending-install.
NoWait bool `json:"NoWait,omitempty"`
}
@@ -621,6 +629,45 @@ type (
PolicyID int
+ // PolicyDesiredState is the per-policy desired state sent from server to agent
+ // in PollStatusResponse.PolicyStates (per-policy payload format).
+ PolicyDesiredState struct {
+ PolicyID PolicyID `json:"policyID"`
+ Type string `json:"type"` // e.g. "helm-k8s"
+ Fingerprint string `json:"fingerprint"` // install-affecting only; restore manifest excluded
+ Config []byte `json:"config"` // handler-specific config blob (e.g. HelmPolicyConfig JSON)
+ }
+
+ // PolicyStatesAsyncPayload is the value of an async "policyStates" command.
+ // States carries per-policy desired state; ChartBundles carries helm chart tarballs
+ // for policies that need installing (emitted only on mutation, never idle polls).
+ PolicyStatesAsyncPayload struct {
+ States []PolicyDesiredState `json:"states"`
+ ChartBundles []PolicyChartBundle `json:"chartBundles,omitempty"`
+ RestoreBundle RestoreSettingsBundle `json:"restoreBundle,omitempty"`
+ }
+
+ // PolicyActualState is the per-policy actual state reported by the agent
+ // via PUT /endpoints/{id}/edge/policies/statuses.
+ PolicyActualState struct {
+ PolicyID PolicyID `json:"policyID"`
+ Type string `json:"type"`
+ Fingerprint string `json:"fingerprint"`
+ Status string `json:"status"` // applying|applied|failed|removing
+ Message string `json:"message,omitempty"`
+ }
+
+ // HelmPolicyConfig is the Config payload for "helm-k8s" PolicyDesiredState entries.
+ // Bundles are not included in the poll response Config — they travel separately
+ // (sync: on-demand GetCharts; async: PolicyStatesCommandPayload.ChartBundles).
+ // RestoreSettings is helm-internal metadata and is intentionally NOT part of the
+ // fingerprint — see Fingerprint contract in reconcile-refactor-plan.md.
+ HelmPolicyConfig struct {
+ Charts []PolicyChartSummary `json:"charts"`
+ Bundles []PolicyChartBundle `json:"bundles,omitempty"`
+ RestoreSettings *RestoreSettings `json:"restoreSettings,omitempty"`
+ }
+
// PolicyType represents the type of policy
PolicyType string
)
@@ -1254,13 +1301,13 @@ type (
// Source represents a GitOps source that can be referenced by stacks or deployments.
Source struct {
- ID SourceID `json:"id" example:"1"`
- Name string `json:"name" example:"my-source"`
- LastSync int64 `json:"lastSync,omitempty" example:"1587399600"`
- Type SourceType `json:"type" example:"1"`
- GitConfig *gittypes.RepoConfig `json:"gitConfig,omitempty"`
- Registry *Registry `json:"registry,omitempty"`
- HelmConfig *HelmConfig `json:"helmConfig,omitempty"`
+ ID SourceID `json:"id" example:"1"`
+ Name string `json:"name" example:"my-source"`
+ LastSync int64 `json:"lastSync,omitempty" example:"1587399600"`
+ Type SourceType `json:"type" example:"1"`
+ Git *gittypes.RepoConfig `json:"git,omitempty"`
+ Registry *Registry `json:"registry,omitempty"`
+ Helm *HelmConfig `json:"helm,omitempty"`
}
// SourceID represents a source identifier
@@ -1489,11 +1536,11 @@ type (
// User represents a user account
User struct {
// User Identifier
- ID UserID `json:"Id" example:"1"`
- Username string `json:"Username" example:"bob"`
+ ID UserID `json:"Id" example:"1" validate:"required"`
+ Username string `json:"Username" example:"bob" validate:"required"`
Password string `json:"Password,omitempty" swaggerignore:"true"`
// User role (1 for administrator account and 2 for regular account)
- Role UserRole `json:"Role" example:"1"`
+ Role UserRole `json:"Role" example:"1" validate:"required"`
TokenIssueAt int64 `json:"TokenIssueAt" example:"1"`
ThemeSettings UserThemeSettings `json:"ThemeSettings"`
UseCache bool `json:"UseCache" example:"true"`
@@ -1501,11 +1548,11 @@ type (
// Deprecated fields
// Deprecated
- UserTheme string `json:"UserTheme,omitempty" example:"dark"`
+ UserTheme string `json:"UserTheme,omitempty" example:"dark" swaggerignore:"true"`
// Deprecated in DBVersion == 25
- PortainerAuthorizations Authorizations
+ PortainerAuthorizations Authorizations `swaggerignore:"true"`
// Deprecated in DBVersion == 25
- EndpointAuthorizations EndpointAuthorizations
+ EndpointAuthorizations EndpointAuthorizations `swaggerignore:"true"`
}
// UserAccessPolicies represent the association of an access policy and a user
@@ -1527,7 +1574,7 @@ type (
// UserThemeSettings represents the theme settings for a user
UserThemeSettings struct {
// Color represents the color theme of the UI
- Color string `json:"color" example:"dark" enums:"dark,light,highcontrast,auto"`
+ Color string `json:"color" example:"dark" enums:"dark,light,highcontrast,auto,"`
}
// Webhook represents a url webhook that can be used to update a service
@@ -1548,26 +1595,29 @@ type (
// WebhookType represents the type of resource a webhook is related to
WebhookType int
- // Artifact represents a GitOps artifact produced by a source
+ // Artifact is one entry in a Workflow's artifact list, pairing target IDs with source files
Artifact struct {
- StackID StackID `json:"stackId,omitempty"`
- EdgeStackID EdgeStackID `json:"edgeStackId,omitempty"`
- ReferenceName string `json:"referenceName,omitempty" example:"refs/heads/main"`
- ConfigFilePath string `json:"configFilePath,omitempty" example:"portainer.yaml"`
- ConfigHash string `json:"configHash,omitempty" example:"abc123"`
+ StackID StackID `json:"stackId,omitempty"`
+ EdgeStackID EdgeStackID `json:"edgeStackId,omitempty"`
+ Files []ArtifactFile `json:"files,omitempty"`
+ EnvIDs []EndpointID `json:"envIds,omitempty"`
+ EnvGroups []EndpointGroupID `json:"envGroups,omitempty"`
+ EdgeGroups []EdgeGroupID `json:"edgeGroups,omitempty"`
}
- // ArtifactSources is one entry in a Workflow's ordered artifact-to-sources mapping
- ArtifactSources struct {
- Artifact Artifact `json:"artifact"`
- SourceIDs []SourceID `json:"sourceIds,omitempty"`
+ // ArtifactFile represents one file within an artifact, tied to a specific source and location within it
+ ArtifactFile struct {
+ SourceID SourceID `json:"sourceId"`
+ Path string `json:"path,omitempty" example:"portainer.yaml"`
+ Ref string `json:"ref,omitempty" example:"refs/heads/main"`
+ Hash string `json:"hash,omitempty" example:"abc123"`
}
// Workflow represents a GitOps workflow
Workflow struct {
- ID WorkflowID `json:"id" example:"1"`
- Name string `json:"name,omitempty" example:"my-workflow"`
- Artifacts []ArtifactSources `json:"artifacts,omitempty"`
+ ID WorkflowID `json:"id" example:"1"`
+ Name string `json:"name,omitempty" example:"my-workflow"`
+ Artifacts []Artifact `json:"artifacts,omitempty"`
}
WorkflowID int
@@ -2051,6 +2101,10 @@ const (
CSPEnvVar = "CSP"
// CompactDBEnvVar is the environment variable used to enable/disable the startup compaction of the database
CompactDBEnvVar = "COMPACT_DB"
+ // NoSetupTokenEnvVar is the environment variable used to disable the setup token requirement on an uninitialized instance
+ NoSetupTokenEnvVar = "PORTAINER_NO_SETUP_TOKEN"
+ // SetupTokenEnvVar is the environment variable used to provide a custom setup token for admin initialization and restore on an uninitialized instance
+ SetupTokenEnvVar = "PORTAINER_SETUP_TOKEN"
)
// List of supported features
diff --git a/api/stacks/deployments/deploy.go b/api/stacks/deployments/deploy.go
index 9e54fcc74..76a0b7ed7 100644
--- a/api/stacks/deployments/deploy.go
+++ b/api/stacks/deployments/deploy.go
@@ -121,7 +121,7 @@ func redeployWhenChangedSecondStage(
user *portainer.User,
endpoint *portainer.Endpoint,
) error {
- gitSrc, artifact, err := workflows.GitSourceAndArtifactForStack(datastore, stack.WorkflowID, stack.ID)
+ gitSrc, file, err := workflows.GitSourceAndArtifactForStack(datastore, stack.WorkflowID, stack.ID)
if err != nil {
return errors.WithMessagef(err, "failed to load git config for stack %v", stack.ID)
}
@@ -130,7 +130,7 @@ func redeployWhenChangedSecondStage(
return nil
}
- gitConfig := workflows.MergeSourceAndArtifact(gitSrc, artifact)
+ gitConfig := workflows.MergeSourceAndFile(gitSrc, file)
var gitCommitChangedOrForceUpdate bool
@@ -225,8 +225,8 @@ func redeployWhenChangedSecondStage(
newHash := gitConfig.ConfigHash
- return workflows.UpdateArtifactForStack(tx, stack.WorkflowID, stack.ID, func(a *portainer.Artifact) {
- a.ConfigHash = newHash
+ return workflows.UpdateArtifactFileForStack(tx, stack.WorkflowID, stack.ID, gitSrc.ID, func(a *portainer.ArtifactFile) {
+ a.Hash = newHash
})
}); err != nil {
return errors.WithMessagef(err, "failed to update the stack %v", stack.ID)
diff --git a/api/stacks/deployments/deploy_test.go b/api/stacks/deployments/deploy_test.go
index 023742ce5..e0fda0080 100644
--- a/api/stacks/deployments/deploy_test.go
+++ b/api/stacks/deployments/deploy_test.go
@@ -200,7 +200,7 @@ func Test_redeployWhenChanged_DoesNothingWhenNoGitChanges(t *testing.T) {
src := &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "url",
ReferenceName: "ref",
ConfigHash: "oldHash",
@@ -209,7 +209,7 @@ func Test_redeployWhenChanged_DoesNothingWhenNoGitChanges(t *testing.T) {
err = store.Source().Create(src)
require.NoError(t, err, "failed to create source")
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{SourceIDs: []portainer.SourceID{src.ID}}}}
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: src.ID}}}}}
err = store.Workflow().Create(wf)
require.NoError(t, err, "failed to create workflow")
@@ -247,7 +247,7 @@ func Test_redeployWhenChanged_FailsWhenCannotClone(t *testing.T) {
src := &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "url",
ReferenceName: "ref",
ConfigHash: "oldHash",
@@ -256,9 +256,9 @@ func Test_redeployWhenChanged_FailsWhenCannotClone(t *testing.T) {
err = store.Source().Create(src)
require.NoError(t, err, "failed to create source")
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{StackID: 1},
- SourceIDs: []portainer.SourceID{src.ID},
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{
+ StackID: 1,
+ Files: []portainer.ArtifactFile{{SourceID: src.ID}},
}}}
err = store.Workflow().Create(wf)
require.NoError(t, err, "failed to create workflow")
@@ -290,7 +290,7 @@ func setupRedeployStore(t *testing.T, stackType portainer.StackType) (dataservic
src := &portainer.Source{
Type: portainer.SourceTypeGit,
- GitConfig: &gittypes.RepoConfig{
+ Git: &gittypes.RepoConfig{
URL: "url",
ReferenceName: "ref",
ConfigHash: "oldHash",
@@ -299,7 +299,7 @@ func setupRedeployStore(t *testing.T, stackType portainer.StackType) (dataservic
err = store.Source().Create(src)
require.NoError(t, err, "failed to create source")
- wf := &portainer.Workflow{Artifacts: []portainer.ArtifactSources{{SourceIDs: []portainer.SourceID{src.ID}}}}
+ wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: src.ID}}}}}
err = store.Workflow().Create(wf)
require.NoError(t, err, "failed to create workflow")
diff --git a/api/stacks/deployments/deployer_remote.go b/api/stacks/deployments/deployer_remote.go
index 1360b7675..033932467 100644
--- a/api/stacks/deployments/deployer_remote.go
+++ b/api/stacks/deployments/deployer_remote.go
@@ -169,13 +169,13 @@ func (d *stackDeployer) StopRemoteSwarmStack(ctx context.Context, stack *portain
// * gather deployment logs and bubble them up
func (d *stackDeployer) remoteStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, operation StackRemoteOperation, opts unpackerCmdBuilderOptions) error {
if stack.WorkflowID != 0 && opts.gitConfig == nil {
- src, artifact, err := workflows.GitSourceAndArtifactForStack(d.dataStore, stack.WorkflowID, stack.ID)
+ src, file, err := workflows.GitSourceAndArtifactForStack(d.dataStore, stack.WorkflowID, stack.ID)
if err != nil {
return errors.Wrap(err, "failed to load git config for remote stack")
}
if src != nil {
- opts.gitConfig = workflows.MergeSourceAndArtifact(src, artifact)
+ opts.gitConfig = workflows.MergeSourceAndFile(src, file)
}
}
diff --git a/api/stacks/deployments/deployment_compose_config.go b/api/stacks/deployments/deployment_compose_config.go
index 14f6b000f..a34865c60 100644
--- a/api/stacks/deployments/deployment_compose_config.go
+++ b/api/stacks/deployments/deployment_compose_config.go
@@ -40,9 +40,7 @@ func CreateComposeStackDeploymentConfigTx(tx dataservices.DataStoreTx, securityC
filteredRegistries := security.FilterRegistries(registries, user, securityContext.UserMemberships, endpoint.ID)
- if err := registryutils.ValidateRegistriesECRTokens(tx, filteredRegistries); err != nil {
- return nil, err
- }
+ registryutils.RefreshAndPersistECRTokens(tx, filteredRegistries)
config := &ComposeStackDeploymentConfig{
stack: stack,
@@ -73,6 +71,10 @@ func (config *ComposeStackDeploymentConfig) Deploy(ctx context.Context) error {
}
}
+ if err := stackutils.ValidateComposeURLs(ctx, config.stack, config.FileService); err != nil {
+ return err
+ }
+
if stackutils.IsRelativePathStack(config.stack) {
return config.StackDeployer.DeployRemoteComposeStack(ctx, config.stack, config.endpoint, config.registries, config.prune, config.forcePullImage, config.ForceCreate)
}
diff --git a/api/stacks/deployments/deployment_swarm_config.go b/api/stacks/deployments/deployment_swarm_config.go
index 8f70d0e2a..51d9a3f09 100644
--- a/api/stacks/deployments/deployment_swarm_config.go
+++ b/api/stacks/deployments/deployment_swarm_config.go
@@ -38,9 +38,7 @@ func CreateSwarmStackDeploymentConfigTx(tx dataservices.DataStoreTx, securityCon
filteredRegistries := security.FilterRegistries(registries, user, securityContext.UserMemberships, endpoint.ID)
- if err := registryutils.ValidateRegistriesECRTokens(tx, filteredRegistries); err != nil {
- return nil, err
- }
+ registryutils.RefreshAndPersistECRTokens(tx, filteredRegistries)
config := &SwarmStackDeploymentConfig{
stack: stack,
@@ -73,6 +71,10 @@ func (config *SwarmStackDeploymentConfig) Deploy(ctx context.Context) error {
}
}
+ if err := stackutils.ValidateComposeURLs(ctx, config.stack, config.FileService); err != nil {
+ return err
+ }
+
if stackutils.IsRelativePathStack(config.stack) {
return config.StackDeployer.DeployRemoteSwarmStack(ctx, config.stack, config.endpoint, config.registries, config.prune, config.pullImage)
}
diff --git a/api/stacks/stackbuilders/stack_git_builder.go b/api/stacks/stackbuilders/stack_git_builder.go
index 4e242f8f1..573304012 100644
--- a/api/stacks/stackbuilders/stack_git_builder.go
+++ b/api/stacks/stackbuilders/stack_git_builder.go
@@ -74,9 +74,9 @@ func (b *GitMethodStackBuilder) prepare(ctx context.Context, payload *StackPaylo
repoConfig.URL = gittypes.SanitizeURL(repoConfig.URL)
src, err := workflows.FindOrCreateGitSource(tx, &portainer.Source{
- Name: gittypes.RepoName(repoConfig.URL),
- Type: portainer.SourceTypeGit,
- GitConfig: &repoConfig,
+ Name: gittypes.RepoName(repoConfig.URL),
+ Type: portainer.SourceTypeGit,
+ Git: &repoConfig,
})
if err != nil {
return fmt.Errorf("failed to find or create source: %w", err)
@@ -84,14 +84,14 @@ func (b *GitMethodStackBuilder) prepare(ctx context.Context, payload *StackPaylo
wf := &portainer.Workflow{
Name: b.stack.Name,
- Artifacts: []portainer.ArtifactSources{{
- Artifact: portainer.Artifact{
- ReferenceName: repoConfig.ReferenceName,
- ConfigFilePath: repoConfig.ConfigFilePath,
- ConfigHash: repoConfig.ConfigHash,
- StackID: b.stack.ID,
- },
- SourceIDs: []portainer.SourceID{src.ID},
+ Artifacts: []portainer.Artifact{{
+ StackID: b.stack.ID,
+ Files: []portainer.ArtifactFile{{
+ SourceID: src.ID,
+ Path: repoConfig.ConfigFilePath,
+ Ref: repoConfig.ReferenceName,
+ Hash: repoConfig.ConfigHash,
+ }},
}},
}
if err := tx.Workflow().Create(wf); err != nil {
diff --git a/api/stacks/stackutils/validation.go b/api/stacks/stackutils/validation.go
index 75bb5015d..9f74ac149 100644
--- a/api/stacks/stackutils/validation.go
+++ b/api/stacks/stackutils/validation.go
@@ -2,10 +2,13 @@ package stackutils
import (
"context"
+ "fmt"
"path"
+ "strings"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/filesystem"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
composeloader "github.com/compose-spec/compose-go/v2/loader"
composetypes "github.com/compose-spec/compose-go/v2/types"
@@ -68,6 +71,97 @@ func IsValidStackFile(config StackFileValidationConfig) error {
return nil
}
+// ValidateComposeURLs parses each stack file and checks that every external URL
+// (build contexts and image registry hostnames) is permitted by the active SSRF
+// policy. It is a no-op when SSRF protection is disabled.
+func ValidateComposeURLs(ctx context.Context, stack *portainer.Stack, fileService portainer.FileService) error {
+ if !ssrf.IsEnabled() {
+ return nil
+ }
+
+ env := BuildEnvMap(stack)
+ workingDir := filesystem.JoinPaths(stack.ProjectPath, path.Dir(stack.EntryPoint))
+
+ for _, file := range GetStackFilePaths(stack, false) {
+ stackContent, err := fileService.GetFileContent(stack.ProjectPath, file)
+ if err != nil {
+ return errors.Wrap(err, "failed to get stack file content")
+ }
+
+ if err := checkComposeFileURLs(ctx, stackContent, env, workingDir); err != nil {
+ return errors.Wrap(err, "stack file contains a URL blocked by the SSRF policy")
+ }
+ }
+
+ return nil
+}
+
+// ValidateEdgeStackComposeContent checks that every external URL in an edge
+// stack's Compose file is permitted by the active SSRF policy. It is a no-op
+// when SSRF protection is disabled or the deployment type is not compose.
+func ValidateEdgeStackComposeContent(ctx context.Context, deploymentType portainer.EdgeStackDeploymentType, content []byte) error {
+ if !ssrf.IsEnabled() || deploymentType != portainer.EdgeStackDeploymentCompose {
+ return nil
+ }
+
+ if err := checkComposeFileURLs(ctx, content, nil, ""); err != nil {
+ return errors.Wrap(err, "stack file contains a URL blocked by the SSRF policy")
+ }
+
+ return nil
+}
+
+func checkComposeFileURLs(ctx context.Context, content []byte, env map[string]string, workingDir string) error {
+ composeConfigDetails := composetypes.ConfigDetails{
+ ConfigFiles: []composetypes.ConfigFile{{Content: content}},
+ Environment: env,
+ WorkingDir: workingDir,
+ }
+
+ composeConfig, err := composeloader.LoadWithContext(ctx, composeConfigDetails, composeloader.WithSkipValidation)
+ if err != nil {
+ return err
+ }
+
+ for _, service := range composeConfig.Services {
+ if service.Build != nil {
+ buildCtx := service.Build.Context
+ if strings.HasPrefix(buildCtx, "http://") || strings.HasPrefix(buildCtx, "https://") {
+ if err := ssrf.CheckURL(ctx, buildCtx); err != nil {
+ return fmt.Errorf("service %q: build context URL blocked: %w", service.Name, err)
+ }
+ }
+ }
+
+ if service.Image != "" {
+ if registry := extractImageRegistry(service.Image); registry != "" {
+ if err := ssrf.CheckURL(ctx, "https://"+registry); err != nil {
+ return fmt.Errorf("service %q: image registry %q blocked: %w", service.Name, registry, err)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+// extractImageRegistry returns the registry hostname from an OCI image reference,
+// or an empty string if the image resolves to Docker Hub (no explicit registry).
+func extractImageRegistry(imageRef string) string {
+ ref, _, _ := strings.Cut(imageRef, "@")
+
+ first, _, hasSlash := strings.Cut(ref, "/")
+ if !hasSlash {
+ return ""
+ }
+
+ if strings.ContainsAny(first, ".:") || first == "localhost" {
+ return first
+ }
+
+ return ""
+}
+
func ValidateStackFiles(stack *portainer.Stack, securitySettings *portainer.EndpointSecuritySettings, fileService portainer.FileService) error {
env := BuildEnvMap(stack)
workingDir := filesystem.JoinPaths(stack.ProjectPath, path.Dir(stack.EntryPoint))
diff --git a/api/stacks/stackutils/validation_test.go b/api/stacks/stackutils/validation_test.go
index 2aca9357b..893f3925b 100644
--- a/api/stacks/stackutils/validation_test.go
+++ b/api/stacks/stackutils/validation_test.go
@@ -6,6 +6,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/filesystem"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/stretchr/testify/require"
)
@@ -271,3 +272,170 @@ services:
err := ValidateStackFiles(stack, securitySettings, fileService)
require.ErrorContains(t, err, "bind-mount disabled for non administrator users")
}
+
+func TestExtractImageRegistry(t *testing.T) {
+ t.Parallel()
+
+ cases := []struct {
+ image string
+ expected string
+ }{
+ {"nginx", ""},
+ {"nginx:latest", ""},
+ {"library/nginx", ""},
+ {"ghcr.io/owner/image:tag", "ghcr.io"},
+ {"myregistry.com/image:tag", "myregistry.com"},
+ {"myregistry.com:5000/image:tag", "myregistry.com:5000"},
+ {"localhost/image:tag", "localhost"},
+ {"localhost:5000/image:tag", "localhost:5000"},
+ {"myregistry.com/image@sha256:abc", "myregistry.com"},
+ {"169.254.169.254/image:tag", "169.254.169.254"},
+ }
+
+ for _, tc := range cases {
+ got := extractImageRegistry(tc.image)
+ require.Equal(t, tc.expected, got, "image: %s", tc.image)
+ }
+}
+
+func TestValidateComposeURLs_DisabledSSRF(t *testing.T) {
+ ssrf.Configure(ssrf.Policy{})
+
+ stack := &portainer.Stack{
+ ProjectPath: "/tmp/stack/1",
+ EntryPoint: "docker-compose.yml",
+ }
+
+ fileService := mockFileService{
+ fileContent: []byte(`
+version: "3"
+services:
+ web:
+ build:
+ context: http://169.254.169.254/repo.tar.gz
+`),
+ projectVersionPath: "/tmp/stack/1",
+ }
+
+ err := ValidateComposeURLs(t.Context(), stack, fileService)
+ require.NoError(t, err)
+}
+
+func TestValidateComposeURLs_BuildContextBlocked(t *testing.T) {
+ ssrf.Configure(ssrf.Policy{Mode: ssrf.ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { ssrf.Configure(ssrf.Policy{}) })
+
+ stack := &portainer.Stack{
+ ProjectPath: "/tmp/stack/1",
+ EntryPoint: "docker-compose.yml",
+ }
+
+ fileService := mockFileService{
+ fileContent: []byte(`
+version: "3"
+services:
+ web:
+ build:
+ context: http://169.254.169.254/repo.tar.gz
+ image: nginx
+`),
+ projectVersionPath: "/tmp/stack/1",
+ }
+
+ err := ValidateComposeURLs(t.Context(), stack, fileService)
+ require.ErrorContains(t, err, "SSRF policy")
+}
+
+func TestValidateComposeURLs_BuildContextPath(t *testing.T) {
+ ssrf.Configure(ssrf.Policy{Mode: ssrf.ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { ssrf.Configure(ssrf.Policy{}) })
+
+ stack := &portainer.Stack{
+ ProjectPath: "/tmp/stack/1",
+ EntryPoint: "docker-compose.yml",
+ }
+
+ fileService := mockFileService{
+ fileContent: []byte(`
+version: "3"
+services:
+ web:
+ build:
+ context: ./app
+ image: nginx
+`),
+ projectVersionPath: "/tmp/stack/1",
+ }
+
+ err := ValidateComposeURLs(t.Context(), stack, fileService)
+ require.NoError(t, err)
+}
+
+func TestValidateComposeURLs_ImageRegistryBlocked(t *testing.T) {
+ ssrf.Configure(ssrf.Policy{Mode: ssrf.ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { ssrf.Configure(ssrf.Policy{}) })
+
+ stack := &portainer.Stack{
+ ProjectPath: "/tmp/stack/1",
+ EntryPoint: "docker-compose.yml",
+ }
+
+ fileService := mockFileService{
+ fileContent: []byte(`
+version: "3"
+services:
+ web:
+ image: 169.254.169.254/myimage:latest
+`),
+ projectVersionPath: "/tmp/stack/1",
+ }
+
+ err := ValidateComposeURLs(t.Context(), stack, fileService)
+ require.ErrorContains(t, err, "SSRF policy")
+}
+
+func TestValidateComposeURLs_ImageRegistryAllowed(t *testing.T) {
+ ssrf.Configure(ssrf.Policy{Mode: ssrf.ModeEnforce, AllowedHosts: []string{"myregistry.com"}})
+ t.Cleanup(func() { ssrf.Configure(ssrf.Policy{}) })
+
+ stack := &portainer.Stack{
+ ProjectPath: "/tmp/stack/1",
+ EntryPoint: "docker-compose.yml",
+ }
+
+ fileService := mockFileService{
+ fileContent: []byte(`
+version: "3"
+services:
+ web:
+ image: myregistry.com/myimage:latest
+`),
+ projectVersionPath: "/tmp/stack/1",
+ }
+
+ err := ValidateComposeURLs(t.Context(), stack, fileService)
+ require.NoError(t, err)
+}
+
+func TestValidateComposeURLs_DockerHubImageAllowed(t *testing.T) {
+ ssrf.Configure(ssrf.Policy{Mode: ssrf.ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { ssrf.Configure(ssrf.Policy{}) })
+
+ stack := &portainer.Stack{
+ ProjectPath: "/tmp/stack/1",
+ EntryPoint: "docker-compose.yml",
+ }
+
+ fileService := mockFileService{
+ fileContent: []byte(`
+version: "3"
+services:
+ web:
+ image: nginx:latest
+`),
+ projectVersionPath: "/tmp/stack/1",
+ }
+
+ err := ValidateComposeURLs(t.Context(), stack, fileService)
+ require.NoError(t, err)
+}
diff --git a/app/portainer/models/settings.js b/app/portainer/models/settings.js
index 6a65df7ef..6795e149e 100644
--- a/app/portainer/models/settings.js
+++ b/app/portainer/models/settings.js
@@ -32,6 +32,7 @@ export function PublicSettingsViewModel(settings) {
this.Features = settings.Features;
this.Edge = new EdgeSettingsViewModel(settings.Edge);
this.DefaultRegistry = settings.DefaultRegistry;
+ this.RequiresSetupToken = settings.RequiresSetupToken;
}
export function InternalAuthSettingsViewModel(data) {
diff --git a/app/portainer/services/api/backupService.js b/app/portainer/services/api/backupService.js
index ca1637ebf..026328ac5 100644
--- a/app/portainer/services/api/backupService.js
+++ b/app/portainer/services/api/backupService.js
@@ -11,8 +11,8 @@ angular.module('portainer.app').factory('BackupService', [
return Backup.download({}, payload).$promise;
};
- service.uploadBackup = function (file, password) {
- return FileUploadService.uploadBackup(file, password);
+ service.uploadBackup = function (file, password, setupToken) {
+ return FileUploadService.uploadBackup(file, password, setupToken);
};
service.getS3Settings = function () {
diff --git a/app/portainer/services/api/userService.js b/app/portainer/services/api/userService.js
index 1ee22a7b5..2d1102f8a 100644
--- a/app/portainer/services/api/userService.js
+++ b/app/portainer/services/api/userService.js
@@ -2,6 +2,7 @@ import _ from 'lodash-es';
import { UserViewModel } from '@/portainer/models/user';
import { getUsers } from '@/portainer/users/user.service';
import { getUser } from '@/portainer/users/queries/useUser';
+import { userAdminInit } from '@api/sdk.gen';
import { TeamMembershipModel } from '../../models/teamMembership';
@@ -84,8 +85,11 @@ export function UserService($q, Users, TeamService) {
return deferred.promise;
};
- service.initAdministrator = function (username, password) {
- return Users.initAdminUser({ Username: username, Password: password }).$promise;
+ service.initAdministrator = function (username, password, setupToken) {
+ return userAdminInit({
+ body: { Username: username, Password: password },
+ ...(setupToken && { headers: { 'X-Setup-Token': setupToken } }),
+ });
};
service.administratorExists = function () {
diff --git a/app/portainer/services/fileUpload.js b/app/portainer/services/fileUpload.js
index 877cdf687..c0f9e824d 100644
--- a/app/portainer/services/fileUpload.js
+++ b/app/portainer/services/fileUpload.js
@@ -38,13 +38,11 @@ function FileUploadFactory($q, Upload) {
});
};
- service.uploadBackup = function (file, password) {
+ service.uploadBackup = function (file, password, setupToken) {
return Upload.upload({
url: 'api/restore',
- data: {
- file,
- password,
- },
+ data: { file, password },
+ headers: setupToken ? { 'X-Setup-Token': setupToken } : {},
});
};
diff --git a/app/portainer/views/init/admin/initAdmin.html b/app/portainer/views/init/admin/initAdmin.html
index 4733ca216..e05f9d33b 100644
--- a/app/portainer/views/init/admin/initAdmin.html
+++ b/app/portainer/views/init/admin/initAdmin.html
@@ -65,6 +65,17 @@
+
+
+
+
+
+
diff --git a/app/react/portainer/environments/ItemView/GeneralEnvironmentForm/GeneralEnvironmentForm.test.tsx b/app/react/portainer/environments/ItemView/GeneralEnvironmentForm/GeneralEnvironmentForm.test.tsx
index c15cae867..e1f28829e 100644
--- a/app/react/portainer/environments/ItemView/GeneralEnvironmentForm/GeneralEnvironmentForm.test.tsx
+++ b/app/react/portainer/environments/ItemView/GeneralEnvironmentForm/GeneralEnvironmentForm.test.tsx
@@ -113,7 +113,7 @@ describe('GeneralEnvironmentForm', () => {
setTimeout(resolve, 100);
});
requestPayload = await request.json();
- return HttpResponse.json({});
+ return HttpResponse.json(createMockEnvironment());
})
);
@@ -354,7 +354,7 @@ describe('GeneralEnvironmentForm', () => {
setTimeout(resolve, 100);
});
requestPayload = await request.json();
- return HttpResponse.json({});
+ return HttpResponse.json(createMockEnvironment());
})
);
diff --git a/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.test.tsx b/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.test.tsx
index c1aa4c0cc..e9a9d4e5f 100644
--- a/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.test.tsx
+++ b/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.test.tsx
@@ -253,8 +253,11 @@ describe('EditGroupView', () => {
// Wait for form to load
await screen.findByLabelText(/Name/i);
- // The Environments tab should be visible
- expect(screen.getByText('Environments')).toBeVisible();
+ // The Environments tab should be visible — target by data-cy so we
+ // don't collide with the "Environments" stat block label in the header.
+ const environmentsTab = screen.getByTestId('tab-0');
+ expect(environmentsTab).toBeVisible();
+ expect(environmentsTab).toHaveTextContent(/Environments/i);
});
});
@@ -492,6 +495,56 @@ describe('EditGroupView', () => {
});
});
+ describe('API request - size parameter', () => {
+ it('should request the group with size=true so TypeInfo is populated', async () => {
+ let capturedUrl: string | undefined;
+
+ server.use(
+ http.get('/api/endpoint_groups/2', ({ request }) => {
+ capturedUrl = request.url;
+ return HttpResponse.json(mockGroup);
+ }),
+ http.get('/api/tags', () => HttpResponse.json([])),
+ http.get('/api/endpoints', () =>
+ HttpResponse.json([], {
+ headers: {
+ 'x-total-count': '0',
+ 'x-total-available': '0',
+ },
+ })
+ )
+ );
+
+ const Wrapped = withTestQueryProvider(
+ withTestRouter(withUserProvider(EditGroupView))
+ );
+ render();
+
+ await screen.findByLabelText(/Name/i);
+
+ // size=true is required for TypeInfo (Docker/Kubernetes/Podman counts) to be
+ // included in the response. Without it, EnvironmentTypeBreakdown in GroupHeader
+ // shows nothing.
+ expect(new URL(capturedUrl!).searchParams.get('size')).toBe('true');
+ });
+
+ it('should show per-platform breakdown in GroupHeader when TypeInfo is returned', async () => {
+ const groupWithTypeInfo = createMockEnvironmentGroup({
+ Id: 2,
+ Name: 'Test Group',
+ Description: 'Test description',
+ TagIds: [1],
+ Total: 3,
+ TypeInfo: { Docker: 2, Kubernetes: 1, Podman: 0, Mixed: true },
+ });
+
+ renderEditGroupView({ groupData: groupWithTypeInfo });
+
+ expect(await screen.findByText('Docker')).toBeVisible();
+ expect(screen.getByText('Kubernetes')).toBeVisible();
+ });
+ });
+
describe('Delete flow', () => {
it('should render the delete button', async () => {
renderEditGroupView();
diff --git a/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.tsx b/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.tsx
index f285bc0b2..24fba3ce1 100644
--- a/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.tsx
+++ b/app/react/portainer/environments/environment-groups/ItemView/EditGroupView.tsx
@@ -23,7 +23,8 @@ export function EditGroupView() {
const deleteMutation = useDeleteEnvironmentGroupMutation();
const [addEnvsDrawerOpen, setAddEnvsDrawerOpen] = useState(false);
const groupQuery = useGroup(
- deleteMutation.isLoading || deleteMutation.isSuccess ? undefined : groupId
+ deleteMutation.isLoading || deleteMutation.isSuccess ? undefined : groupId,
+ { size: true }
);
const group = groupQuery.data;
const groupName = group?.Name ?? 'Environment group';
diff --git a/app/react/portainer/environments/environment-groups/ItemView/GroupHeader.tsx b/app/react/portainer/environments/environment-groups/ItemView/GroupHeader.tsx
index 4be84b12f..df6a1229c 100644
--- a/app/react/portainer/environments/environment-groups/ItemView/GroupHeader.tsx
+++ b/app/react/portainer/environments/environment-groups/ItemView/GroupHeader.tsx
@@ -1,16 +1,18 @@
-import { Layers, Plus, RefreshCw, Trash2 } from 'lucide-react';
+import { LayoutGrid, Plus, RefreshCw, Trash2 } from 'lucide-react';
-import { useTags } from '@/portainer/tags/queries';
-
-import { Button } from '@@/buttons';
import { ResourceDetailHeader } from '@@/ResourceDetailHeader/ResourceDetailHeader';
-import { Badge } from '@@/Badge';
+import { ResourceStatBlock } from '@@/ResourceDetailHeader/ResourceStatBlock';
+import { ActionBarShell } from '@@/ResourceDetailHeader/ActionBarShell';
+import { ActionBarButton } from '@@/ResourceDetailHeader/ActionBarButton';
+import { HeaderStats } from '@@/ResourceDetailHeader/HeaderStats';
import { EnvironmentGroup } from '../types';
+import { PlatformBadge } from '../components/PlatformBadge';
+import { EnvironmentTypeBreakdown } from '../components/EnvironmentTypeBreakdown';
interface Props {
group?: EnvironmentGroup;
- isLoading: boolean;
+ isLoading?: boolean;
onRefresh?: () => void;
onAddEnvironments?: () => void;
onDelete?: () => void;
@@ -23,50 +25,34 @@ export function GroupHeader({
onAddEnvironments,
onDelete,
}: Props) {
- const tagsQuery = useTags();
-
- const tagBadges = group?.TagIds?.length
- ? group.TagIds.map((tagId) => {
- const tag = tagsQuery.data?.find((t) => t.ID === tagId);
- return (
-
- {tag?.Name ?? `Tag ${tagId}`}
-
- );
- })
- : undefined;
-
const actionBar = group ? (
- <>
+
-
-
+
-
+
- >
+
) : undefined;
return (
@@ -75,13 +61,25 @@ export function GroupHeader({
errorMessage={
!isLoading && !group ? 'Failed to load group details' : undefined
}
- icon={}
- iconBackgroundClassName="bg-blue-3 th-dark:bg-blue-9"
+ icon={
+
+ }
subtitleLabel="Environment Group"
- subtitleClassName="text-blue-9 th-dark:text-blue-5"
title={group?.Name || ''}
- badge={tagBadges ? <>{tagBadges}> : undefined}
+ badge={group && }
description={group?.Description}
+ rightInfo={
+ group && (
+
+
+ Environments
+
+
+
+
+
+ )
+ }
actionBar={actionBar}
/>
);
diff --git a/app/react/portainer/environments/environment-groups/ItemView/tabs/EnvironmentsTab.tsx b/app/react/portainer/environments/environment-groups/ItemView/tabs/EnvironmentsTab.tsx
index d57e90436..804849601 100644
--- a/app/react/portainer/environments/environment-groups/ItemView/tabs/EnvironmentsTab.tsx
+++ b/app/react/portainer/environments/environment-groups/ItemView/tabs/EnvironmentsTab.tsx
@@ -44,7 +44,7 @@ export function EnvironmentsTab({
return (
<>
-
+
{groupQuery.data && (
diff --git a/app/react/portainer/environments/environment-groups/components/PlatformBadge.tsx b/app/react/portainer/environments/environment-groups/components/PlatformBadge.tsx
index dee14ef3a..6e25027db 100644
--- a/app/react/portainer/environments/environment-groups/components/PlatformBadge.tsx
+++ b/app/react/portainer/environments/environment-groups/components/PlatformBadge.tsx
@@ -2,11 +2,11 @@ import { EnvironmentGroup } from '../types';
import { getPlatformLabel } from '../utils/getPlatformLabel';
const colorByLabel: Record = {
- Docker: 'bg-green-2',
- Kubernetes: 'bg-blue-2',
- Podman: 'bg-orange-2',
- Mixed: 'bg-purple-2',
- Empty: 'bg-gray-2',
+ Docker: 'bg-green-2 th-dark:bg-green-9',
+ Kubernetes: 'bg-blue-2 th-dark:bg-blue-9',
+ Podman: 'bg-orange-2 th-dark:bg-orange-9',
+ Mixed: 'bg-purple-2 th-dark:bg-purple-9',
+ Empty: 'bg-gray-2 th-dark:bg-gray-8',
};
interface Props {
@@ -15,11 +15,12 @@ interface Props {
export function PlatformBadge({ group }: Props) {
const platformLabel = getPlatformLabel(group);
- const colorClass = colorByLabel[platformLabel] ?? 'bg-gray-2';
+ const colorClass =
+ colorByLabel[platformLabel] ?? 'bg-gray-2 th-dark:bg-gray-8';
return (
{platformLabel}
diff --git a/app/react/portainer/environments/environment-groups/queries/query-keys.ts b/app/react/portainer/environments/environment-groups/queries/query-keys.ts
index e1cc6b98e..bcea887f4 100644
--- a/app/react/portainer/environments/environment-groups/queries/query-keys.ts
+++ b/app/react/portainer/environments/environment-groups/queries/query-keys.ts
@@ -3,5 +3,6 @@ import { EnvironmentGroupId } from '../../types';
export const queryKeys = {
base: () => ['environment-groups'] as const,
list: (size = false) => [...queryKeys.base(), { size }] as const,
- group: (id?: EnvironmentGroupId) => [...queryKeys.base(), id] as const,
+ group: (id?: EnvironmentGroupId, size = false) =>
+ [...queryKeys.base(), id, { size }] as const,
};
diff --git a/app/react/portainer/environments/environment-groups/queries/useGroup.ts b/app/react/portainer/environments/environment-groups/queries/useGroup.ts
index f15c8d3d6..e382be05a 100644
--- a/app/react/portainer/environments/environment-groups/queries/useGroup.ts
+++ b/app/react/portainer/environments/environment-groups/queries/useGroup.ts
@@ -13,18 +13,23 @@ export function useGroup(
{
select,
enabled = true,
+ size = false,
}: {
select?: (group: PortainerEndpointGroup | null) => T;
enabled?: boolean;
+ size?: boolean;
} = {}
) {
return useQuery(
- queryKeys.group(groupId),
+ queryKeys.group(groupId, size),
async () => {
if (groupId === undefined) {
return null;
}
- const { data } = await getEndpointGroupsById({ path: { id: groupId } });
+ const { data } = await getEndpointGroupsById({
+ path: { id: groupId },
+ query: { size },
+ });
return data;
},
diff --git a/app/react/portainer/environments/environment.service/index.ts b/app/react/portainer/environments/environment.service/index.ts
index a23c53a72..b353d4aa7 100644
--- a/app/react/portainer/environments/environment.service/index.ts
+++ b/app/react/portainer/environments/environment.service/index.ts
@@ -1,4 +1,5 @@
import { endpointList } from '@api/sdk.gen';
+import { PortainerEndpoint } from '@api/types.gen';
import axios, { parseAxiosError } from '@/portainer/services/axios/axios';
import {
@@ -170,10 +171,13 @@ export async function getAgentVersions() {
export async function getEndpoint(id: EnvironmentId, excludeSnapshot = true) {
try {
- const { data: endpoint } = await axios.get(buildUrl(id), {
- params: { excludeSnapshot },
- });
- return endpoint;
+ const { data: endpoint } = await axios.get(
+ buildUrl(id),
+ {
+ params: { excludeSnapshot },
+ }
+ );
+ return toEnvironment(endpoint);
} catch (e) {
throw parseAxiosError(e as Error);
}
diff --git a/app/react/portainer/environments/queries/useUpdateEnvironmentMutation.ts b/app/react/portainer/environments/queries/useUpdateEnvironmentMutation.ts
index 4c9c86acd..d6a361010 100644
--- a/app/react/portainer/environments/queries/useUpdateEnvironmentMutation.ts
+++ b/app/react/portainer/environments/queries/useUpdateEnvironmentMutation.ts
@@ -1,17 +1,19 @@
import { useQueryClient, useMutation } from '@tanstack/react-query';
-import { EndpointsEndpointUpdatePayload } from '@api/types.gen';
+import {
+ EndpointsEndpointUpdatePayload,
+ PortainerEndpoint,
+} from '@api/types.gen';
import { withError, withInvalidate } from '@/react-tools/react-query';
import {
EnvironmentId,
- Environment,
EnvironmentStatusMessage,
KubernetesSettings,
} from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios/axios';
-import { buildUrl } from '../environment.service/utils';
+import { buildUrl, toEnvironment } from '../environment.service/utils';
import { environmentQueryKeys } from './query-keys';
@@ -51,12 +53,9 @@ export async function updateEnvironment({
payload.TLSKey
);
- const { data: endpoint } = await axios.put(
- buildUrl(id),
- payload
- );
+ const { data } = await axios.put(buildUrl(id), payload);
- return endpoint;
+ return toEnvironment(data);
} catch (e) {
throw parseAxiosError(e as Error, 'Unable to update environment');
}
diff --git a/app/react/portainer/generated-api/portainer/sdk.gen.ts b/app/react/portainer/generated-api/portainer/sdk.gen.ts
index 158a830c1..bfb2d06c9 100644
--- a/app/react/portainer/generated-api/portainer/sdk.gen.ts
+++ b/app/react/portainer/generated-api/portainer/sdk.gen.ts
@@ -1008,6 +1008,7 @@ import {
zGetApplicationsResourcesQuery,
zGetApplicationsResourcesResponse,
zGetEndpointGroupsByIdPath,
+ zGetEndpointGroupsByIdQuery,
zGetEndpointGroupsByIdResponse,
zGetKubernetesConfigMapPath,
zGetKubernetesConfigMapResponse,
@@ -1189,6 +1190,7 @@ import {
zRestartKubernetesPodPath,
zRestartKubernetesPodResponse,
zRestoreBody,
+ zRestoreHeaders,
zRoleListResponse,
zSetDefaultKubernetesStorageClassPath,
zSetDefaultKubernetesStorageClassResponse,
@@ -1326,6 +1328,7 @@ import {
zUploadTlsResponse,
zUserAdminCheckResponse,
zUserAdminInitBody,
+ zUserAdminInitHeaders,
zUserAdminInitResponse,
zUserCreateBody,
zUserCreateResponse,
@@ -2859,7 +2862,7 @@ export const getEndpointGroupsById = (
.object({
body: z.never().optional(),
path: zGetEndpointGroupsByIdPath,
- query: z.never().optional(),
+ query: zGetEndpointGroupsByIdQuery.optional(),
})
.parseAsync(data),
responseType: 'json',
@@ -3875,7 +3878,7 @@ export const gitOpsSourcesList = (
/**
* Delete a source
*
- * Deletes an existing GitOps source. Returns 409 if the source is referenced by any workflow.
+ * Deletes an existing GitOps source. Returns 409 if the source is referenced by any workflow or custom template.
* **Access policy**: admin
*/
export const gitOpsSourcesDelete = (
@@ -7524,7 +7527,7 @@ export const resourceControlUpdate = (
* Triggers a system restore using provided backup file
*
* Triggers a system restore using provided backup file
- * **Access policy**: public
+ * **Access policy**: public (requires the X-Setup-Token header on an uninitialized instance unless --no-setup-token is set)
*/
export const restore = (
options: Options
@@ -7538,6 +7541,7 @@ export const restore = (
await z
.object({
body: zRestoreBody,
+ headers: zRestoreHeaders.optional(),
path: z.never().optional(),
query: z.never().optional(),
})
@@ -8096,9 +8100,9 @@ export const stackStart = (
});
/**
- * Stops a stopped Stack
+ * Stop a running Stack
*
- * Stops a stopped Stack.
+ * Stop a running Stack.
* **Access policy**: authenticated
*/
export const stackStop = (
@@ -9811,7 +9815,7 @@ export const userAdminCheck = (
* Initialize administrator account
*
* Initialize the 'admin' user account.
- * **Access policy**: public
+ * **Access policy**: public (requires the X-Setup-Token header on an uninitialized instance unless --no-setup-token is set)
*/
export const userAdminInit = (
options: Options
@@ -9825,6 +9829,7 @@ export const userAdminInit = (
await z
.object({
body: zUserAdminInitBody,
+ headers: zUserAdminInitHeaders.optional(),
path: z.never().optional(),
query: z.never().optional(),
})
diff --git a/app/react/portainer/generated-api/portainer/types.gen.ts b/app/react/portainer/generated-api/portainer/types.gen.ts
index e913b3a61..b01b2fceb 100644
--- a/app/react/portainer/generated-api/portainer/types.gen.ts
+++ b/app/react/portainer/generated-api/portainer/types.gen.ts
@@ -263,18 +263,6 @@ export type V1ListMeta = {
selfLink?: string;
};
-export type V1Duration = {
- 'time.Duration'?:
- | -9223372036854776000
- | 9223372036854776000
- | 1
- | 1000
- | 1000000
- | 1000000000
- | 60000000000
- | 3600000000000;
-};
-
export type V1OwnerReference = {
/**
* API version of the referent.
@@ -615,7 +603,7 @@ export type V1Beta1PodMetrics = {
* collected from the interval [Timestamp-Window, Timestamp].
*/
timestamp?: string;
- window?: V1Duration;
+ window?: string;
};
export type V1Beta1NodeMetricsList = {
@@ -680,7 +668,7 @@ export type V1Beta1NodeMetrics = {
* The memory usage is the memory working set.
*/
usage?: V1ResourceList;
- window?: V1Duration;
+ window?: string;
};
export type V1WindowsSecurityContextOptions = {
@@ -4567,6 +4555,10 @@ export type SettingsPublicSettingsResponse = {
* The minimum required length for a password of any user when using internal auth mode
*/
RequiredPasswordLength?: number;
+ /**
+ * Whether the setup wizard must send the X-Setup-Token header for admin init / restore
+ */
+ RequiresSetupToken?: boolean;
/**
* Whether team sync is enabled
*/
@@ -5320,7 +5312,7 @@ export type PortainerUserThemeSettings = {
/**
* Color represents the color theme of the UI
*/
- color?: 'dark' | 'light' | 'highcontrast' | 'auto';
+ color?: 'dark' | 'light' | 'highcontrast' | 'auto' | '';
};
export const PortainerUserRole = {
@@ -5360,38 +5352,18 @@ export type PortainerResourceAccessLevel =
(typeof PortainerResourceAccessLevel)[keyof typeof PortainerResourceAccessLevel];
export type PortainerUser = {
- /**
- * Deprecated in DBVersion == 25
- */
- EndpointAuthorizations?: PortainerEndpointAuthorizations;
/**
* User Identifier
*/
- Id?: number;
- /**
- * Deprecated in DBVersion == 25
- */
- PortainerAuthorizations?: PortainerAuthorizations;
+ Id: number;
/**
* User role (1 for administrator account and 2 for regular account)
*/
- Role?: PortainerUserRole;
+ Role: PortainerUserRole;
ThemeSettings?: PortainerUserThemeSettings;
TokenIssueAt?: number;
UseCache?: boolean;
- /**
- * Deprecated
- */
- UserTheme?: string;
- Username?: string;
-};
-
-export type PortainerAuthorizations = {
- [key: string]: boolean;
-};
-
-export type PortainerEndpointAuthorizations = {
- [key: string]: PortainerAuthorizations;
+ Username: string;
};
export type PortainerTeamResourceAccess = {
@@ -6020,6 +5992,10 @@ export type PortainerRole = {
Priority?: number;
};
+export type PortainerAuthorizations = {
+ [key: string]: boolean;
+};
+
export type PortainerPerformanceMetrics = {
CPUUsage?: number;
DiskUsage?: number;
@@ -6638,6 +6614,7 @@ export type PortainerCustomTemplatePlatform =
(typeof PortainerCustomTemplatePlatform)[keyof typeof PortainerCustomTemplatePlatform];
export type PortainerCustomTemplate = {
+ ArtifactSources?: PortainerArtifactSources;
/**
* User identifier who created this template
*/
@@ -6695,6 +6672,19 @@ export type PortainerCustomTemplate = {
Variables?: Array;
};
+export type PortainerArtifact = {
+ configFilePath?: string;
+ configHash?: string;
+ edgeStackId?: number;
+ referenceName?: string;
+ stackId?: number;
+};
+
+export type PortainerArtifactSources = {
+ artifact?: PortainerArtifact;
+ sourceIds?: Array;
+};
+
export type MotdMotd = {
ContentLayout?: {
[key: string]: string;
@@ -9733,7 +9723,12 @@ export type GetEndpointGroupsByIdData = {
*/
id: number;
};
- query?: never;
+ query?: {
+ /**
+ * If true, include the number of environments and breakdown by type
+ */
+ size?: boolean;
+ };
url: '/endpoint_groups/{id}';
};
@@ -9756,7 +9751,7 @@ export type GetEndpointGroupsByIdResponses = {
/**
* Success
*/
- 200: PortainerEndpointGroup;
+ 200: EndpointgroupsEndpointGroupResponse;
};
export type GetEndpointGroupsByIdResponse =
@@ -11201,7 +11196,7 @@ export type GitOpsSourcesDeleteErrors = {
*/
404: unknown;
/**
- * Source is in use by one or more workflows
+ * Source is in use by one or more workflows or custom templates
*/
409: unknown;
/**
@@ -15837,6 +15832,12 @@ export type RestoreData = {
* Restore request payload
*/
body: BackupRestorePayload;
+ headers?: {
+ /**
+ * Setup token (required when instance is uninitialized and --no-setup-token is not set)
+ */
+ 'X-Setup-Token'?: string;
+ };
path?: never;
query?: never;
url: '/restore';
@@ -15847,6 +15848,10 @@ export type RestoreErrors = {
* Invalid request
*/
400: unknown;
+ /**
+ * Access denied - invalid or missing setup token
+ */
+ 403: unknown;
/**
* Server error
*/
@@ -18374,6 +18379,12 @@ export type UserAdminInitData = {
* User details
*/
body: UsersAdminInitPayload;
+ headers?: {
+ /**
+ * Setup token (required when instance is uninitialized and --no-setup-token is not set)
+ */
+ 'X-Setup-Token'?: string;
+ };
path?: never;
query?: never;
url: '/users/admin/init';
@@ -18384,6 +18395,10 @@ export type UserAdminInitErrors = {
* Invalid request
*/
400: unknown;
+ /**
+ * Access denied - invalid or missing setup token
+ */
+ 403: unknown;
/**
* Admin user already initialized
*/
diff --git a/app/react/portainer/generated-api/portainer/zod.gen.ts b/app/react/portainer/generated-api/portainer/zod.gen.ts
index b5435152a..e18d9625b 100644
--- a/app/react/portainer/generated-api/portainer/zod.gen.ts
+++ b/app/react/portainer/generated-api/portainer/zod.gen.ts
@@ -151,21 +151,6 @@ export const zV1ListMeta = z.object({
selfLink: z.string().optional(),
});
-export const zV1Duration = z.object({
- 'time.Duration': z
- .union([
- z.literal(-9223372036854776000),
- z.literal(9223372036854776000),
- z.literal(1),
- z.literal(1000),
- z.literal(1000000),
- z.literal(1000000000),
- z.literal(60000000000),
- z.literal(3600000000000),
- ])
- .optional(),
-});
-
export const zV1OwnerReference = z.object({
apiVersion: z.string().optional(),
blockOwnerDeletion: z.boolean().optional(),
@@ -226,7 +211,7 @@ export const zV1Beta1PodMetrics = z.object({
kind: z.string().optional(),
metadata: zV1ObjectMeta.optional(),
timestamp: z.string().optional(),
- window: zV1Duration.optional(),
+ window: z.string().optional(),
});
export const zV1Beta1PodMetricsList = z.object({
@@ -242,7 +227,7 @@ export const zV1Beta1NodeMetrics = z.object({
metadata: zV1ObjectMeta.optional(),
timestamp: z.string().optional(),
usage: zV1ResourceList.optional(),
- window: zV1Duration.optional(),
+ window: z.string().optional(),
});
export const zV1Beta1NodeMetricsList = z.object({
@@ -1327,6 +1312,7 @@ export const zSettingsPublicSettingsResponse = z.object({
OAuthLoginURI: z.string().optional(),
OAuthLogoutURI: z.string().optional(),
RequiredPasswordLength: z.int().optional(),
+ RequiresSetupToken: z.boolean().optional(),
TeamSync: z.boolean().optional(),
});
@@ -1637,7 +1623,7 @@ export const zPortainerWebhook = z.object({
});
export const zPortainerUserThemeSettings = z.object({
- color: z.enum(['dark', 'light', 'highcontrast', 'auto']).optional(),
+ color: z.enum(['dark', 'light', 'highcontrast', 'auto', '']).optional(),
});
export const zPortainerUserRole = z.enum(PortainerUserRole);
@@ -1651,23 +1637,13 @@ export const zPortainerUserResourceAccess = z.object({
UserId: z.int().optional(),
});
-export const zPortainerAuthorizations = z.record(z.string(), z.boolean());
-
-export const zPortainerEndpointAuthorizations = z.record(
- z.string(),
- zPortainerAuthorizations
-);
-
export const zPortainerUser = z.object({
- EndpointAuthorizations: zPortainerEndpointAuthorizations.optional(),
- Id: z.int().optional(),
- PortainerAuthorizations: zPortainerAuthorizations.optional(),
- Role: zPortainerUserRole.optional(),
+ Id: z.int(),
+ Role: zPortainerUserRole,
ThemeSettings: zPortainerUserThemeSettings.optional(),
TokenIssueAt: z.int().optional(),
UseCache: z.boolean().optional(),
- UserTheme: z.string().optional(),
- Username: z.string().optional(),
+ Username: z.string(),
});
export const zPortainerTeamResourceAccess = z.object({
@@ -1885,6 +1861,8 @@ export const zPortainerSslSettings = z.object({
selfSigned: z.boolean().optional(),
});
+export const zPortainerAuthorizations = z.record(z.string(), z.boolean());
+
export const zPortainerRole = z.object({
Authorizations: zPortainerAuthorizations.optional(),
Description: z.string().optional(),
@@ -2181,7 +2159,21 @@ export const zPortainerCustomTemplatePlatform = z.enum(
PortainerCustomTemplatePlatform
);
+export const zPortainerArtifact = z.object({
+ configFilePath: z.string().optional(),
+ configHash: z.string().optional(),
+ edgeStackId: z.int().optional(),
+ referenceName: z.string().optional(),
+ stackId: z.int().optional(),
+});
+
+export const zPortainerArtifactSources = z.object({
+ artifact: zPortainerArtifact.optional(),
+ sourceIds: z.array(z.int()).optional(),
+});
+
export const zPortainerCustomTemplate = z.object({
+ ArtifactSources: zPortainerArtifactSources.optional(),
CreatedByUserId: z.int().optional(),
Description: z.string().optional(),
EdgeTemplate: z.boolean().optional(),
@@ -3574,10 +3566,15 @@ export const zGetEndpointGroupsByIdPath = z.object({
id: z.int(),
});
+export const zGetEndpointGroupsByIdQuery = z.object({
+ size: z.boolean().optional(),
+});
+
/**
* Success
*/
-export const zGetEndpointGroupsByIdResponse = zPortainerEndpointGroup;
+export const zGetEndpointGroupsByIdResponse =
+ zEndpointgroupsEndpointGroupResponse;
/**
* EndpointGroup details
@@ -5286,6 +5283,10 @@ export const zResourceControlUpdateResponse = zPortainerResourceControl;
*/
export const zRestoreBody = zBackupRestorePayload;
+export const zRestoreHeaders = z.object({
+ 'X-Setup-Token': z.string().optional(),
+});
+
/**
* Success
*/
@@ -5997,6 +5998,10 @@ export const zUserAdminCheckResponse = z.void();
*/
export const zUserAdminInitBody = zUsersAdminInitPayload;
+export const zUserAdminInitHeaders = z.object({
+ 'X-Setup-Token': z.string().optional(),
+});
+
/**
* Success
*/
diff --git a/app/react/sidebar/SidebarItem/SidebarItem.tsx b/app/react/sidebar/SidebarItem/SidebarItem.tsx
index 7b875be9a..16e661c82 100644
--- a/app/react/sidebar/SidebarItem/SidebarItem.tsx
+++ b/app/react/sidebar/SidebarItem/SidebarItem.tsx
@@ -20,6 +20,9 @@ interface Props extends AutomationTestingProps {
label: string;
isSubMenu?: boolean;
ignorePaths?: string[];
+ /** When a create or detail path id differs from the list path, includePaths can be used to specify which paths should also mark the item as active.
+ *
+ * E.g. including the portainer.wizard.endpoints (create environment) path to activate the portainer.endpoints sidebar item. */
includePaths?: string[];
count?: number;
}
diff --git a/go.mod b/go.mod
index 0f2f2bb9f..93ad5a24e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/portainer/portainer
-go 1.26.2
+go 1.26.3
replace github.com/robfig/cron/v3 => github.com/robfig/cron/v3 v3.0.1 // Not actively maintained. Pinned to last known good version. Review needed when upgrading.
@@ -16,7 +16,7 @@ require (
github.com/aws/smithy-go v1.24.2
github.com/cbroglie/mustache v1.4.0
github.com/compose-spec/compose-go/v2 v2.9.1
- github.com/containerd/containerd v1.7.30
+ github.com/containerd/containerd v1.7.32
github.com/containerd/errdefs v1.0.0
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
github.com/distribution/reference v0.6.0
@@ -48,6 +48,7 @@ require (
github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.67.5
github.com/prometheus/prometheus v0.311.3
+ github.com/quasilyte/go-ruleguard/dsl v0.3.23
github.com/robfig/cron/v3 v3.0.1
github.com/rs/zerolog v1.34.0
github.com/samber/slog-zerolog/v2 v2.9.1
@@ -126,12 +127,12 @@ require (
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/containerd/console v1.0.5 // indirect
- github.com/containerd/containerd/api v1.9.0 // indirect
- github.com/containerd/containerd/v2 v2.1.5 // indirect
+ github.com/containerd/containerd/api v1.10.0 // indirect
+ github.com/containerd/containerd/v2 v2.2.4 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
- github.com/containerd/platforms v1.0.0-rc.1 // indirect
+ github.com/containerd/platforms v1.0.0-rc.2 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
@@ -150,7 +151,7 @@ require (
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
github.com/edsrzf/mmap-go v1.2.0 // indirect
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect
- github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+ github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
@@ -272,7 +273,7 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/novln/docker-parser v1.0.0 // indirect
github.com/oklog/ulid/v2 v2.1.1 // indirect
- github.com/opencontainers/runtime-spec v1.2.1 // indirect
+ github.com/opencontainers/runtime-spec v1.3.0 // indirect
github.com/openshift/api v3.9.0+incompatible // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
@@ -314,7 +315,7 @@ require (
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
- github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
@@ -341,7 +342,7 @@ require (
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/mock v0.6.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
- golang.org/x/net v0.54.0 // indirect
+ golang.org/x/net v0.55.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/term v0.43.0 // indirect
golang.org/x/text v0.37.0 // indirect
@@ -371,5 +372,5 @@ require (
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/yaml v1.6.0 // indirect
- tags.cncf.io/container-device-interface v1.0.1 // indirect
+ tags.cncf.io/container-device-interface v1.1.0 // indirect
)
diff --git a/go.sum b/go.sum
index 4addcb24a..17ab4f159 100644
--- a/go.sum
+++ b/go.sum
@@ -56,8 +56,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4
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/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
-github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
+github.com/Microsoft/hcsshim v0.14.1 h1:CMuB3fqQVfPdhyXhUqYdUmPUIOhJkmghCx3dJet8Cqs=
+github.com/Microsoft/hcsshim v0.14.1/go.mod h1:VnzvPLyWUhxiPVsJ31P6XadxCcTogTguBFDy/1GR/OM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
@@ -187,16 +187,16 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AX
github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs=
github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
-github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
-github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
+github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
+github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
-github.com/containerd/containerd v1.7.30 h1:/2vezDpLDVGGmkUXmlNPLCCNKHJ5BbC5tJB5JNzQhqE=
-github.com/containerd/containerd v1.7.30/go.mod h1:fek494vwJClULlTpExsmOyKCMUAbuVjlFsJQc4/j44M=
-github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
-github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
-github.com/containerd/containerd/v2 v2.1.5 h1:pWSmPxUszaLZKQPvOx27iD4iH+aM6o0BoN9+hg77cro=
-github.com/containerd/containerd/v2 v2.1.5/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
+github.com/containerd/containerd v1.7.32 h1:S54xuVcPxeLaYgaRABtpJ2VyVUVsy0IGf7qHBs+sbY8=
+github.com/containerd/containerd v1.7.32/go.mod h1:jdwD6s/BhV4XVJGrvtziNPVA+83n66TwptVaPKprq4E=
+github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
+github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
+github.com/containerd/containerd/v2 v2.2.4 h1:8x2UdXqww7NYqGNabQ7i1nAgB5LegzjC9KQzO/900iA=
+github.com/containerd/containerd/v2 v2.2.4/go.mod h1:YBcTO8D9149QY9zNmUjy04Mhuc4DlrZQ8FIOwKZEM7o=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -209,8 +209,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/nydus-snapshotter v0.15.2 h1:qsHI4M+Wwrf6Jr4eBqhNx8qh+YU0dSiJ+WPmcLFWNcg=
github.com/containerd/nydus-snapshotter v0.15.2/go.mod h1:FfwH2KBkNYoisK/e+KsmNr7xTU53DmnavQHMFOcXwfM=
-github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
-github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
+github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
+github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=
@@ -294,8 +294,8 @@ github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJ
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
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/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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
@@ -771,8 +771,8 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
-github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
-github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
+github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs=
@@ -841,6 +841,8 @@ github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuX
github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU=
github.com/puzpuzpuz/xsync/v4 v4.4.0 h1:vlSN6/CkEY0pY8KaB0yqo/pCLZvp9nhdbBdjipT4gWo=
github.com/puzpuzpuz/xsync/v4 v4.4.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
+github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
+github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
@@ -976,8 +978,9 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
@@ -1119,8 +1122,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
-golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
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.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1283,5 +1286,5 @@ sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WO
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/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=
-tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
-tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
+tags.cncf.io/container-device-interface v1.1.0 h1:RnxNhxF1JOu6CJUVpetTYvrXHdxw9j9jFYgZpI+anSY=
+tags.cncf.io/container-device-interface v1.1.0/go.mod h1:76Oj0Yqp9FwTx/pySDc8Bxjpg+VqXfDb50cKAXVJ34Q=
diff --git a/package.json b/package.json
index 809eee06a..ca9a77237 100644
--- a/package.json
+++ b/package.json
@@ -37,15 +37,15 @@
"packageManager": "pnpm@10.26.2",
"dependencies": {
"@aws-crypto/sha256-js": "^2.0.0",
- "@codemirror/autocomplete": "^6.4.0",
- "@codemirror/commands": "^6.8.0",
- "@codemirror/language": "^6.3.2",
- "@codemirror/legacy-modes": "^6.3.1",
- "@codemirror/lint": "^6.8.4",
- "@codemirror/search": "^6.2.3",
- "@codemirror/state": "^6.2.0",
- "@codemirror/theme-one-dark": "^6.1.0",
- "@codemirror/view": "^6.7.1",
+ "@codemirror/autocomplete": "^6.20.2",
+ "@codemirror/commands": "^6.10.3",
+ "@codemirror/language": "^6.12.3",
+ "@codemirror/legacy-modes": "^6.5.3",
+ "@codemirror/lint": "^6.9.6",
+ "@codemirror/search": "^6.7.0",
+ "@codemirror/state": "^6.6.0",
+ "@codemirror/theme-one-dark": "^6.1.3",
+ "@codemirror/view": "^6.43.0",
"@lezer/common": "^1.0.2",
"@lezer/highlight": "^1.1.3",
"@nxmix/tokenize-ansi": "^3.0.0",
@@ -62,8 +62,8 @@
"@uirouter/angularjs": "1.0.11",
"@uirouter/react": "^1.0.7",
"@uirouter/react-hybrid": "^1.0.4",
- "@uiw/codemirror-themes": "^4.19.9",
- "@uiw/react-codemirror": "^4.19.5",
+ "@uiw/codemirror-themes": "^4.25.10",
+ "@uiw/react-codemirror": "^4.25.10",
"@wojtekmaj/react-daterange-picker": "^5.5.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
@@ -79,7 +79,7 @@
"angular-ui-bootstrap": "~2.5.0",
"angularjs-scroll-glue": "^2.2.0",
"angularjs-slider": "^6.4.0",
- "axios": "^1.13.5",
+ "axios": "^1.16.1",
"axios-cache-interceptor": "^1.11.2",
"axios-progress-bar": "git://github.com/portainer/progress-bar-4-axios",
"babel-plugin-angularjs-annotate": "^0.10.0",
@@ -91,7 +91,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^1.1.1",
"codemirror": "^6.0.1",
- "codemirror-json-schema": "^0.8.0",
+ "codemirror-json-schema": "^0.8.1",
"core-js": "^3.19.3",
"date-fns": "^2.29.3",
"docker-types": "~1.47",
@@ -99,7 +99,7 @@
"file-saver": "^2.0.5",
"filesize": "^10.1.6",
"filesize-parser": "^1.5.0",
- "formik": "^2.2.9",
+ "formik": "^2.4.9",
"globals": "^15.15.0",
"i18next": "^21.3.3",
"i18next-browser-languagedetector": "^6.1.2",
@@ -108,12 +108,12 @@
"js-base64": "^3.7.2",
"jsdom": "^24",
"json-schema": "^0.4.0",
- "lodash": "^4.17.21",
- "lodash-es": "npm:lodash@4.17.21",
+ "lodash": "^4.18.1",
+ "lodash-es": "npm:lodash@4.18.1",
"lucide-react": "^0.577.0",
"markdown-to-jsx": "^7.7.4",
- "moment": "^2.29.1",
- "moment-timezone": "^0.5.40",
+ "moment": "^2.30.1",
+ "moment-timezone": "^0.6.2",
"mustache": "^4.2.0",
"ng-file-upload": "~12.2.13",
"parse-duration": "^2.1.4",
@@ -126,8 +126,8 @@
"react-dom": "^17.0.2",
"react-i18next": "^11.12.0",
"react-is": "^17.0.2",
- "react-json-view-lite": "^1.2.1",
- "react-select": "^5.2.1",
+ "react-json-view-lite": "^1.5.0",
+ "react-select": "^5.10.2",
"react-select-async-paginate": "^0.7.11",
"sanitize-html": "^2.17.0",
"spinkit": "^2.0.1",
@@ -155,26 +155,26 @@
"@eslint/js": "^9.39.2",
"@hey-api/openapi-ts": "0.97.3",
"@simbathesailor/use-what-changed": "^2.0.0",
- "@storybook/addon-docs": "10.3.5",
- "@storybook/addon-links": "10.3.5",
+ "@storybook/addon-docs": "10.4.2",
+ "@storybook/addon-links": "10.4.2",
"@storybook/addon-styling-webpack": "^3.0.2",
"@storybook/addon-webpack5-compiler-swc": "^4.0.3",
- "@storybook/react-webpack5": "10.3.5",
+ "@storybook/react-webpack5": "10.4.2",
"@svgr/webpack": "^8.1.0",
"@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^12",
"@testing-library/react-hooks": "^8.0.1",
- "@testing-library/user-event": "^14.5.2",
+ "@testing-library/user-event": "^14.6.1",
"@types/angular": "^1.8.3",
"@types/file-saver": "^2.0.4",
"@types/filesize": "^5.0.2",
"@types/filesize-parser": "^1.5.1",
"@types/jquery": "^3.5.10",
"@types/json-schema": "^7.0.15",
- "@types/lodash": "^4.17.21",
+ "@types/lodash": "^4.17.24",
"@types/mustache": "^4.1.2",
- "@types/node": "^22.19.11",
+ "@types/node": "^25.9.1",
"@types/nprogress": "^0.2.0",
"@types/qs": "^6.9.17",
"@types/react": "^17.0.37",
@@ -184,19 +184,19 @@
"@types/sanitize-html": "^2.8.0",
"@types/toastr": "^2.1.39",
"@types/uuid": "^3.3.2",
- "@typescript-eslint/eslint-plugin": "^8.58.2",
- "@typescript-eslint/parser": "^8.58.2",
- "@vitest/coverage-v8": "^4",
+ "@typescript-eslint/eslint-plugin": "^8.60.1",
+ "@typescript-eslint/parser": "^8.60.1",
+ "@vitest/coverage-v8": "^4.1.8",
"@vitest/eslint-plugin": "^1.6.15",
"auto-ngtemplate-loader": "^3.1.2",
- "autoprefixer": "^10.4.16",
+ "autoprefixer": "^10.5.0",
"babel-loader": "^9.1.3",
"babel-plugin-i18next-extract": "^0.9.0",
"babel-plugin-lodash": "^3.3.4",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.8.1",
- "cssnano": "^6.0.1",
+ "cssnano": "^8.0.1",
"dotenv-webpack": "^8.0.1",
"eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8",
@@ -205,11 +205,11 @@
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
- "eslint-plugin-promise": "^7.2.1",
+ "eslint-plugin-promise": "^7.3.0",
"eslint-plugin-react": "^7.37.5",
- "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-regex": "^1.10.0",
- "eslint-plugin-storybook": "10.3.5",
+ "eslint-plugin-storybook": "10.4.2",
"html-loader": "^5.1.0",
"html-webpack-plugin": "^5.5.3",
"husky": "^8.0.0",
@@ -217,45 +217,43 @@
"lint-staged": "^14.0.1",
"lodash-webpack-plugin": "^0.11.6",
"mini-css-extract-plugin": "^2.7.6",
- "msw": "^2.12.10",
+ "msw": "^2.14.6",
"msw-storybook-addon": "^2.0.7",
"ngtemplate-loader": "^2.1.0",
- "postcss": "^8.5.6",
+ "postcss": "^8.5.15",
"postcss-loader": "^7.3.3",
"prettier": "^3.8.3",
"prettier-plugin-tailwindcss": "^0.8.0",
"react-docgen-typescript-plugin": "^1.0.5",
"source-map-loader": "^4.0.1",
"speed-measure-webpack-plugin": "^1.5.0",
- "storybook": "10.3.5",
+ "storybook": "10.4.2",
"storybook-css-modules-preset": "^1.1.1",
"style-loader": "^3.3.3",
"swagger2openapi": "^7.0.8",
"tailwindcss": "3.3.3",
"typescript": "^6.0.2",
- "typescript-eslint": "^8.58.2",
- "vite": "^8.0.8",
+ "typescript-eslint": "^8.60.1",
+ "vite": "^8.0.16",
"vite-plugin-svgr": "^4.5.0",
"vite-tsconfig-paths": "^4.3.1",
- "vitest": "^4",
- "webpack": "^5.105.0",
+ "vitest": "^4.1.8",
+ "webpack": "^5.107.2",
"webpack-build-notifier": "^3.1.0",
"webpack-bundle-analyzer": "^5.2.0",
"webpack-cli": "^6.0.1",
- "webpack-dev-server": "^5.2.3",
+ "webpack-dev-server": "^5.2.4",
"webpack-merge": "^6.0.1"
},
"pnpm": {
"overrides": {
"jquery": "^3.6.0",
"decompress": "^4.2.1",
- "lodash": "^4.17.21",
"minimist": "^1.2.6",
"http-proxy": "^1.18.1",
"daterangepicker>jquery": "^3.5.1",
"@uirouter/react": "^1.0.7",
"@uirouter/angularjs": "1.0.11",
- "moment": "^2.21.0",
"markdown-it": "^12.3.2",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
diff --git a/pkg/libhttp/ssrf/ssrf.go b/pkg/libhttp/ssrf/ssrf.go
new file mode 100644
index 000000000..15a3ca1e1
--- /dev/null
+++ b/pkg/libhttp/ssrf/ssrf.go
@@ -0,0 +1,252 @@
+package ssrf
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "net/http"
+ "net/url"
+ "strings"
+ "sync/atomic"
+
+ "github.com/rs/zerolog/log"
+)
+
+// Mode controls how the SSRF policy is applied.
+type Mode string
+
+const (
+ // ModeOff disables SSRF protection entirely. All connections pass through unchanged.
+ ModeOff Mode = "off"
+ // ModeAudit resolves and checks destinations but only logs violations; connections are allowed.
+ ModeAudit Mode = "audit"
+ // ModeEnforce blocks connections that violate the policy.
+ ModeEnforce Mode = "enforce"
+)
+
+// Policy defines the SSRF protection policy for outbound HTTP connections.
+type Policy struct {
+ // Mode controls whether protection is off, in audit-only mode, or enforcing.
+ Mode Mode
+
+ // AllowedHosts is the allowlist of permitted destinations.
+ // Accepted formats:
+ // - Exact hostname: "example.com"
+ // - Wildcard hostname: "*.example.com" (matches any subdomain at any depth)
+ // - Single IP: "1.2.3.4"
+ // - CIDR range: "10.0.0.0/8"
+ //
+ // When Mode is ModeEnforce and AllowedHosts is empty, all outbound connections are blocked.
+ AllowedHosts []string
+}
+
+type safeDialer struct {
+ base net.Dialer
+ mode Mode
+ allowedNets []*net.IPNet
+ allowedHosts map[string]bool
+ allowedWilds []string // derived from "*.foo.com" entries; stored as ".foo.com"
+}
+
+var globalDialer atomic.Pointer[safeDialer]
+
+// Configure initializes the global SSRF policy. Intended to be called once
+// at startup before any outbound HTTP connections are established.
+func Configure(policy Policy) {
+ if policy.Mode == ModeOff || policy.Mode == "" {
+ globalDialer.Store(nil)
+ return
+ }
+
+ globalDialer.Store(newSafeDialer(policy))
+}
+
+// IsEnabled reports whether SSRF protection is currently active (audit or enforce).
+func IsEnabled() bool {
+ return globalDialer.Load() != nil
+}
+
+// CheckURL validates rawURL against the active SSRF policy without making a
+// connection. Returns nil when protection is disabled or the destination is
+// permitted. In audit mode, logs a warning on violations and returns nil.
+func CheckURL(ctx context.Context, rawURL string) error {
+ d := globalDialer.Load()
+ if d == nil {
+ return nil
+ }
+
+ u, err := url.Parse(rawURL)
+ if err != nil {
+ return fmt.Errorf("ssrf: invalid URL %q: %w", rawURL, err)
+ }
+
+ host := u.Hostname()
+ if host == "" {
+ return nil
+ }
+
+ return d.checkHost(ctx, host)
+}
+
+// WrapTransport clones t and replaces its DialContext with the global SSRF-filtering
+// dialer. Returns t unchanged when SSRF protection is not configured.
+func WrapTransport(t *http.Transport) *http.Transport {
+ d := globalDialer.Load()
+ if d == nil {
+ return t
+ }
+
+ cloned := t.Clone()
+ cloned.DialContext = d.DialContext
+
+ return cloned
+}
+
+// WrapTransportInternal is a documented no-op for transports that connect to
+// internally computed destinations (local Docker socket proxy, Chisel tunnels,
+// in-cluster Kubernetes API). The destination is chosen by Portainer, not
+// supplied by any user, so SSRF validation is not applicable. Using this
+// function instead of WrapTransport makes the exemption explicit and
+// satisfies the ruleguard lint rule.
+func WrapTransportInternal(t *http.Transport) *http.Transport {
+ return t
+}
+
+func newSafeDialer(policy Policy) *safeDialer {
+ allowedNets := make([]*net.IPNet, 0, len(policy.AllowedHosts))
+ allowedHosts := make(map[string]bool, len(policy.AllowedHosts))
+ var allowedWilds []string
+
+ for _, entry := range policy.AllowedHosts {
+ if _, network, err := net.ParseCIDR(entry); err == nil {
+ allowedNets = append(allowedNets, network)
+ continue
+ }
+
+ if ip := net.ParseIP(entry); ip != nil {
+ bits := 32
+ if ip.To4() == nil {
+ bits = 128
+ }
+
+ mask := net.CIDRMask(bits, bits)
+ allowedNets = append(allowedNets, &net.IPNet{IP: ip.Mask(mask), Mask: mask})
+
+ continue
+ }
+
+ if strings.HasPrefix(entry, "*.") {
+ allowedWilds = append(allowedWilds, entry[1:]) // "*.foo.com" -> ".foo.com"
+ continue
+ }
+
+ allowedHosts[entry] = true
+ }
+
+ return &safeDialer{
+ mode: policy.Mode,
+ allowedNets: allowedNets,
+ allowedHosts: allowedHosts,
+ allowedWilds: allowedWilds,
+ }
+}
+
+// DialContext resolves addr, validates all resolved IPs against the allowlist policy,
+// then dials using the first resolved IP to prevent DNS rebinding attacks.
+func (d *safeDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
+ host, port, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, fmt.Errorf("ssrf: invalid address %q: %w", addr, err)
+ }
+
+ resolved, err := net.DefaultResolver.LookupIPAddr(ctx, host)
+ if err != nil {
+ return nil, fmt.Errorf("ssrf: resolving %q: %w", host, err)
+ }
+
+ if len(resolved) == 0 {
+ return nil, fmt.Errorf("ssrf: no addresses resolved for %q", host)
+ }
+
+ // Dial by resolved IP regardless of how the host was allowed to close the
+ // window between DNS validation and the TCP handshake (DNS rebinding).
+ dialTarget := net.JoinHostPort(resolved[0].IP.String(), port)
+
+ if d.allowedHosts[host] || d.matchesWildcard(host) {
+ return d.base.DialContext(ctx, network, dialTarget)
+ }
+
+ for _, a := range resolved {
+ if !d.ipAllowed(a.IP) {
+ if d.mode == ModeAudit {
+ log.Warn().Str("host", host).Str("ip", a.IP.String()).Msg("ssrf: destination not in allowlist (audit mode, allowing)")
+ continue
+ }
+
+ return nil, fmt.Errorf("ssrf: destination %s is not in the allowlist", a.IP)
+ }
+ }
+
+ return d.base.DialContext(ctx, network, dialTarget)
+}
+
+func (d *safeDialer) checkHost(ctx context.Context, host string) error {
+ if d.allowedHosts[host] || d.matchesWildcard(host) {
+ return nil
+ }
+
+ if ip := net.ParseIP(host); ip != nil {
+ if !d.ipAllowed(ip) {
+ if d.mode == ModeAudit {
+ log.Warn().Str("host", host).Msg("ssrf: destination not in allowlist (audit mode, allowing)")
+ return nil
+ }
+
+ return fmt.Errorf("ssrf: destination %s is not in the allowlist", ip)
+ }
+
+ return nil
+ }
+
+ resolved, err := net.DefaultResolver.LookupIPAddr(ctx, host)
+ if err != nil {
+ return fmt.Errorf("ssrf: resolving %q: %w", host, err)
+ }
+
+ if len(resolved) == 0 {
+ return fmt.Errorf("ssrf: no addresses resolved for %q", host)
+ }
+
+ for _, a := range resolved {
+ if !d.ipAllowed(a.IP) {
+ if d.mode == ModeAudit {
+ log.Warn().Str("host", host).Str("ip", a.IP.String()).Msg("ssrf: destination not in allowlist (audit mode, allowing)")
+ continue
+ }
+
+ return fmt.Errorf("ssrf: destination %s is not in the allowlist", a.IP)
+ }
+ }
+
+ return nil
+}
+
+func (d *safeDialer) matchesWildcard(host string) bool {
+ for _, suffix := range d.allowedWilds {
+ if strings.HasSuffix(host, suffix) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (d *safeDialer) ipAllowed(ip net.IP) bool {
+ for _, network := range d.allowedNets {
+ if network.Contains(ip) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/pkg/libhttp/ssrf/ssrf_test.go b/pkg/libhttp/ssrf/ssrf_test.go
new file mode 100644
index 000000000..329d63225
--- /dev/null
+++ b/pkg/libhttp/ssrf/ssrf_test.go
@@ -0,0 +1,357 @@
+package ssrf
+
+import (
+ "context"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestIpAllowed_CIDR(t *testing.T) {
+ t.Parallel()
+
+ d := newSafeDialer(Policy{
+ Mode: ModeEnforce,
+ AllowedHosts: []string{"8.8.0.0/16", "2001:4860::/32"},
+ })
+
+ require.True(t, d.ipAllowed(net.ParseIP("8.8.8.8")))
+ require.True(t, d.ipAllowed(net.ParseIP("8.8.4.4")))
+ require.True(t, d.ipAllowed(net.ParseIP("2001:4860:4860::8888")))
+
+ require.False(t, d.ipAllowed(net.ParseIP("1.1.1.1")))
+ require.False(t, d.ipAllowed(net.ParseIP("127.0.0.1")))
+ require.False(t, d.ipAllowed(net.ParseIP("169.254.169.254")))
+}
+
+func TestIpAllowed_SingleIP(t *testing.T) {
+ t.Parallel()
+
+ d := newSafeDialer(Policy{
+ Mode: ModeEnforce,
+ AllowedHosts: []string{"1.2.3.4"},
+ })
+
+ require.True(t, d.ipAllowed(net.ParseIP("1.2.3.4")))
+ require.False(t, d.ipAllowed(net.ParseIP("1.2.3.5")))
+}
+
+func TestMatchesWildcard(t *testing.T) {
+ t.Parallel()
+
+ d := newSafeDialer(Policy{
+ Mode: ModeEnforce,
+ AllowedHosts: []string{"*.example.com", "exact.host.com"},
+ })
+
+ require.True(t, d.matchesWildcard("foo.example.com"))
+ require.True(t, d.matchesWildcard("bar.example.com"))
+ require.True(t, d.matchesWildcard("deep.nested.example.com"))
+
+ require.False(t, d.matchesWildcard("example.com"))
+ require.False(t, d.matchesWildcard("notexample.com"))
+ require.False(t, d.matchesWildcard("exact.host.com"))
+}
+
+func TestNewSafeDialer_MixedHosts(t *testing.T) {
+ t.Parallel()
+
+ d := newSafeDialer(Policy{
+ Mode: ModeEnforce,
+ AllowedHosts: []string{"example.com", "*.internal.net", "10.0.0.0/8", "1.2.3.4"},
+ })
+
+ require.True(t, d.allowedHosts["example.com"])
+ require.Contains(t, d.allowedWilds, ".internal.net")
+ require.Len(t, d.allowedNets, 2) // 10.0.0.0/8 and 1.2.3.4/32
+}
+
+func TestConfigure_Disabled(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ require.NotNil(t, globalDialer.Load())
+
+ Configure(Policy{})
+ require.Nil(t, globalDialer.Load())
+}
+
+func TestWrapTransport_NoPolicy(t *testing.T) {
+ globalDialer.Store(nil)
+
+ base := &http.Transport{}
+ result := WrapTransport(base)
+ require.Equal(t, base, result)
+}
+
+func TestWrapTransport_WithPolicy(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ base := &http.Transport{}
+ result := WrapTransport(base)
+ require.NotEqual(t, base, result)
+ require.NotNil(t, result.DialContext)
+}
+
+func TestCheckURL_Disabled(t *testing.T) {
+ globalDialer.Store(nil)
+
+ err := CheckURL(t.Context(), "http://169.254.169.254/latest/meta-data/")
+ require.NoError(t, err)
+}
+
+func TestCheckURL_BlocksIPNotInAllowlist(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"8.8.8.0/24"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://169.254.169.254/latest/meta-data/")
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "ssrf")
+}
+
+func TestCheckURL_AllowedHostname(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "https://example.com/path")
+ require.NoError(t, err)
+}
+
+func TestCheckURL_AuditMode_ReturnsNil(t *testing.T) {
+ Configure(Policy{Mode: ModeAudit, AllowedHosts: []string{"8.8.8.0/24"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://169.254.169.254/latest/meta-data/")
+ require.NoError(t, err)
+}
+
+// TestDialContext_BlocksLoopback is an end-to-end test: it starts a real HTTP
+// server on 127.0.0.1, enables SSRF protection with an allowlist that does not
+// include loopback, and verifies that the wrapped transport blocks the request.
+func TestDialContext_BlocksLoopback(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ }))
+ defer srv.Close()
+
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"8.8.8.8"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ blocked := &http.Client{Transport: WrapTransport(&http.Transport{})}
+ resp, err := blocked.Get(srv.URL)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "ssrf")
+ if resp != nil {
+ require.NoError(t, resp.Body.Close())
+ }
+
+ Configure(Policy{})
+
+ open := &http.Client{Transport: WrapTransport(&http.Transport{})}
+ resp, err = open.Get(srv.URL)
+ require.NoError(t, err)
+ require.NoError(t, resp.Body.Close())
+}
+
+// TestDialContext_AuditMode_AllowsLoopback verifies that audit mode logs the
+// violation but still allows the connection through.
+func TestDialContext_AuditMode_AllowsLoopback(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ }))
+ defer srv.Close()
+
+ Configure(Policy{Mode: ModeAudit, AllowedHosts: []string{"8.8.8.8"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ client := &http.Client{Transport: WrapTransport(&http.Transport{})}
+ resp, err := client.Get(srv.URL)
+ require.NoError(t, err)
+ require.NoError(t, resp.Body.Close())
+}
+
+func TestIsEnabled(t *testing.T) {
+ globalDialer.Store(nil)
+ require.False(t, IsEnabled())
+
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+ require.True(t, IsEnabled())
+}
+
+func TestWrapTransportInternal(t *testing.T) {
+ t.Parallel()
+
+ base := &http.Transport{}
+ result := WrapTransportInternal(base)
+ require.Equal(t, base, result)
+}
+
+func TestNewSafeDialer_IPv6SingleIP(t *testing.T) {
+ t.Parallel()
+
+ d := newSafeDialer(Policy{
+ Mode: ModeEnforce,
+ AllowedHosts: []string{"::1"},
+ })
+
+ require.True(t, d.ipAllowed(net.ParseIP("::1")))
+ require.False(t, d.ipAllowed(net.ParseIP("::2")))
+}
+
+func TestCheckURL_InvalidURL(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://%gg")
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "ssrf")
+}
+
+func TestCheckURL_EmptyHost(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://")
+ require.NoError(t, err)
+}
+
+// TestCheckURL_IPInAllowlist verifies that a literal IP address that falls
+// within an allowed CIDR range is permitted.
+func TestCheckURL_IPInAllowlist(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"8.8.8.0/24"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://8.8.8.8/path")
+ require.NoError(t, err)
+}
+
+func TestCheckURL_WildcardHostname(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"*.example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "https://api.example.com/path")
+ require.NoError(t, err)
+}
+
+// TestCheckURL_HostnameDNSResolvesToAllowedIP verifies that a hostname
+// resolving to an IP within the allowlist is permitted (DNS resolution path).
+func TestCheckURL_HostnameDNSResolvesToAllowedIP(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"127.0.0.0/8", "::1/128"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://localhost/path")
+ require.NoError(t, err)
+}
+
+// TestCheckURL_HostnameDNSResolvesToBlockedIP verifies that a hostname
+// resolving to an IP outside the allowlist is blocked (DNS resolution path).
+func TestCheckURL_HostnameDNSResolvesToBlockedIP(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"8.8.8.0/24"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://localhost/path")
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "ssrf")
+}
+
+// TestCheckURL_HostnameDNSAuditMode verifies that audit mode logs violations
+// from hostname DNS resolution but still returns nil.
+func TestCheckURL_HostnameDNSAuditMode(t *testing.T) {
+ Configure(Policy{Mode: ModeAudit, AllowedHosts: []string{"8.8.8.0/24"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ err := CheckURL(t.Context(), "http://localhost/path")
+ require.NoError(t, err)
+}
+
+// TestCheckURL_HostnameDNSError verifies that a DNS resolution failure is
+// propagated as an SSRF-prefixed error.
+func TestCheckURL_HostnameDNSError(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"8.8.8.0/24"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ ctx, cancel := context.WithCancel(t.Context())
+ cancel()
+
+ err := CheckURL(ctx, "http://portainer-nonexistent.test.invalid/path")
+ require.Error(t, err)
+}
+
+// TestDialContext_InvalidAddress verifies that an address without a port
+// returns an SSRF-prefixed error.
+func TestDialContext_InvalidAddress(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"example.com"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ d := globalDialer.Load()
+ _, err := d.DialContext(t.Context(), "tcp", "no-port-here")
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "ssrf")
+}
+
+// TestDialContext_DNSError verifies that a DNS resolution failure in
+// DialContext is propagated as an SSRF-prefixed error.
+func TestDialContext_DNSError(t *testing.T) {
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ ctx, cancel := context.WithCancel(t.Context())
+ cancel()
+
+ d := globalDialer.Load()
+ _, err := d.DialContext(ctx, "tcp", "portainer-nonexistent.test.invalid:80")
+ require.Error(t, err)
+}
+
+// TestDialContext_AllowedByCIDR is an end-to-end test verifying that
+// connections to IPs within an allowed CIDR range are permitted.
+func TestDialContext_AllowedByCIDR(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ }))
+ defer srv.Close()
+
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"127.0.0.0/8"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ client := &http.Client{Transport: WrapTransport(&http.Transport{})}
+ resp, err := client.Get(srv.URL)
+ require.NoError(t, err)
+ require.NoError(t, resp.Body.Close())
+}
+
+// TestDialContext_AllowedByExactHostname verifies that when a hostname is in
+// the allowed-hosts list, the connection is permitted even though the resolved
+// IP is not covered by any CIDR entry.
+//
+// The server is bound to whatever IP "localhost" resolves to first so that the
+// dialTarget computed by DialContext (resolved[0]) matches the listening address.
+func TestDialContext_AllowedByExactHostname(t *testing.T) {
+ addrs, err := net.DefaultResolver.LookupIPAddr(t.Context(), "localhost")
+ require.NoError(t, err)
+ require.NotEmpty(t, addrs, "localhost must resolve to at least one address")
+
+ l, err := net.Listen("tcp", net.JoinHostPort(addrs[0].IP.String(), "0"))
+ require.NoError(t, err)
+
+ srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ }))
+ srv.Listener = l
+ srv.Start()
+ defer srv.Close()
+
+ _, portStr, err := net.SplitHostPort(l.Addr().String())
+ require.NoError(t, err)
+
+ Configure(Policy{Mode: ModeEnforce, AllowedHosts: []string{"localhost"}})
+ t.Cleanup(func() { globalDialer.Store(nil) })
+
+ client := &http.Client{Transport: WrapTransport(&http.Transport{})}
+ resp, err := client.Get("http://localhost:" + portStr)
+ require.NoError(t, err)
+ require.NoError(t, resp.Body.Close())
+}
diff --git a/pkg/libpolicy/fingerprint.go b/pkg/libpolicy/fingerprint.go
new file mode 100644
index 000000000..3dba6f2db
--- /dev/null
+++ b/pkg/libpolicy/fingerprint.go
@@ -0,0 +1,43 @@
+package libpolicy
+
+import (
+ "hash/fnv"
+ "sort"
+ "strconv"
+
+ "github.com/segmentio/encoding/json"
+
+ portainer "github.com/portainer/portainer/api"
+)
+
+// ConfigFingerprint is the generic primitive: FNV-1a over serialized policy
+// config bytes. It is a change detector, not a security boundary; collisions
+// are acceptable because a later poll or policy edit will retry reconciliation.
+// Callers must ensure the bytes are deterministic (e.g. collections sorted
+// before marshaling).
+//
+// New policy types should call this after marshaling their own config struct.
+func ConfigFingerprint(config []byte) string {
+ h := fnv.New32a()
+ h.Write(config)
+ return strconv.FormatUint(uint64(h.Sum32()), 16)
+}
+
+// HelmPolicyFingerprint computes a fingerprint for a Helm policy from its chart
+// summaries. Charts are sorted by name before hashing so the result is
+// order-independent. The restore manifest is excluded because it does not
+// affect what gets installed.
+//
+// Both server-ee and the agent import this function to guarantee they compute
+// identical fingerprints for the same chart summaries.
+func HelmPolicyFingerprint(chartSummaries []portainer.PolicyChartSummary) string {
+ sorted := make([]portainer.PolicyChartSummary, len(chartSummaries))
+ copy(sorted, chartSummaries)
+ sort.Slice(sorted, func(i, j int) bool { return sorted[i].ChartName < sorted[j].ChartName })
+ b, err := json.Marshal(portainer.HelmPolicyConfig{Charts: sorted})
+ if err != nil {
+ // json.Marshal of a known struct with no custom marshalers cannot error.
+ return ""
+ }
+ return ConfigFingerprint(b)
+}
diff --git a/pkg/registryhttp/client.go b/pkg/registryhttp/client.go
index 519d2fd30..cedab57b1 100644
--- a/pkg/registryhttp/client.go
+++ b/pkg/registryhttp/client.go
@@ -4,6 +4,7 @@ import (
"net/http"
portainer "github.com/portainer/portainer/api"
+ "github.com/portainer/portainer/pkg/libhttp/ssrf"
"github.com/rs/zerolog/log"
"oras.land/oras-go/v2/registry/remote/retry"
@@ -15,8 +16,8 @@ import (
func CreateClient(registry *portainer.Registry) (httpClient *http.Client, usePlainHttp bool, err error) {
switch registry.Type {
case portainer.AzureRegistry, portainer.EcrRegistry, portainer.GithubRegistry, portainer.GitlabRegistry, portainer.DockerHubRegistry:
- // Cloud registries use the default retry client with built-in TLS
- return retry.DefaultClient, false, nil
+ base := http.DefaultTransport.(*http.Transport).Clone()
+ return &http.Client{Transport: retry.NewTransport(ssrf.WrapTransport(base))}, false, nil
default:
// For all other registry types, use shared helper to build transport and scheme
diff --git a/pkg/registryhttp/client_test.go b/pkg/registryhttp/client_test.go
index b6d494c9f..abe780658 100644
--- a/pkg/registryhttp/client_test.go
+++ b/pkg/registryhttp/client_test.go
@@ -11,6 +11,11 @@ import (
"oras.land/oras-go/v2/registry/remote/retry"
)
+func isRetryTransport(t *http.Client) bool {
+ _, ok := t.Transport.(*retry.Transport)
+ return ok
+}
+
func init() {
fips.InitFIPS(false)
}
@@ -102,8 +107,7 @@ func TestCreateClient(t *testing.T) {
// Verify client type based on registry configuration
switch tt.registry.Type {
case portainer.AzureRegistry, portainer.EcrRegistry, portainer.GithubRegistry, portainer.GitlabRegistry:
- // Cloud registries should use the default retry client
- assert.Equal(t, retry.DefaultClient, client)
+ assert.True(t, isRetryTransport(client), "Cloud registries should use a retry transport")
}
})
}
@@ -133,7 +137,7 @@ func TestCreateClient_CloudRegistries(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, client)
assert.False(t, usePlainHTTP, "Cloud registries should use HTTPS")
- assert.Equal(t, retry.DefaultClient, client, "Cloud registries should use default retry client")
+ assert.True(t, isRetryTransport(client), "Cloud registries should use a retry transport")
})
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fb2f7026f..19dd19bd6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,13 +7,11 @@ settings:
overrides:
jquery: ^3.6.0
decompress: ^4.2.1
- lodash: ^4.17.21
minimist: ^1.2.6
http-proxy: ^1.18.1
daterangepicker>jquery: ^3.5.1
'@uirouter/react': ^1.0.7
'@uirouter/angularjs': 1.0.11
- moment: ^2.21.0
markdown-it: ^12.3.2
'@types/react': ^17.0.37
'@types/react-dom': ^17.0.11
@@ -29,32 +27,32 @@ importers:
specifier: ^2.0.0
version: 2.0.1
'@codemirror/autocomplete':
- specifier: ^6.4.0
- version: 6.18.6
+ specifier: ^6.20.2
+ version: 6.20.2
'@codemirror/commands':
- specifier: ^6.8.0
- version: 6.8.0
+ specifier: ^6.10.3
+ version: 6.10.3
'@codemirror/language':
- specifier: ^6.3.2
- version: 6.10.8
+ specifier: ^6.12.3
+ version: 6.12.3
'@codemirror/legacy-modes':
- specifier: ^6.3.1
- version: 6.4.2
+ specifier: ^6.5.3
+ version: 6.5.3
'@codemirror/lint':
- specifier: ^6.8.4
- version: 6.8.4
+ specifier: ^6.9.6
+ version: 6.9.6
'@codemirror/search':
- specifier: ^6.2.3
- version: 6.5.8
+ specifier: ^6.7.0
+ version: 6.7.0
'@codemirror/state':
- specifier: ^6.2.0
- version: 6.5.1
+ specifier: ^6.6.0
+ version: 6.6.0
'@codemirror/theme-one-dark':
- specifier: ^6.1.0
- version: 6.1.2
+ specifier: ^6.1.3
+ version: 6.1.3
'@codemirror/view':
- specifier: ^6.7.1
- version: 6.36.2
+ specifier: ^6.43.0
+ version: 6.43.0
'@lezer/common':
specifier: ^1.0.2
version: 1.2.3
@@ -104,11 +102,11 @@ importers:
specifier: ^1.0.4
version: 1.0.4(angular@1.8.2)(react@17.0.2)
'@uiw/codemirror-themes':
- specifier: ^4.19.9
- version: 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
+ specifier: ^4.25.10
+ version: 4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)
'@uiw/react-codemirror':
- specifier: ^4.19.5
- version: 4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ specifier: ^4.25.10
+ version: 4.25.10(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@wojtekmaj/react-daterange-picker':
specifier: ^5.5.0
version: 5.5.0(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
@@ -155,14 +153,14 @@ importers:
specifier: ^6.4.0
version: 6.7.0(angular@1.8.2)
axios:
- specifier: ^1.13.5
- version: 1.13.5
+ specifier: ^1.16.1
+ version: 1.16.1
axios-cache-interceptor:
specifier: ^1.11.2
- version: 1.11.2(axios@1.13.5)
+ version: 1.11.2(axios@1.16.1)
axios-progress-bar:
specifier: git://github.com/portainer/progress-bar-4-axios
- version: https://codeload.github.com/portainer/progress-bar-4-axios/tar.gz/d424cd29f5b629af9ffc5c3d6db8d92d11d82f0f(axios@1.13.5)
+ version: https://codeload.github.com/portainer/progress-bar-4-axios/tar.gz/d424cd29f5b629af9ffc5c3d6db8d92d11d82f0f(axios@1.16.1)
babel-plugin-angularjs-annotate:
specifier: ^0.10.0
version: 0.10.0
@@ -191,8 +189,8 @@ importers:
specifier: ^6.0.1
version: 6.0.1
codemirror-json-schema:
- specifier: ^0.8.0
- version: 0.8.0(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(@lezer/common@1.2.3)
+ specifier: ^0.8.1
+ version: 0.8.1(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)(@lezer/common@1.2.3)
core-js:
specifier: ^3.19.3
version: 3.20.0
@@ -215,8 +213,8 @@ importers:
specifier: ^1.5.0
version: 1.5.0
formik:
- specifier: ^2.2.9
- version: 2.2.9(react@17.0.2)
+ specifier: ^2.4.9
+ version: 2.4.9(@types/react@17.0.75)(react@17.0.2)
globals:
specifier: ^15.15.0
version: 15.15.0
@@ -242,11 +240,11 @@ importers:
specifier: ^0.4.0
version: 0.4.0
lodash:
- specifier: ^4.17.21
- version: 4.17.21
+ specifier: ^4.18.1
+ version: 4.18.1
lodash-es:
- specifier: npm:lodash@4.17.21
- version: lodash@4.17.21
+ specifier: npm:lodash@4.18.1
+ version: lodash@4.18.1
lucide-react:
specifier: ^0.577.0
version: 0.577.0(react@17.0.2)
@@ -254,11 +252,11 @@ importers:
specifier: ^7.7.4
version: 7.7.4(react@17.0.2)
moment:
- specifier: ^2.21.0
- version: 2.29.4
+ specifier: ^2.30.1
+ version: 2.30.1
moment-timezone:
- specifier: ^0.5.40
- version: 0.5.41
+ specifier: ^0.6.2
+ version: 0.6.2
mustache:
specifier: ^4.2.0
version: 4.2.0
@@ -282,7 +280,7 @@ importers:
version: 4.8.0(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
react-codemirror-merge:
specifier: ^4.23.11
- version: 4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ version: 4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
react-datetime-picker:
specifier: ^5.6.0
version: 5.6.0(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
@@ -296,14 +294,14 @@ importers:
specifier: ^17.0.2
version: 17.0.2
react-json-view-lite:
- specifier: ^1.2.1
- version: 1.2.1(react@17.0.2)
+ specifier: ^1.5.0
+ version: 1.5.0(react@17.0.2)
react-select:
- specifier: ^5.2.1
- version: 5.2.1(@babel/core@7.23.6)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ specifier: ^5.10.2
+ version: 5.10.2(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
react-select-async-paginate:
specifier: ^0.7.11
- version: 0.7.11(@types/react@17.0.75)(react-select@5.2.1(@babel/core@7.23.6)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)
+ version: 0.7.11(@types/react@17.0.75)(react-select@5.10.2(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)
sanitize-html:
specifier: ^2.17.0
version: 2.17.0
@@ -358,13 +356,13 @@ importers:
version: 7.23.0(@babel/core@7.23.6)
'@chromatic-com/storybook':
specifier: ^5.1.2
- version: 5.1.2(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
+ version: 5.1.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
'@eslint-community/eslint-plugin-eslint-comments':
specifier: ^4.7.1
- version: 4.7.1(eslint@9.39.4(jiti@2.6.1))
+ version: 4.7.1(eslint@9.39.4(jiti@2.7.0))
'@eslint/compat':
specifier: ^2.0.0
- version: 2.0.5(eslint@9.39.4(jiti@2.6.1))
+ version: 2.0.5(eslint@9.39.4(jiti@2.7.0))
'@eslint/eslintrc':
specifier: ^3.3.3
version: 3.3.5
@@ -378,20 +376,20 @@ importers:
specifier: ^2.0.0
version: 2.0.0(react@17.0.2)
'@storybook/addon-docs':
- specifier: 10.3.5
- version: 10.3.5(@types/react@17.0.75)(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.105.0)
+ specifier: 10.4.2
+ version: 10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.107.2)
'@storybook/addon-links':
- specifier: 10.3.5
- version: 10.3.5(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
+ specifier: 10.4.2
+ version: 10.4.2(@types/react@17.0.75)(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
'@storybook/addon-styling-webpack':
specifier: ^3.0.2
- version: 3.0.2(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.105.0)
+ version: 3.0.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.107.2)
'@storybook/addon-webpack5-compiler-swc':
specifier: ^4.0.3
- version: 4.0.3(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.105.0)
+ version: 4.0.3(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.107.2)
'@storybook/react-webpack5':
- specifier: 10.3.5
- version: 10.3.5(@swc/core@1.15.11)(esbuild@0.27.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)
+ specifier: 10.4.2
+ version: 10.4.2(@swc/core@1.15.11)(@types/react-dom@17.0.25)(@types/react@17.0.75)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)
'@svgr/webpack':
specifier: ^8.1.0
version: 8.1.0
@@ -408,8 +406,8 @@ importers:
specifier: ^8.0.1
version: 8.0.1(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@testing-library/user-event':
- specifier: ^14.5.2
- version: 14.5.2(@testing-library/dom@9.3.4)
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@9.3.4)
'@types/angular':
specifier: ^1.8.3
version: 1.8.9
@@ -429,8 +427,8 @@ importers:
specifier: ^7.0.15
version: 7.0.15
'@types/lodash':
- specifier: ^4.17.21
- version: 4.17.21
+ specifier: ^4.17.24
+ version: 4.17.24
'@types/mustache':
specifier: ^4.1.2
version: 4.2.6
@@ -465,26 +463,26 @@ importers:
specifier: ^3.3.2
version: 3.4.13
'@typescript-eslint/eslint-plugin':
- specifier: ^8.58.2
- version: 8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
+ specifier: ^8.60.1
+ version: 8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
'@typescript-eslint/parser':
- specifier: ^8.58.2
- version: 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
+ specifier: ^8.60.1
+ version: 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
'@vitest/coverage-v8':
- specifier: ^4
+ specifier: ^4.1.8
version: 4.1.8(vitest@4.1.8)
'@vitest/eslint-plugin':
specifier: ^1.6.15
- version: 1.6.19(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.8)
+ version: 1.6.15(@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)(vitest@4.1.8)
auto-ngtemplate-loader:
specifier: ^3.1.2
- version: 3.1.2(webpack@5.105.0)
+ version: 3.1.2(webpack@5.107.2)
autoprefixer:
- specifier: ^10.4.16
- version: 10.4.16(postcss@8.5.6)
+ specifier: ^10.5.0
+ version: 10.5.0(postcss@8.5.15)
babel-loader:
specifier: ^9.1.3
- version: 9.1.3(@babel/core@7.23.6)(webpack@5.105.0)
+ version: 9.1.3(@babel/core@7.23.6)(webpack@5.107.2)
babel-plugin-i18next-extract:
specifier: ^0.9.0
version: 0.9.0
@@ -493,61 +491,61 @@ importers:
version: 3.3.4
clean-webpack-plugin:
specifier: ^4.0.0
- version: 4.0.0(webpack@5.105.0)
+ version: 4.0.0(webpack@5.107.2)
copy-webpack-plugin:
specifier: ^11.0.0
- version: 11.0.0(webpack@5.105.0)
+ version: 11.0.0(webpack@5.107.2)
css-loader:
specifier: ^6.8.1
- version: 6.8.1(webpack@5.105.0)
+ version: 6.8.1(webpack@5.107.2)
cssnano:
- specifier: ^6.0.1
- version: 6.0.1(postcss@8.5.6)
+ specifier: ^8.0.1
+ version: 8.0.1(postcss@8.5.15)
dotenv-webpack:
specifier: ^8.0.1
- version: 8.0.1(webpack@5.105.0)
+ version: 8.0.1(webpack@5.107.2)
eslint:
specifier: ^9.39.4
- version: 9.39.4(jiti@2.6.1)
+ version: 9.39.4(jiti@2.7.0)
eslint-config-prettier:
specifier: ^10.1.8
- version: 10.1.8(eslint@9.39.4(jiti@2.6.1))
+ version: 10.1.8(eslint@9.39.4(jiti@2.7.0))
eslint-import-resolver-alias:
specifier: ^1.1.2
version: 1.1.2(eslint-plugin-import@2.32.0)
eslint-import-resolver-typescript:
specifier: ^4.4.4
- version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1))
+ version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-eslint-comments:
specifier: ^3.2.0
- version: 3.2.0(eslint@9.39.4(jiti@2.6.1))
+ version: 3.2.0(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-import:
specifier: ^2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.6.1))
+ version: 2.32.0(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-jsx-a11y:
specifier: ^6.10.2
- version: 6.10.2(eslint@9.39.4(jiti@2.6.1))
+ version: 6.10.2(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-promise:
- specifier: ^7.2.1
- version: 7.2.1(eslint@9.39.4(jiti@2.6.1))
+ specifier: ^7.3.0
+ version: 7.3.0(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-react:
specifier: ^7.37.5
- version: 7.37.5(eslint@9.39.4(jiti@2.6.1))
+ version: 7.37.5(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-react-hooks:
- specifier: ^7.0.1
- version: 7.0.1(eslint@9.39.4(jiti@2.6.1))
+ specifier: ^7.1.1
+ version: 7.1.1(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-regex:
specifier: ^1.10.0
- version: 1.10.0(eslint@9.39.4(jiti@2.6.1))
+ version: 1.10.0(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-storybook:
- specifier: 10.3.5
- version: 10.3.5(eslint@9.39.4(jiti@2.6.1))(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)
+ specifier: 10.4.2
+ version: 10.4.2(eslint@9.39.4(jiti@2.7.0))(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)
html-loader:
specifier: ^5.1.0
- version: 5.1.0(webpack@5.105.0)
+ version: 5.1.0(webpack@5.107.2)
html-webpack-plugin:
specifier: ^5.5.3
- version: 5.5.3(webpack@5.105.0)
+ version: 5.5.3(webpack@5.107.2)
husky:
specifier: ^8.0.0
version: 8.0.3
@@ -559,25 +557,25 @@ importers:
version: 14.0.1
lodash-webpack-plugin:
specifier: ^0.11.6
- version: 0.11.6(webpack@5.105.0)
+ version: 0.11.6(webpack@5.107.2)
mini-css-extract-plugin:
specifier: ^2.7.6
- version: 2.7.6(webpack@5.105.0)
+ version: 2.7.6(webpack@5.107.2)
msw:
- specifier: ^2.12.10
- version: 2.12.10(@types/node@25.0.3)(typescript@6.0.2)
+ specifier: ^2.14.6
+ version: 2.14.6(@types/node@25.0.3)(typescript@6.0.2)
msw-storybook-addon:
specifier: ^2.0.7
- version: 2.0.7(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))
+ version: 2.0.7(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))
ngtemplate-loader:
specifier: ^2.1.0
version: 2.1.0
postcss:
- specifier: ^8.5.6
- version: 8.5.6
+ specifier: ^8.5.15
+ version: 8.5.15
postcss-loader:
specifier: ^7.3.3
- version: 7.3.3(postcss@8.5.6)(webpack@5.105.0)
+ version: 7.3.3(postcss@8.5.15)(webpack@5.107.2)
prettier:
specifier: ^3.8.3
version: 3.8.3
@@ -586,22 +584,22 @@ importers:
version: 0.8.0(prettier@3.8.3)
react-docgen-typescript-plugin:
specifier: ^1.0.5
- version: 1.0.5(typescript@6.0.2)(webpack@5.105.0)
+ version: 1.0.5(typescript@6.0.2)(webpack@5.107.2)
source-map-loader:
specifier: ^4.0.1
- version: 4.0.1(webpack@5.105.0)
+ version: 4.0.1(webpack@5.107.2)
speed-measure-webpack-plugin:
specifier: ^1.5.0
- version: 1.5.0(webpack@5.105.0)
+ version: 1.5.0(webpack@5.107.2)
storybook:
- specifier: 10.3.5
- version: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ specifier: 10.4.2
+ version: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
storybook-css-modules-preset:
specifier: ^1.1.1
version: 1.1.1
style-loader:
specifier: ^3.3.3
- version: 3.3.3(webpack@5.105.0)
+ version: 3.3.3(webpack@5.107.2)
swagger2openapi:
specifier: ^7.0.8
version: 7.0.8
@@ -612,35 +610,35 @@ importers:
specifier: ^6.0.2
version: 6.0.2
typescript-eslint:
- specifier: ^8.58.2
- version: 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
+ specifier: ^8.60.1
+ version: 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
vite:
- specifier: ^8.0.8
- version: 8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
+ specifier: ^8.0.16
+ version: 8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
vite-plugin-svgr:
specifier: ^4.5.0
- version: 4.5.0(rollup@4.54.0)(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
+ version: 4.5.0(rollup@4.54.0)(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
vite-tsconfig-paths:
specifier: ^4.3.1
- version: 4.3.1(typescript@6.0.2)(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
+ version: 4.3.1(typescript@6.0.2)(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
vitest:
- specifier: ^4
- version: 4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
+ specifier: ^4.1.8
+ version: 4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
webpack:
- specifier: ^5.105.0
- version: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ specifier: ^5.107.2
+ version: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
webpack-build-notifier:
specifier: ^3.1.0
- version: 3.1.0(webpack@5.105.0)
+ version: 3.1.0(webpack@5.107.2)
webpack-bundle-analyzer:
specifier: ^5.2.0
version: 5.2.0
webpack-cli:
specifier: ^6.0.1
- version: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ version: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2)
webpack-dev-server:
- specifier: ^5.2.3
- version: 5.2.3(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.105.0)
+ specifier: ^5.2.4
+ version: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.2)
webpack-merge:
specifier: ^6.0.1
version: 6.0.1
@@ -1414,11 +1412,11 @@ packages:
peerDependencies:
storybook: ^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0
- '@codemirror/autocomplete@6.18.6':
- resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==}
+ '@codemirror/autocomplete@6.20.2':
+ resolution: {integrity: sha512-G5FPkgIiLjOgZMjqVjvuKQ1rGPtHogLldJr33eFJdVLtmwY+giGrlv/ewljLz6b9BSQLkjxuwBc6g6omDM+YxQ==}
- '@codemirror/commands@6.8.0':
- resolution: {integrity: sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ==}
+ '@codemirror/commands@6.10.3':
+ resolution: {integrity: sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==}
'@codemirror/lang-json@6.0.1':
resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
@@ -1426,29 +1424,32 @@ packages:
'@codemirror/lang-yaml@6.1.2':
resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==}
- '@codemirror/language@6.10.8':
- resolution: {integrity: sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==}
+ '@codemirror/language@6.12.3':
+ resolution: {integrity: sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==}
- '@codemirror/legacy-modes@6.4.2':
- resolution: {integrity: sha512-HsvWu08gOIIk303eZQCal4H4t65O/qp1V4ul4zVa3MHK5FJ0gz3qz3O55FIkm+aQUcshUOjBx38t2hPiJwW5/g==}
+ '@codemirror/legacy-modes@6.5.3':
+ resolution: {integrity: sha512-xCsmIzH78MyWkib9jlPaaun57XNkfbMIhagfaZVd0iLTqlpw3jXaIcbZm72MTmmn64eTZpBVNjbyYh+QXnxRsg==}
- '@codemirror/lint@6.8.4':
- resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==}
+ '@codemirror/lint@6.9.6':
+ resolution: {integrity: sha512-6Kp7r6XfCi/D/5sdXieMfg9pJU1bUEx96WITuLU6ESaKizCz0QHFMjY/TaFSbigDdEAIgi93itLBIUETP4oK+A==}
'@codemirror/merge@6.10.0':
resolution: {integrity: sha512-Omn0gU6MM5cKQGqgKoIhFjUqCNWH/nukCMLXzu/1jOdtiHsxAu3GdENBf1QYkoIC3FSgkF7X/ClOqYeUATQ4Sw==}
- '@codemirror/search@6.5.8':
- resolution: {integrity: sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==}
+ '@codemirror/search@6.7.0':
+ resolution: {integrity: sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==}
- '@codemirror/state@6.5.1':
- resolution: {integrity: sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==}
+ '@codemirror/state@6.6.0':
+ resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==}
- '@codemirror/theme-one-dark@6.1.2':
- resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
+ '@codemirror/theme-one-dark@6.1.3':
+ resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
- '@codemirror/view@6.36.2':
- resolution: {integrity: sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==}
+ '@codemirror/view@6.43.0':
+ resolution: {integrity: sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==}
+
+ '@colordx/core@5.4.3':
+ resolution: {integrity: sha512-kIxYSfA5T8HXjav55UaaH/o/cKivF6jCCGIb8eqtcsfI46wsvlSiT8jMDyrl779qLec3c2c2oHBZo4oAhvbjrQ==}
'@discoveryjs/json-ext@0.5.7':
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
@@ -1458,51 +1459,77 @@ packages:
resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==}
engines: {node: '>=14.17.0'}
+ '@emnapi/core@1.10.0':
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
+
'@emnapi/core@1.9.2':
resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==}
+ '@emnapi/runtime@1.10.0':
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
+
'@emnapi/runtime@1.9.2':
resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==}
'@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+ '@emotion/babel-plugin@11.13.5':
+ resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==}
+
+ '@emotion/cache@11.14.0':
+ resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==}
+
'@emotion/cache@11.6.0':
resolution: {integrity: sha512-ElbsWY1KMwEowkv42vGo0UPuLgtPYfIs9BxxVrmvsaJVvktknsHYYlx5NQ5g6zLDcOTyamlDc7FkRg2TAcQDKQ==}
- '@emotion/hash@0.8.0':
- resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
+ '@emotion/hash@0.9.2':
+ resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==}
'@emotion/memoize@0.7.5':
resolution: {integrity: sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==}
- '@emotion/react@11.6.0':
- resolution: {integrity: sha512-23MnRZFBN9+D1lHXC5pD6z4X9yhPxxtHr6f+iTGz6Fv6Rda0GdefPrsHL7otsEf+//7uqCdT5QtHeRxHCERzuw==}
+ '@emotion/memoize@0.9.0':
+ resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==}
+
+ '@emotion/react@11.14.0':
+ resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==}
peerDependencies:
- '@babel/core': ^7.0.0
'@types/react': '*'
react: '>=16.8.0'
peerDependenciesMeta:
- '@babel/core':
- optional: true
'@types/react':
optional: true
- '@emotion/serialize@1.0.2':
- resolution: {integrity: sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==}
+ '@emotion/serialize@1.3.3':
+ resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==}
'@emotion/sheet@1.1.0':
resolution: {integrity: sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==}
- '@emotion/unitless@0.7.5':
- resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
+ '@emotion/sheet@1.4.0':
+ resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==}
+
+ '@emotion/unitless@0.10.0':
+ resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==}
+
+ '@emotion/use-insertion-effect-with-fallbacks@1.2.0':
+ resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==}
+ peerDependencies:
+ react: '>=16.8.0'
'@emotion/utils@1.0.0':
resolution: {integrity: sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==}
+ '@emotion/utils@1.4.2':
+ resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==}
+
'@emotion/weak-memoize@0.2.5':
resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==}
+ '@emotion/weak-memoize@0.4.0':
+ resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==}
+
'@esbuild/aix-ppc64@0.27.3':
resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
engines: {node: '>=18'}
@@ -1719,6 +1746,15 @@ packages:
'@exodus/schemasafe@1.0.0-rc.6':
resolution: {integrity: sha512-dDnQizD94EdBwEj/fh3zPRa/HWCS9O5au2PuHhZBbuM3xWHxuaKzPBOEWze7Nn0xW68MIpZ7Xdyn1CoCpjKCuQ==}
+ '@floating-ui/core@1.7.5':
+ resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
+
+ '@floating-ui/dom@1.7.6':
+ resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
+
+ '@floating-ui/utils@0.2.11':
+ resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
+
'@hey-api/codegen-core@0.8.2':
resolution: {integrity: sha512-R2NMf3wq97rh1mjz33WJQU8svz3F0RYUjvx/QzXucjpSqQ3O5huTdDjErG4fMxSr1X+X56NuDrqtGfHmo1TRUQ==}
engines: {node: '>=22.13.0'}
@@ -1760,35 +1796,35 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
- '@inquirer/ansi@1.0.2':
- resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==}
- engines: {node: '>=18'}
+ '@inquirer/ansi@2.0.7':
+ resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==}
+ engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
- '@inquirer/confirm@5.1.21':
- resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==}
- engines: {node: '>=18'}
+ '@inquirer/confirm@6.1.1':
+ resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==}
+ engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
peerDependencies:
'@types/node': ^25
peerDependenciesMeta:
'@types/node':
optional: true
- '@inquirer/core@10.3.2':
- resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==}
- engines: {node: '>=18'}
+ '@inquirer/core@11.2.1':
+ resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==}
+ engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
peerDependencies:
'@types/node': ^25
peerDependenciesMeta:
'@types/node':
optional: true
- '@inquirer/figures@1.0.15':
- resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==}
- engines: {node: '>=18'}
+ '@inquirer/figures@2.0.7':
+ resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==}
+ engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
- '@inquirer/type@3.0.10':
- resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
- engines: {node: '>=18'}
+ '@inquirer/type@4.0.7':
+ resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==}
+ engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
peerDependencies:
'@types/node': ^25
peerDependenciesMeta:
@@ -1962,6 +1998,9 @@ packages:
'@lezer/common@1.2.3':
resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==}
+ '@lezer/common@1.5.2':
+ resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==}
+
'@lezer/highlight@1.2.1':
resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
@@ -1987,15 +2026,15 @@ packages:
'@types/react': ^17.0.37
react: '>=16'
- '@mswjs/interceptors@0.41.2':
- resolution: {integrity: sha512-7G0Uf0yK3f2bjElBLGHIQzgRgMESczOMyYVasq1XK8P5HaXtlW4eQhz9MBL+TQILZLaruq+ClGId+hH0w4jvWw==}
+ '@mswjs/interceptors@0.41.9':
+ resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==}
engines: {node: '>=18'}
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
- '@napi-rs/wasm-runtime@1.1.3':
- resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==}
+ '@napi-rs/wasm-runtime@1.1.4':
+ resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
peerDependencies:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
@@ -2026,14 +2065,234 @@ packages:
'@open-draft/deferred-promise@2.2.0':
resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
+ '@open-draft/deferred-promise@3.0.0':
+ resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==}
+
'@open-draft/logger@0.3.0':
resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==}
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
- '@oxc-project/types@0.124.0':
- resolution: {integrity: sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==}
+ '@oxc-parser/binding-android-arm-eabi@0.127.0':
+ resolution: {integrity: sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxc-parser/binding-android-arm64@0.127.0':
+ resolution: {integrity: sha512-b5jtVTH6AU5CJXHNdj7Jj9IEiR9yVjjnwHzPJhGyHGPdcsZSzBCkS9GBbV33niRMvKthDwQRFRJfI4a+k4PvYg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxc-parser/binding-darwin-arm64@0.127.0':
+ resolution: {integrity: sha512-obCE8B7ISKkJidjlhv9xRGJPOSDG2Yu6PRga9Ruaz35uintHxbp1Ki/Yc71wx4rj3Edrm0a1kzG1TAwit0wFpg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxc-parser/binding-darwin-x64@0.127.0':
+ resolution: {integrity: sha512-JL6Xb5IwPQT8rUzlpsX7E+AgfcdNklXNPFp8pjCQQ5MQOQo5rtEB2ui+3Hgg9Sn7Y9Egj6YOLLiHhLpdAe12Aw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxc-parser/binding-freebsd-x64@0.127.0':
+ resolution: {integrity: sha512-SDQ/3MQFw58fqQz3Z1PhSKFF3JoCF4gmlNjziDm8X02tTahCw0qJbd7FGPDKw1i4VTBZene9JPyC3mHtSvi+wA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0':
+ resolution: {integrity: sha512-Av+D1MIqzV0YMGPT9we2SIZaMKD7Cxs4CvXSx/yxaWHewZjYEjScpOf5igc8IILASViw4WTnjlwUdI1KzVtDHQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-arm-musleabihf@0.127.0':
+ resolution: {integrity: sha512-Cs2fdJ8cPpFdeebj6p4dag8A4+56hPvZ0AhQQzlaLswGz1tz7bXt1nETLeorrM9+AMcWFFkqxcXwDGfTVidY8g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-arm64-gnu@0.127.0':
+ resolution: {integrity: sha512-qdOfTcT6SY8gsJrrV92uyEUyjqMGPpIB5JZUG6QN5dukYd+7/j0kX6MwK1DgQj39jtUYixxPiaRUiEN1+0CXgQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-arm64-musl@0.127.0':
+ resolution: {integrity: sha512-EoTCZneNFU/P2qrpEM+RHmQwt+CvDkyGESG6qhr7KaegXLZwePfbrkCDfAk8/rhxbDUVGsZILX+2tqPzFtoFWA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-ppc64-gnu@0.127.0':
+ resolution: {integrity: sha512-zALjmZYgxFLHjXeudcDF0xFGNydTAtkAeXAr2EuC17ywCyFxcmQra4w0BMde0Yi/re4Bi4iwEoEXtYN7l6eBLQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-riscv64-gnu@0.127.0':
+ resolution: {integrity: sha512-fPP8M6zQLS7Jz7o9d5ArUSuAuSK3e+WCYVrCpdzeCOejidtZExJ9tjhDrAd3HEPqARBCPmdpqxESPFqy44vkBQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-riscv64-musl@0.127.0':
+ resolution: {integrity: sha512-7IcC4Ao02oGpfnjt+X/oF4U2mllo2qoSkw5xxiXNKL9MCTsTiAC6616beOuehdxGcnz1bRoPC1RQ2f1GQDdN+g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-s390x-gnu@0.127.0':
+ resolution: {integrity: sha512-pbXIhiNFHoqWeqDNLiJ9JkpHz1IM9k4DXa66x+1GTWMG7iLxtkXgE53iiuKSXwmk3zIYmaPVfBvgcAhS583K4Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-x64-gnu@0.127.0':
+ resolution: {integrity: sha512-MYCguB9RvBvlSd6gbuNI7QwiLoCCAlGnlRJFPrzLI6U1/9wkC/WK6LtBAUln55H1Ctqw45PWmqrobKoMhsYQzQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-x64-musl@0.127.0':
+ resolution: {integrity: sha512-5eY0B/bxf1xIUxb4NOTvOI3KWtBQfPWYyKAzgcrCt0mDibSZygVpO1Pz8bkeiSZ5Jj9+M09dkggG3H8I5d0Uyg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxc-parser/binding-openharmony-arm64@0.127.0':
+ resolution: {integrity: sha512-Gld0ajrFTUXNtdw20fVBuTQx66FA75nIVg+//pPfR3sXkuABB4mTBhl3r9JNzrJpgW//qiwxf0nWXUWGJSL3UQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxc-parser/binding-wasm32-wasi@0.127.0':
+ resolution: {integrity: sha512-T6KVD7rhLzFlwGRXMnxUFfkCZD8FHnb968wVXW1mXzgRFc5RNXOBY2mPPDZ77x5Ln76ltLMgtPg0cOkU1NSrEQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [wasm32]
+
+ '@oxc-parser/binding-win32-arm64-msvc@0.127.0':
+ resolution: {integrity: sha512-Ujvw4X+LD1CCGULcsQcvb4YNVoBGqt+JHgNNzGGaCImELiZLk477ifUH53gIbE7EKd933NdTi25JWEr9K2HwXw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxc-parser/binding-win32-ia32-msvc@0.127.0':
+ resolution: {integrity: sha512-0cwxKO7KHQQQfo4Uf4B2SQrhgm+cJaP9OvFFhx52Tkg4bezsacu83GB2/In5bC415Ueeym+kXdnge/57rbSfTw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxc-parser/binding-win32-x64-msvc@0.127.0':
+ resolution: {integrity: sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxc-project/types@0.127.0':
+ resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==}
+
+ '@oxc-project/types@0.133.0':
+ resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+
+ '@oxc-resolver/binding-android-arm-eabi@11.20.0':
+ resolution: {integrity: sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==}
+ cpu: [arm]
+ os: [android]
+
+ '@oxc-resolver/binding-android-arm64@11.20.0':
+ resolution: {integrity: sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxc-resolver/binding-darwin-arm64@11.20.0':
+ resolution: {integrity: sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxc-resolver/binding-darwin-x64@11.20.0':
+ resolution: {integrity: sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxc-resolver/binding-freebsd-x64@11.20.0':
+ resolution: {integrity: sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0':
+ resolution: {integrity: sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0':
+ resolution: {integrity: sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-arm64-gnu@11.20.0':
+ resolution: {integrity: sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-arm64-musl@11.20.0':
+ resolution: {integrity: sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0':
+ resolution: {integrity: sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0':
+ resolution: {integrity: sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-riscv64-musl@11.20.0':
+ resolution: {integrity: sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-s390x-gnu@11.20.0':
+ resolution: {integrity: sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-x64-gnu@11.20.0':
+ resolution: {integrity: sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-x64-musl@11.20.0':
+ resolution: {integrity: sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxc-resolver/binding-openharmony-arm64@11.20.0':
+ resolution: {integrity: sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxc-resolver/binding-wasm32-wasi@11.20.0':
+ resolution: {integrity: sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@oxc-resolver/binding-win32-arm64-msvc@11.20.0':
+ resolution: {integrity: sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxc-resolver/binding-win32-x64-msvc@11.20.0':
+ resolution: {integrity: sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==}
+ cpu: [x64]
+ os: [win32]
'@peculiar/asn1-cms@2.6.0':
resolution: {integrity: sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA==}
@@ -2372,97 +2631,97 @@ packages:
react: ^16.8.0 || 17.x
react-dom: ^16.8.0 || 17.x
- '@rolldown/binding-android-arm64@1.0.0-rc.15':
- resolution: {integrity: sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==}
+ '@rolldown/binding-android-arm64@1.0.3':
+ resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
- '@rolldown/binding-darwin-arm64@1.0.0-rc.15':
- resolution: {integrity: sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==}
+ '@rolldown/binding-darwin-arm64@1.0.3':
+ resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@rolldown/binding-darwin-x64@1.0.0-rc.15':
- resolution: {integrity: sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==}
+ '@rolldown/binding-darwin-x64@1.0.3':
+ resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@rolldown/binding-freebsd-x64@1.0.0-rc.15':
- resolution: {integrity: sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==}
+ '@rolldown/binding-freebsd-x64@1.0.3':
+ resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15':
- resolution: {integrity: sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==}
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
+ resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==}
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
+ resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
- '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15':
- resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==}
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
+ resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
- '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==}
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
+ resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
- '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==}
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
+ resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
- '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==}
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
+ resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
- '@rolldown/binding-linux-x64-musl@1.0.0-rc.15':
- resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==}
+ '@rolldown/binding-linux-x64-musl@1.0.3':
+ resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
- '@rolldown/binding-openharmony-arm64@1.0.0-rc.15':
- resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==}
+ '@rolldown/binding-openharmony-arm64@1.0.3':
+ resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@rolldown/binding-wasm32-wasi@1.0.0-rc.15':
- resolution: {integrity: sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==}
- engines: {node: '>=14.0.0'}
+ '@rolldown/binding-wasm32-wasi@1.0.3':
+ resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [wasm32]
- '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15':
- resolution: {integrity: sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==}
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
+ resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
- '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15':
- resolution: {integrity: sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==}
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
+ resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
- '@rolldown/pluginutils@1.0.0-rc.15':
- resolution: {integrity: sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==}
+ '@rolldown/pluginutils@1.0.1':
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
'@rollup/pluginutils@5.3.0':
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
@@ -2627,17 +2886,24 @@ packages:
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
- '@storybook/addon-docs@10.3.5':
- resolution: {integrity: sha512-WuHbxia/o5TX4Rg/IFD0641K5qId/Nk0dxhmAUNoFs5L0+yfZUwh65XOBbzXqrkYmYmcVID4v7cgDRmzstQNkA==}
+ '@storybook/addon-docs@10.4.2':
+ resolution: {integrity: sha512-CtW1O4xSKZPNtpWgpfp4yB/x4pj/of+3MvlEDfErSlr3Hp3QmEa2pCLaecR08H5LJqJFlt1PtG0UrIynTvgW9w==}
peerDependencies:
- storybook: ^10.3.5
-
- '@storybook/addon-links@10.3.5':
- resolution: {integrity: sha512-Xe2wCGZ+hpZ0cDqAIBHk+kPc8nODNbu585ghd5bLrlYJMDVXoNM/fIlkrLgjIDVbfpgeJLUEg7vldJrn+FyOLw==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.3.5
+ '@types/react': ^17.0.37
+ storybook: ^10.4.2
peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@storybook/addon-links@10.4.2':
+ resolution: {integrity: sha512-cU8h4/m+oAr8UUwF4teZG2N1ilV+vU+98Ii/Ma+IIx9M/V7i5544UxfAz84dV5Rx2Oho6x8XH3gIvmevSyPi/Q==}
+ peerDependencies:
+ '@types/react': ^17.0.37
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ storybook: ^10.4.2
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
react:
optional: true
@@ -2653,26 +2919,26 @@ packages:
peerDependencies:
storybook: ^9.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0
- '@storybook/builder-webpack5@10.3.5':
- resolution: {integrity: sha512-DYjIpfuwkl8CrDbYWjMcwxrLY3QpcZtDJr4ZcT3hrbZHF5BJ3HnVIv1YM+KF/bJfIUMS2h/YMsRyKVYGthiSzQ==}
+ '@storybook/builder-webpack5@10.4.2':
+ resolution: {integrity: sha512-nhmV0+nThCgy1y5742SS7c4vJrd5/1KfCXCNfsJ1v4Rkq7NIQnUhEIBwkSaY63lqH7FRHlFxIjwGS63veiCJuw==}
peerDependencies:
- storybook: ^10.3.5
+ storybook: ^10.4.2
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- '@storybook/core-webpack@10.3.5':
- resolution: {integrity: sha512-CEtGU2f6+FefIR3v4P1KBJB17UngZDSmib2w36jfVp1pNPIzqdIG2s1NCKAM7vbQHxXVcLpBH31mJqyU+vdypQ==}
+ '@storybook/core-webpack@10.4.2':
+ resolution: {integrity: sha512-qnYKMruU8lvI4yaq2PA9Gmxjrc7EZ3DRBI/cVKwEgOIREoxzr1F1IE7t7+325k9Phylue7E5rD3A7yjxeEKUyw==}
peerDependencies:
- storybook: ^10.3.5
+ storybook: ^10.4.2
- '@storybook/csf-plugin@10.3.5':
- resolution: {integrity: sha512-qlEzNKxOjq86pvrbuMwiGD/bylnsXk1dg7ve0j77YFjEEchqtl7qTlrXvFdNaLA89GhW6D/EV6eOCu/eobPDgw==}
+ '@storybook/csf-plugin@10.4.2':
+ resolution: {integrity: sha512-GqX/2DeF3/jKs5D7gpDiuT9gd0c/f2TKcnQ5av4/s3YqeN+0nhm7btkCrDfgF16uzE1Zj3OrkxvB3AOkfxWgDg==}
peerDependencies:
esbuild: '*'
rollup: '*'
- storybook: ^10.3.5
+ storybook: ^10.4.2
vite: '*'
webpack: '*'
peerDependenciesMeta:
@@ -2688,18 +2954,18 @@ packages:
'@storybook/global@5.0.0':
resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
- '@storybook/icons@2.0.1':
- resolution: {integrity: sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==}
+ '@storybook/icons@2.0.2':
+ resolution: {integrity: sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- '@storybook/preset-react-webpack@10.3.5':
- resolution: {integrity: sha512-PAlh2nJOY+yxYBUBAurWOdd+1RGhl8e5MhpC8hTNhaTB8//WKTpUAOyM8Q1PvflCYKU9Hz11nP4Q4jY1WVEoUA==}
+ '@storybook/preset-react-webpack@10.4.2':
+ resolution: {integrity: sha512-21ld380f0/jTTitkfhTKgP3FBnVAgMu1P1ymrRyiFYJVSJBA5YejndFFBo0ugq9iGGsHXrVdOphC/OJKbTSWRQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.3.5
+ storybook: ^10.4.2
typescript: '*'
peerDependenciesMeta:
typescript:
@@ -2711,32 +2977,45 @@ packages:
typescript: '>= 4.x'
webpack: '>= 4'
- '@storybook/react-dom-shim@10.3.5':
- resolution: {integrity: sha512-Gw8R7XZm0zSUH0XAuxlQJhmizsLzyD6x00KOlP6l7oW9eQHXGfxg3seNDG3WrSAcW07iP1/P422kuiriQlOv7g==}
+ '@storybook/react-dom-shim@10.4.2':
+ resolution: {integrity: sha512-Eng3Yt2NCjPX94QcfyLeUFhrMj0hec2yU9J/qafBVbfj9XrFI8o+0ZwYJ7uXb9ECbvPN4y06dgt/2W/LiR417w==}
peerDependencies:
+ '@types/react': ^17.0.37
+ '@types/react-dom': ^17.0.11
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.3.5
+ storybook: ^10.4.2
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
- '@storybook/react-webpack5@10.3.5':
- resolution: {integrity: sha512-g5nxwFBVjm59IDG8qu0mnIno7DJHKvBuJvwO/HyHSuaKvmNRkDCJ8mDegPhpaEwmHPX4dg1GDDUjZ4fwFbQWbA==}
+ '@storybook/react-webpack5@10.4.2':
+ resolution: {integrity: sha512-x7xwGLxU0w6/qi29/cHhua8qiCvfE05ku4pPLTXF8TsP/zfGsY8tbdlKO2+YKp+iBG8vafVc//ZXOAty1oypDA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.3.5
+ storybook: ^10.4.2
typescript: '>= 4.9.x'
peerDependenciesMeta:
typescript:
optional: true
- '@storybook/react@10.3.5':
- resolution: {integrity: sha512-tpLTLaVGoA6fLK3ReyGzZUricq7lyPaV2hLPpj5wqdXLV/LpRtAHClUpNoPDYSBjlnSjL81hMZijbkGC3mA+gw==}
+ '@storybook/react@10.4.2':
+ resolution: {integrity: sha512-NfEH3CrdCAgUV4Z7SPN3Iw6nofcueqtRj8iHuo77GNjz0qSfuVi9iS7a8o7x7QFSeIBZwS0Jv3CgmhN8qvoLjg==}
peerDependencies:
+ '@types/react': ^17.0.37
+ '@types/react-dom': ^17.0.11
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.3.5
+ storybook: ^10.4.2
typescript: '>= 4.9.x'
peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
typescript:
optional: true
@@ -2965,12 +3244,6 @@ packages:
react: <18.0.0
react-dom: <18.0.0
- '@testing-library/user-event@14.5.2':
- resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
- engines: {node: '>=12', npm: '>=6'}
- peerDependencies:
- '@testing-library/dom': '>=7.21.4'
-
'@testing-library/user-event@14.6.1':
resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
engines: {node: '>=12', npm: '>=6'}
@@ -3008,9 +3281,6 @@ packages:
'@types/babel__template@7.4.1':
resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
- '@types/babel__traverse@7.20.4':
- resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==}
-
'@types/babel__traverse@7.28.0':
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
@@ -3035,18 +3305,9 @@ packages:
'@types/doctrine@0.0.9':
resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==}
- '@types/eslint-scope@3.7.7':
- resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
-
- '@types/eslint@9.6.1':
- resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
-
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
- '@types/estree@1.0.9':
- resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
-
'@types/express-serve-static-core@4.17.34':
resolution: {integrity: sha512-fvr49XlCGoUj2Pp730AItckfjat4WNb0lb3kfrLWffd+RLeoGAMsq7UOy04PAPtoL01uKwcp6u8nhzpgpDYr3w==}
@@ -3069,6 +3330,11 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ '@types/hoist-non-react-statics@3.3.7':
+ resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==}
+ peerDependencies:
+ '@types/react': ^17.0.37
+
'@types/html-minifier-terser@6.1.0':
resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==}
@@ -3099,8 +3365,8 @@ packages:
'@types/lodash.memoize@4.1.9':
resolution: {integrity: sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw==}
- '@types/lodash@4.17.21':
- resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==}
+ '@types/lodash@4.17.24':
+ resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -3123,6 +3389,9 @@ packages:
'@types/nprogress@0.2.3':
resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
+ '@types/parse-json@4.0.2':
+ resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
+
'@types/prop-types@15.7.4':
resolution: {integrity: sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==}
@@ -3174,6 +3443,9 @@ packages:
'@types/serve-static@1.15.10':
resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==}
+ '@types/set-cookie-parser@2.4.10':
+ resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==}
+
'@types/sizzle@2.3.10':
resolution: {integrity: sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==}
@@ -3201,63 +3473,100 @@ packages:
'@types/yargs@16.0.4':
resolution: {integrity: sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==}
- '@typescript-eslint/eslint-plugin@8.58.2':
- resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==}
+ '@typescript-eslint/eslint-plugin@8.60.1':
+ resolution: {integrity: sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.58.2
+ '@typescript-eslint/parser': ^8.60.1
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/parser@8.58.2':
- resolution: {integrity: sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==}
+ '@typescript-eslint/parser@8.60.1':
+ resolution: {integrity: sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/project-service@8.58.2':
- resolution: {integrity: sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==}
+ '@typescript-eslint/project-service@8.58.1':
+ resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/scope-manager@8.58.2':
- resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@typescript-eslint/tsconfig-utils@8.58.2':
- resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==}
+ '@typescript-eslint/project-service@8.60.1':
+ resolution: {integrity: sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/type-utils@8.58.2':
- resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==}
+ '@typescript-eslint/scope-manager@8.58.1':
+ resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/scope-manager@8.60.1':
+ resolution: {integrity: sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.58.1':
+ resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/tsconfig-utils@8.60.1':
+ resolution: {integrity: sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/type-utils@8.60.1':
+ resolution: {integrity: sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/types@8.58.2':
- resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==}
+ '@typescript-eslint/types@8.58.1':
+ resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.58.2':
- resolution: {integrity: sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==}
+ '@typescript-eslint/types@8.60.1':
+ resolution: {integrity: sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.58.1':
+ resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/utils@8.58.2':
- resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==}
+ '@typescript-eslint/typescript-estree@8.60.1':
+ resolution: {integrity: sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/utils@8.58.1':
+ resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/visitor-keys@8.58.2':
- resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==}
+ '@typescript-eslint/utils@8.60.1':
+ resolution: {integrity: sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/visitor-keys@8.58.1':
+ resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/visitor-keys@8.60.1':
+ resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@uirouter/angularjs@1.0.11':
@@ -3296,8 +3605,19 @@ packages:
'@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
- '@uiw/codemirror-themes@4.23.7':
- resolution: {integrity: sha512-UNf1XOx1hG9OmJnrtT86PxKcdcwhaNhbrcD+nsk8WxRJ3n5c8nH6euDvgVPdVLPwbizsaQcZTILACgA/FjRpVg==}
+ '@uiw/codemirror-extensions-basic-setup@4.25.10':
+ resolution: {integrity: sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==}
+ peerDependencies:
+ '@codemirror/autocomplete': '>=6.0.0'
+ '@codemirror/commands': '>=6.0.0'
+ '@codemirror/language': '>=6.0.0'
+ '@codemirror/lint': '>=6.0.0'
+ '@codemirror/search': '>=6.0.0'
+ '@codemirror/state': '>=6.0.0'
+ '@codemirror/view': '>=6.0.0'
+
+ '@uiw/codemirror-themes@4.25.10':
+ resolution: {integrity: sha512-Fqiz1HIuDlDftcL+/O53V333UOH6MqQ84VbiQB5egn6u+uDwAqACp1FrdAoi4wgpR3b3TGW4Gr0wIYcrJSSz1A==}
peerDependencies:
'@codemirror/language': '>=6.0.0'
'@codemirror/state': '>=6.0.0'
@@ -3314,8 +3634,20 @@ packages:
react: '>=16.8.0'
react-dom: '>=16.8.0'
+ '@uiw/react-codemirror@4.25.10':
+ resolution: {integrity: sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==}
+ peerDependencies:
+ '@babel/runtime': '>=7.11.0'
+ '@codemirror/state': '>=6.0.0'
+ '@codemirror/theme-one-dark': '>=6.0.0'
+ '@codemirror/view': '>=6.0.0'
+ codemirror: '>=6.0.0'
+ react: '>=17.0.0'
+ react-dom: '>=17.0.0'
+
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+ deprecated: Potential CWE-502 - Update to 1.3.1 or higher
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
@@ -3421,8 +3753,8 @@ packages:
'@vitest/browser':
optional: true
- '@vitest/eslint-plugin@1.6.19':
- resolution: {integrity: sha512-zodmXRsVKFsuHxHJILuTFaaKsrsxm0YsiOX65clk+LpCW9JrVXaf6ERXr0caDs+NEk0S62Jyk0K7XYQ7gWXheA==}
+ '@vitest/eslint-plugin@1.6.15':
+ resolution: {integrity: sha512-dTMjrdngmcB+DxomlKQ+SUubCTvd0m2hQQFpv5sx+GRodmeoxr2PVbphk57SVp250vpxphk9Ccwyv6fQ6+2gkA==}
engines: {node: '>=18'}
peerDependencies:
'@typescript-eslint/eslint-plugin': '*'
@@ -3609,6 +3941,15 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@6.0.2:
+ resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+ engines: {node: '>= 6.0.0'}
+
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
@@ -3830,8 +4171,8 @@ packages:
resolution: {integrity: sha512-rAoaXKKj7bgZZI4RhpSTimgBv92hea1J9sygmwTFdy7fu8cP1/+xx6vCmW24+z52bmqVluKAJcVIPQ7ancXilA==}
engines: {node: '>=12'}
- autoprefixer@10.4.16:
- resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==}
+ autoprefixer@10.5.0:
+ resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==}
engines: {node: ^10 || ^12 || >=14}
hasBin: true
peerDependencies:
@@ -3861,8 +4202,8 @@ packages:
peerDependencies:
axios: ^1.6.2
- axios@1.13.5:
- resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==}
+ axios@1.16.1:
+ resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
@@ -3885,6 +4226,10 @@ packages:
babel-plugin-lodash@3.3.4:
resolution: {integrity: sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==}
+ babel-plugin-macros@3.1.0:
+ resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
+ engines: {node: '>=10', npm: '>=6'}
+
babel-plugin-polyfill-corejs2@0.4.7:
resolution: {integrity: sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==}
peerDependencies:
@@ -3910,8 +4255,9 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
- baseline-browser-mapping@2.9.11:
- resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
+ baseline-browser-mapping@2.10.33:
+ resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==}
+ engines: {node: '>=6.0.0'}
hasBin: true
batch@0.6.1:
@@ -3961,8 +4307,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
- browserslist@4.28.1:
- resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ browserslist@4.28.2:
+ resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -4043,8 +4389,8 @@ packages:
caniuse-lite@1.0.30001751:
resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==}
- caniuse-lite@1.0.30001762:
- resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==}
+ caniuse-lite@1.0.30001793:
+ resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==}
case-sensitive-paths-webpack-plugin@2.4.0:
resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==}
@@ -4177,8 +4523,8 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
- codemirror-json-schema@0.8.0:
- resolution: {integrity: sha512-Hiww3eU/8zwPw6oWT2VNTT7w7vwWzFiE7+syleD8rYg5pCfKuGa2oiAAgZCXpx6EUJtIcq3LKP9gkEbNUD5jcA==}
+ codemirror-json-schema@0.8.1:
+ resolution: {integrity: sha512-4lKPjW+nugNAmM5MsggJyn6TUxYdCCwAJIr9T4cZeTFPdkbBvPteCOGtDedrTOIeTC2ZFJtVg7VHIXnYU32t8w==}
peerDependencies:
'@codemirror/language': ^6.10.2
'@codemirror/lint': ^6.8.0
@@ -4209,9 +4555,6 @@ packages:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
hasBin: true
- colord@2.9.2:
- resolution: {integrity: sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==}
-
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
@@ -4230,6 +4573,10 @@ packages:
resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==}
engines: {node: '>=16'}
+ commander@11.1.0:
+ resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
+ engines: {node: '>=16'}
+
commander@12.1.0:
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
engines: {node: '>=18'}
@@ -4285,6 +4632,9 @@ packages:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
+ convert-source-map@1.9.0:
+ resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -4322,6 +4672,10 @@ packages:
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+ cosmiconfig@7.1.0:
+ resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
+ engines: {node: '>=10'}
+
cosmiconfig@8.2.0:
resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==}
engines: {node: '>=14'}
@@ -4336,12 +4690,6 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
- css-declaration-sorter@6.4.0:
- resolution: {integrity: sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==}
- engines: {node: ^10 || ^12 || >=14}
- peerDependencies:
- postcss: ^8.0.9
-
css-loader@6.8.1:
resolution: {integrity: sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==}
engines: {node: '>= 12.13.0'}
@@ -4374,6 +4722,10 @@ packages:
resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
@@ -4386,23 +4738,23 @@ packages:
engines: {node: '>=4'}
hasBin: true
- cssnano-preset-default@6.0.1:
- resolution: {integrity: sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==}
- engines: {node: ^14 || ^16 || >=18.0}
+ cssnano-preset-default@8.0.1:
+ resolution: {integrity: sha512-OTdKeYMlvQ8KBgyej5ysktnWJoeyo7rGrVnm+bdpIHGvxhbTGPsOkB+7T1EdTuX00dGlQQb2UEbSPB1OpMXULw==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- cssnano-utils@4.0.0:
- resolution: {integrity: sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ cssnano-utils@6.0.0:
+ resolution: {integrity: sha512-ztS9W/+uaDn+bkYmDhs+GdMveHJ3CL8IPNHpRqDUQXv5GJOTQAJjV1XUOInr9esLXSabQV1pLRZlJpyUwEqDyQ==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- cssnano@6.0.1:
- resolution: {integrity: sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==}
- engines: {node: ^14 || ^16 || >=18.0}
+ cssnano@8.0.1:
+ resolution: {integrity: sha512-oSiOnPQNNYjusTUlYJiE6xvFQG4don3N0QavaoV1BxIsC1zjvxOwikXlR7lG1EVmZNDDaJkHbQx1VRB8kaoMHA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
csso@5.0.5:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
@@ -4682,8 +5034,8 @@ packages:
electron-to-chromium@1.4.616:
resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==}
- electron-to-chromium@1.5.267:
- resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
+ electron-to-chromium@1.5.364:
+ resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==}
emoji-regex-xs@1.0.0:
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
@@ -4705,8 +5057,8 @@ packages:
endent@2.1.0:
resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==}
- enhanced-resolve@5.19.0:
- resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==}
+ enhanced-resolve@5.22.1:
+ resolution: {integrity: sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==}
engines: {node: '>=10.13.0'}
entities@2.1.0:
@@ -4794,10 +5146,6 @@ packages:
engines: {node: '>=18'}
hasBin: true
- escalade@3.1.1:
- resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
- engines: {node: '>=6'}
-
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -4893,17 +5241,17 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
- eslint-plugin-promise@7.2.1:
- resolution: {integrity: sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==}
+ eslint-plugin-promise@7.3.0:
+ resolution: {integrity: sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+ eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
- eslint-plugin-react-hooks@7.0.1:
- resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==}
+ eslint-plugin-react-hooks@7.1.1:
+ resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==}
engines: {node: '>=18'}
peerDependencies:
- eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0
eslint-plugin-react@7.37.5:
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
@@ -4917,11 +5265,11 @@ packages:
peerDependencies:
eslint: '>=4.0.0'
- eslint-plugin-storybook@10.3.5:
- resolution: {integrity: sha512-rEFkfU3ypF44GpB4tiJ9EFDItueoGvGi3+weLHZax2ON2MB7VIDsxdSUGvIU5tMURg+oWYlpzCyLm4TpDq2deA==}
+ eslint-plugin-storybook@10.4.2:
+ resolution: {integrity: sha512-l3/vzLRmb8VSi3X1Bo6/Pa+64naw1jFsZE5jPPA4izvVdNhH1rF4rGuOC3kDTU926qKVBQtKua8D24XWQtvcGg==}
peerDependencies:
eslint: '>=8'
- storybook: ^10.3.5
+ storybook: ^10.4.2
eslint-scope@5.1.1:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
@@ -5045,9 +5393,18 @@ packages:
fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+ fast-string-truncated-width@3.0.3:
+ resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
+
+ fast-string-width@3.0.2:
+ resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==}
+
fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
+ fast-wrap-ansi@0.2.2:
+ resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==}
+
fastest-levenshtein@1.0.12:
resolution: {integrity: sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==}
@@ -5100,6 +5457,9 @@ packages:
resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==}
engines: {node: '>=14.16'}
+ find-root@1.1.0:
+ resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
+
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -5134,8 +5494,8 @@ packages:
resolution: {integrity: sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==}
engines: {node: '>=10'}
- follow-redirects@1.15.11:
- resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+ follow-redirects@1.16.0:
+ resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
@@ -5169,8 +5529,8 @@ packages:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
- formik@2.2.9:
- resolution: {integrity: sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==}
+ formik@2.4.9:
+ resolution: {integrity: sha512-5nI94BMnlFDdQRBY4Sz39WkhxajZJ57Fzs8wVbtsQlm5ScKIR1QLYqv/ultBnobObtlUyxpxoLodpixrsf36Og==}
peerDependencies:
react: '>=16.8.0'
@@ -5178,8 +5538,8 @@ packages:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
- fraction.js@4.3.6:
- resolution: {integrity: sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==}
+ fraction.js@5.3.4:
+ resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
fresh@0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
@@ -5322,8 +5682,8 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
- graphql@16.12.0:
- resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==}
+ graphql@16.14.1:
+ resolution: {integrity: sha512-cQOsSMS/IrDz82PVyRDvf/Q1F/bRbBVjJlh+xYOkI1qw2bWRvWGiWc+m2O0d6l4Bt1fyY+8kzJ8JFWGJqNeDBg==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
growly@1.3.0:
@@ -5383,8 +5743,8 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
- headers-polyfill@4.0.2:
- resolution: {integrity: sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==}
+ headers-polyfill@5.0.1:
+ resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==}
hermes-estree@0.25.1:
resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
@@ -5455,10 +5815,6 @@ packages:
resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==}
engines: {node: '>= 0.6'}
- http-errors@2.0.0:
- resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
- engines: {node: '>= 0.8'}
-
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@@ -5489,6 +5845,10 @@ packages:
http2-client@1.3.5:
resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==}
+ https-proxy-agent@5.0.1:
+ resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+ engines: {node: '>= 6'}
+
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
@@ -5886,8 +6246,8 @@ packages:
resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==}
hasBin: true
- jiti@2.6.1:
- resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
jquery@3.6.0:
@@ -6089,6 +6449,10 @@ packages:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'}
+ lilconfig@3.1.3:
+ resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+ engines: {node: '>=14'}
+
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -6109,8 +6473,8 @@ packages:
enquirer:
optional: true
- loader-runner@4.3.1:
- resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==}
+ loader-runner@4.3.2:
+ resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==}
engines: {node: '>=6.11.5'}
loader-utils@1.4.2:
@@ -6161,8 +6525,11 @@ packages:
lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
- lodash@4.17.21:
- resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+ lodash@4.17.23:
+ resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
+
+ lodash@4.18.1:
+ resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
log-update@5.0.1:
resolution: {integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==}
@@ -6242,6 +6609,9 @@ packages:
mdn-data@2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
mdurl@1.0.1:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
@@ -6259,8 +6629,8 @@ packages:
peerDependencies:
tslib: '2'
- memoize-one@5.2.1:
- resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+ memoize-one@6.0.0:
+ resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
@@ -6354,11 +6724,11 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
- moment-timezone@0.5.41:
- resolution: {integrity: sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==}
+ moment-timezone@0.6.2:
+ resolution: {integrity: sha512-lDsQv8FoGdBUdf0+TjGsq2orxKuXdwFlQ6Zw6TX3xIcTwTfEpCLyKqvEauvCHJ8iu3KBV8+uPhlv70YsNGdUBQ==}
- moment@2.29.4:
- resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
+ moment@2.30.1:
+ resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
moo@0.5.2:
resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==}
@@ -6381,8 +6751,8 @@ packages:
peerDependencies:
msw: ^2.0.0
- msw@2.12.10:
- resolution: {integrity: sha512-G3VUymSE0/iegFnuipujpwyTM2GuZAKXNeerUSrG2+Eg391wW63xFs5ixWsK9MWzr1AGoSkYGmyAzNgbR3+urw==}
+ msw@2.14.6:
+ resolution: {integrity: sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
@@ -6399,9 +6769,9 @@ packages:
resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
hasBin: true
- mute-stream@2.0.0:
- resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ mute-stream@3.0.0:
+ resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==}
+ engines: {node: ^20.17.0 || >=22.9.0}
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
@@ -6409,8 +6779,8 @@ packages:
nanoclone@0.2.1:
resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==}
- nanoid@3.3.11:
- resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ nanoid@3.3.12:
+ resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
@@ -6426,8 +6796,8 @@ packages:
resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==}
hasBin: true
- needle@3.5.0:
- resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==}
+ needle@3.3.1:
+ resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
engines: {node: '>= 4.4.x'}
hasBin: true
@@ -6480,17 +6850,14 @@ packages:
node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
- node-releases@2.0.27:
- resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+ node-releases@2.0.46:
+ resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==}
+ engines: {node: '>=18'}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
- normalize-range@0.1.2:
- resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
- engines: {node: '>=0.10.0'}
-
npm-run-path@5.1.0:
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -6628,6 +6995,13 @@ packages:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
+ oxc-parser@0.127.0:
+ resolution: {integrity: sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
+ oxc-resolver@11.20.0:
+ resolution: {integrity: sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==}
+
p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
@@ -6751,10 +7125,6 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
- picomatch@4.0.3:
- resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
- engines: {node: '>=12'}
-
picomatch@4.0.4:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
@@ -6792,8 +7162,8 @@ packages:
resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==}
engines: {node: '>=14.16'}
- pkg-types@2.3.0:
- resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+ pkg-types@2.3.1:
+ resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
pkijs@3.3.3:
resolution: {integrity: sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==}
@@ -6803,47 +7173,47 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
- postcss-calc@9.0.1:
- resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-calc@10.1.1:
+ resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==}
+ engines: {node: ^18.12 || ^20.9 || >=22.0}
peerDependencies:
- postcss: ^8.2.2
+ postcss: ^8.4.38
- postcss-colormin@6.0.0:
- resolution: {integrity: sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-colormin@8.0.0:
+ resolution: {integrity: sha512-KKwMmsSgsmdYXqrjQeqL3tnuIFtctiR1GEMHdjNpDpz/TCRkkkok2mMcreK2zVV3l7POWOmAkR2xYHUpRUK1DA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-convert-values@6.0.0:
- resolution: {integrity: sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-convert-values@8.0.0:
+ resolution: {integrity: sha512-Ohtj3rNZWawTRePv5NCHTy8VJSdJ/G/uKuxcxJreOMichuqcT6uEl2TAnopVeJCJ/c13jaSqg7m63yFLM5zBsA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-discard-comments@6.0.0:
- resolution: {integrity: sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-discard-comments@8.0.0:
+ resolution: {integrity: sha512-zGpvVLj2sbagEp+BTVETvAfkZdGVA6rALNujDK/WTIjdf1/rQOxOG8BBzkI8UQgnw8SkL6xffAfbtGMHFypadw==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-discard-duplicates@6.0.0:
- resolution: {integrity: sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-discard-duplicates@8.0.0:
+ resolution: {integrity: sha512-zjRyYmNGI3PTipKBBtCgExlmZXQn49KvKoaiNnR2g+iXxeNk7GY5Js2ULtZXPrCYeqjPagrzKIBNcBocvXCR7g==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-discard-empty@6.0.0:
- resolution: {integrity: sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-discard-empty@8.0.0:
+ resolution: {integrity: sha512-kxPJg6EqahbBvm+l7hpYYCtpsv8dlz7Tv6wJXUXZaeuY0WGS61DxfGdZR4uVB/Cx+yi3iOHQVSqpSHKMFaBg6Q==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-discard-overridden@6.0.0:
- resolution: {integrity: sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-discard-overridden@8.0.0:
+ resolution: {integrity: sha512-sW2OWH3l9p0FmBSVr228uztFseqroZxwgD7SGF0Ks0dRPDttSo3P8FK5ZBLtWBH2A5+chpB0J2fB/T8heKHLBw==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
postcss-import@15.1.0:
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
@@ -6876,41 +7246,41 @@ packages:
postcss: ^7.0.0 || ^8.0.1
webpack: ^5.0.0
- postcss-merge-longhand@6.0.0:
- resolution: {integrity: sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-merge-longhand@8.0.0:
+ resolution: {integrity: sha512-YDmAmQ8H+ljfomVpSXvr9NA0GP01fraQJqjWBYoMVGg6rOT+PJLwPyeVo2ekn4WB4ZVSH5ddtK3DTRxbz6CFzg==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-merge-rules@6.0.1:
- resolution: {integrity: sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-merge-rules@8.0.0:
+ resolution: {integrity: sha512-bgstL5mpi41dDpnYGDUcI3M814NWkCMcIWpwDqEHXkHg3BT7b4XRAfNEuwJncZOVn/67kVKvWzhfv/7xyrp2uQ==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-minify-font-values@6.0.0:
- resolution: {integrity: sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-minify-font-values@8.0.0:
+ resolution: {integrity: sha512-EnOHQEnSt6oH5NrL1DMFAQuwB2IOimFXTCzc9bKfUeH1jREbqIF5MAK4gQJQOC4mPUwJt4sWifAmNZ1qLu6j3Q==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-minify-gradients@6.0.0:
- resolution: {integrity: sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-minify-gradients@8.0.0:
+ resolution: {integrity: sha512-43iAnYIGk0ZjNx5X/rkIcHi6dhmu/vEjY0kqfUfxPuJRO+V7jx8uKIdcnL0dpfNoC5J9TSh3EtzLWbq0gpqnWA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-minify-params@6.0.0:
- resolution: {integrity: sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-minify-params@8.0.0:
+ resolution: {integrity: sha512-z7w4QO7G55l4vMUK1Lmx03GW7iyRLgf2V5Dz/7ioSPLnXRjeD+b7m0XfAXUGrbBYYrJ6bXPk+3LoX5u4JfAcSg==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-minify-selectors@6.0.0:
- resolution: {integrity: sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-minify-selectors@8.0.1:
+ resolution: {integrity: sha512-c31D46811kTkQDxV1KTTow79axX6gj/01AY5G7cGZg3s31KvAwP13jEFXGAzQbJ7NvOFV1pRqEia6nrAdHU7qg==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
postcss-modules-extract-imports@3.0.0:
resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
@@ -6960,77 +7330,77 @@ packages:
peerDependencies:
postcss: ^8.2.14
- postcss-normalize-charset@6.0.0:
- resolution: {integrity: sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-charset@8.0.0:
+ resolution: {integrity: sha512-s88FUNDSUD8m0wBYvTQQcubVts6zhXwBU8zCD4vkRKiecd0v8cOjHVIF9r/i+5xzS/WG3f98qq4XsOM0JqvfLA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-display-values@6.0.0:
- resolution: {integrity: sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-display-values@8.0.0:
+ resolution: {integrity: sha512-gG2nBxD27fiw6Luinb1QYKdM/Co5GornRJgSD+JTwNH4PGKxImP0qyruDDav49aHUPLY3qrL3qN3LvybO7IzxQ==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-positions@6.0.0:
- resolution: {integrity: sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-positions@8.0.0:
+ resolution: {integrity: sha512-t/wGqpehS20Ke7kc4QAsWpH+AJjUdMK/V5qV2RhrXkj8hO/fT1t1MJ8NL7sedWYk7ZqC7eISEJQonW5j0tU1MQ==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-repeat-style@6.0.0:
- resolution: {integrity: sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-repeat-style@8.0.0:
+ resolution: {integrity: sha512-3ebOmGdCYKrBYyGKc1xhj0unEnW7beZpVU7JohVeGl7mTxR+7T6egpaawTWAVsB0pEIhcsbJVOjPKCJSoRO6Zg==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-string@6.0.0:
- resolution: {integrity: sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-string@8.0.0:
+ resolution: {integrity: sha512-TvWCGZ/e04Tv31uJvOUtbexkfgUnqmQ3M2P5DkAaVzvOj+BvTkG2QjpA5Y71SL1SPxJcj4M23fNh+RDVCmG8kA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-timing-functions@6.0.0:
- resolution: {integrity: sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-timing-functions@8.0.0:
+ resolution: {integrity: sha512-uEfaXst5Xgqxv7geYUuz6vs9mn88K2NPY2RoIzM3BMmSjsdTSeppV9x2qIgrxsisdbSqF6IVhzI2occcte3hTA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-unicode@6.0.0:
- resolution: {integrity: sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-unicode@8.0.0:
+ resolution: {integrity: sha512-+WYngZaChEeTHZmWhmKtnJ4gTzWdINEaFcgWBnu6WdVu8Ftim8OBTcw768DuCC/3Aax9bZ9WkwrLGHym2Lzf+A==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-url@6.0.0:
- resolution: {integrity: sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-url@8.0.0:
+ resolution: {integrity: sha512-4Mz9hZHn/QIB+YtFqTXrDmE2193GYxGb3F8uMfLvMicaEXCCUlDIJ658gFFJbqEGl9FYzwPtRiuNgbwlO9kkBg==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-normalize-whitespace@6.0.0:
- resolution: {integrity: sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-normalize-whitespace@8.0.0:
+ resolution: {integrity: sha512-V1f8tYnwIP5tscOXQFTKK8Y5EJ+R2GMpFJ6FjzwoKoQnhbqQy3IeSrDjJJb8JjVos8ut6Osi80Zybpayv/XjIQ==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-ordered-values@6.0.0:
- resolution: {integrity: sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-ordered-values@8.0.0:
+ resolution: {integrity: sha512-Dg9+itb6lmD0bxqhQyHCtXAwYRh0wUrx6Mp4/BNXgkLoJmdYMmWi+V+Pypw79Q6iQhxA8KFMHqLBITQJV2gKMA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-reduce-initial@6.0.0:
- resolution: {integrity: sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-reduce-initial@8.0.0:
+ resolution: {integrity: sha512-DChcE9d528AKrlpCTHjhsAiOsWCk4H9ApHPS1QqRT3praObWTiWyn6W1UddGpc46K9LQnHwUu4YwaPUukGtXVA==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-reduce-transforms@6.0.0:
- resolution: {integrity: sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-reduce-transforms@8.0.0:
+ resolution: {integrity: sha512-cLZT0som7vvumQT9XQCnSKOSnRinNQZd1Hm+J723Ney13E8CIydDhw6JwzsjPtgnYThTqn9Q45906gz6wxaAsw==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
postcss-selector-parser@6.0.13:
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
@@ -7040,27 +7410,23 @@ packages:
resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==}
engines: {node: '>=4'}
- postcss-svgo@6.0.0:
- resolution: {integrity: sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==}
- engines: {node: ^14 || ^16 || >= 18}
+ postcss-svgo@8.0.0:
+ resolution: {integrity: sha512-Q2fMSYEiNE1ioDc/3sxvI24NdgA/MJno2XLNpOxgv8aCcJbym8mZY10/lDY5+AWCIc3Aiqzy2Wcp9/zaIXBZgQ==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
- postcss-unique-selectors@6.0.0:
- resolution: {integrity: sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ postcss-unique-selectors@8.0.0:
+ resolution: {integrity: sha512-iObuolUX+ITJfMU2QQFQdh31JgSjNLPNjVs6YGAqBHvOvAWXMMNget6donQl83aQaeS32i5XeKZURUW/WBxIUw==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
- postcss@8.5.6:
- resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
- engines: {node: ^10 || ^12 || >=14}
-
- postcss@8.5.9:
- resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==}
+ postcss@8.5.15:
+ resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
powershell-utils@0.1.0:
@@ -7154,8 +7520,9 @@ packages:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
- proxy-from-env@1.1.0:
- resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+ proxy-from-env@2.1.0:
+ resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
+ engines: {node: '>=10'}
prr@1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
@@ -7348,8 +7715,8 @@ packages:
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
- react-json-view-lite@1.2.1:
- resolution: {integrity: sha512-Itc0g86fytOmKZoIoJyGgvNqohWSbh3NXIKNgH6W6FT9PC1ck4xas1tT3Rr/b3UlFXyA9Jjaw9QSXdZy2JwGMQ==}
+ react-json-view-lite@1.5.0:
+ resolution: {integrity: sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==}
engines: {node: '>=14'}
peerDependencies:
react: ^16.13.1 || ^17.0.0 || ^18.0.0
@@ -7380,11 +7747,11 @@ packages:
react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-select: ^5.0.0
- react-select@5.2.1:
- resolution: {integrity: sha512-OOyNzfKrhOcw/BlembyGWgdlJ2ObZRaqmQppPFut1RptJO423j+Y+JIsmxkvsZ4D/3CpOmwIlCvWbbAWEdh12A==}
+ react-select@5.10.2:
+ resolution: {integrity: sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==}
peerDependencies:
- react: ^16.8.0 || ^17.0.0
- react-dom: ^16.8.0 || ^17.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-style-singleton@2.2.3:
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
@@ -7566,8 +7933,8 @@ packages:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
- rettime@0.10.1:
- resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==}
+ rettime@0.11.11:
+ resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==}
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
@@ -7586,8 +7953,8 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
- rolldown@1.0.0-rc.15:
- resolution: {integrity: sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==}
+ rolldown@1.0.3:
+ resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
@@ -7700,9 +8067,6 @@ packages:
serialize-javascript@6.0.1:
resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==}
- serialize-javascript@6.0.2:
- resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
-
serve-index@1.9.1:
resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==}
engines: {node: '>= 0.8.0'}
@@ -7714,6 +8078,9 @@ packages:
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+ set-cookie-parser@3.1.0:
+ resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==}
+
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
@@ -7843,6 +8210,10 @@ packages:
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+ source-map@0.5.7:
+ resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
+ engines: {node: '>=0.10.0'}
+
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
@@ -7880,10 +8251,6 @@ packages:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
- statuses@2.0.1:
- resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
- engines: {node: '>= 0.8'}
-
statuses@2.0.2:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
@@ -7902,14 +8269,20 @@ packages:
storybook-css-modules-preset@1.1.1:
resolution: {integrity: sha512-wyINPOtB/8SvU7J92ePAhciBk4xoHuwrcqDNyGnSwilwHjmtHwbeEgZ1//JALOTwMB10zwz3WPONRkWec9LdGw==}
- storybook@10.3.5:
- resolution: {integrity: sha512-uBSZu/GZa9aEIW3QMGvdQPMZWhGxSe4dyRWU8B3/Vd47Gy/XLC7tsBxRr13txmmPOEDHZR94uLuq0H50fvuqBw==}
+ storybook@10.4.2:
+ resolution: {integrity: sha512-5Ax5vbHxFgMBGGhQDm75Rrumm/HZC4ICFhMcJaM0UlqnC/4FKj/IaZtImZFupknyiiyUEcWHPQFA2kX3/VSv1A==}
hasBin: true
peerDependencies:
+ '@types/react': ^17.0.37
prettier: ^2 || ^3
+ vite-plus: ^0.1.15
peerDependenciesMeta:
+ '@types/react':
+ optional: true
prettier:
optional: true
+ vite-plus:
+ optional: true
strict-event-emitter@0.5.1:
resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
@@ -8011,15 +8384,18 @@ packages:
style-mod@4.1.2:
resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
- stylehacks@6.0.0:
- resolution: {integrity: sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==}
- engines: {node: ^14 || ^16 || >=18.0}
+ stylehacks@8.0.0:
+ resolution: {integrity: sha512-sWyjaJvBqHoVKYPbQ8JRvrGSPaYWtWrJsU+fGVtwKB1GE1rRPu3rC7T6UCuXLoL00Dwb+tsHe2T904r8Vnsx8w==}
+ engines: {node: ^22.11.0 || ^24.11.0 || >=26.0}
peerDependencies:
- postcss: ^8.2.15
+ postcss: ^8.5.14
stylis@4.0.10:
resolution: {integrity: sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==}
+ stylis@4.2.0:
+ resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
+
sucrase@3.34.0:
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
engines: {node: '>=8'}
@@ -8053,6 +8429,11 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
+ svgo@4.0.1:
+ resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==}
+ engines: {node: '>=16'}
+ hasBin: true
+
swagger2openapi@7.0.8:
resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==}
hasBin: true
@@ -8090,19 +8471,50 @@ packages:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'}
- terser-webpack-plugin@5.3.16:
- resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==}
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
+ terser-webpack-plugin@5.6.1:
+ resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==}
engines: {node: '>= 10.13.0'}
peerDependencies:
+ '@minify-html/node': '*'
'@swc/core': '*'
+ '@swc/css': '*'
+ '@swc/html': '*'
+ clean-css: '*'
+ cssnano: '*'
+ csso: '*'
esbuild: '*'
+ html-minifier-terser: '*'
+ lightningcss: '*'
+ postcss: '*'
uglify-js: '*'
webpack: ^5.1.0
peerDependenciesMeta:
+ '@minify-html/node':
+ optional: true
'@swc/core':
optional: true
+ '@swc/css':
+ optional: true
+ '@swc/html':
+ optional: true
+ clean-css:
+ optional: true
+ cssnano:
+ optional: true
+ csso:
+ optional: true
esbuild:
optional: true
+ html-minifier-terser:
+ optional: true
+ lightningcss:
+ optional: true
+ postcss:
+ optional: true
uglify-js:
optional: true
@@ -8148,6 +8560,10 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
tinyrainbow@2.0.0:
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
engines: {node: '>=14.0.0'}
@@ -8196,8 +8612,8 @@ packages:
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
engines: {node: '>=6'}
- tough-cookie@6.0.0:
- resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==}
+ tough-cookie@6.0.1:
+ resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
engines: {node: '>=16'}
tr46@0.0.3:
@@ -8273,8 +8689,8 @@ packages:
resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
engines: {node: '>=10'}
- type-fest@5.4.4:
- resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==}
+ type-fest@5.7.0:
+ resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==}
engines: {node: '>=20'}
type-is@1.6.18:
@@ -8312,8 +8728,8 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
- typescript-eslint@8.58.2:
- resolution: {integrity: sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==}
+ typescript-eslint@8.60.1:
+ resolution: {integrity: sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -8481,6 +8897,7 @@ packages:
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
hasBin: true
v8-to-istanbul@9.2.0:
@@ -8520,13 +8937,13 @@ packages:
vite:
optional: true
- vite@8.0.8:
- resolution: {integrity: sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==}
+ vite@8.0.16:
+ resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^25
- '@vitejs/devtools': ^0.1.0
+ '@vitejs/devtools': ^0.1.18
esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0'
less: ^4.0.0
@@ -8674,8 +9091,8 @@ packages:
webpack:
optional: true
- webpack-dev-server@5.2.3:
- resolution: {integrity: sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==}
+ webpack-dev-server@5.2.4:
+ resolution: {integrity: sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA==}
engines: {node: '>= 18.12.0'}
hasBin: true
peerDependencies:
@@ -8694,18 +9111,15 @@ packages:
resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==}
engines: {node: '>=18.0.0'}
- webpack-sources@3.3.3:
- resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==}
+ webpack-sources@3.5.0:
+ resolution: {integrity: sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==}
engines: {node: '>=10.13.0'}
- webpack-virtual-modules@0.6.1:
- resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==}
-
webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
- webpack@5.105.0:
- resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==}
+ webpack@5.107.2:
+ resolution: {integrity: sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==}
engines: {node: '>=10.13.0'}
hasBin: true
peerDependencies:
@@ -8884,10 +9298,6 @@ packages:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
- yoctocolors-cjs@2.1.3:
- resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
- engines: {node: '>=18'}
-
yup@0.32.11:
resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==}
engines: {node: '>=10'}
@@ -9078,7 +9488,7 @@ snapshots:
dependencies:
'@babel/compat-data': 7.29.0
'@babel/helper-validator-option': 7.27.1
- browserslist: 4.28.1
+ browserslist: 4.28.2
lru-cache: 5.1.1
semver: 6.3.1
@@ -9867,109 +10277,123 @@ snapshots:
'@bcoe/v8-coverage@1.0.2': {}
- '@chromatic-com/storybook@5.1.2(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
+ '@chromatic-com/storybook@5.1.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
dependencies:
'@neoconfetti/react': 1.0.0
chromatic: 13.3.5
filesize: 10.1.6
jsonfile: 6.2.0
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
strip-ansi: 7.1.0
transitivePeerDependencies:
- '@chromatic-com/cypress'
- '@chromatic-com/playwright'
- '@codemirror/autocomplete@6.18.6':
+ '@codemirror/autocomplete@6.20.2':
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
'@lezer/common': 1.2.3
- '@codemirror/commands@6.8.0':
+ '@codemirror/commands@6.10.3':
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
'@lezer/common': 1.2.3
'@codemirror/lang-json@6.0.1':
dependencies:
- '@codemirror/language': 6.10.8
+ '@codemirror/language': 6.12.3
'@lezer/json': 1.0.3
optional: true
'@codemirror/lang-yaml@6.1.2':
dependencies:
- '@codemirror/autocomplete': 6.18.6
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
+ '@codemirror/autocomplete': 6.20.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@lezer/yaml': 1.0.3
optional: true
- '@codemirror/language@6.10.8':
+ '@codemirror/language@6.12.3':
dependencies:
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
- '@lezer/common': 1.2.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
+ '@lezer/common': 1.5.2
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
style-mod: 4.1.2
- '@codemirror/legacy-modes@6.4.2':
+ '@codemirror/legacy-modes@6.5.3':
dependencies:
- '@codemirror/language': 6.10.8
+ '@codemirror/language': 6.12.3
- '@codemirror/lint@6.8.4':
+ '@codemirror/lint@6.9.6':
dependencies:
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
crelt: 1.0.6
'@codemirror/merge@6.10.0':
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
'@lezer/highlight': 1.2.1
style-mod: 4.1.2
- '@codemirror/search@6.5.8':
+ '@codemirror/search@6.7.0':
dependencies:
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
crelt: 1.0.6
- '@codemirror/state@6.5.1':
+ '@codemirror/state@6.6.0':
dependencies:
'@marijn/find-cluster-break': 1.0.2
- '@codemirror/theme-one-dark@6.1.2':
+ '@codemirror/theme-one-dark@6.1.3':
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
'@lezer/highlight': 1.2.1
- '@codemirror/view@6.36.2':
+ '@codemirror/view@6.43.0':
dependencies:
- '@codemirror/state': 6.5.1
+ '@codemirror/state': 6.6.0
+ crelt: 1.0.6
style-mod: 4.1.2
w3c-keyname: 2.2.8
+ '@colordx/core@5.4.3': {}
+
'@discoveryjs/json-ext@0.5.7': {}
'@discoveryjs/json-ext@0.6.3': {}
+ '@emnapi/core@1.10.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.1
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/core@1.9.2':
dependencies:
'@emnapi/wasi-threads': 1.2.1
tslib: 2.8.1
optional: true
+ '@emnapi/runtime@1.10.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/runtime@1.9.2':
dependencies:
tslib: 2.8.1
@@ -9980,6 +10404,30 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emotion/babel-plugin@11.13.5':
+ dependencies:
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/runtime': 7.26.0
+ '@emotion/hash': 0.9.2
+ '@emotion/memoize': 0.9.0
+ '@emotion/serialize': 1.3.3
+ babel-plugin-macros: 3.1.0
+ convert-source-map: 1.9.0
+ escape-string-regexp: 4.0.0
+ find-root: 1.1.0
+ source-map: 0.5.7
+ stylis: 4.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@emotion/cache@11.14.0':
+ dependencies:
+ '@emotion/memoize': 0.9.0
+ '@emotion/sheet': 1.4.0
+ '@emotion/utils': 1.4.2
+ '@emotion/weak-memoize': 0.4.0
+ stylis: 4.2.0
+
'@emotion/cache@11.6.0':
dependencies:
'@emotion/memoize': 0.7.5
@@ -9988,40 +10436,54 @@ snapshots:
'@emotion/weak-memoize': 0.2.5
stylis: 4.0.10
- '@emotion/hash@0.8.0': {}
+ '@emotion/hash@0.9.2': {}
'@emotion/memoize@0.7.5': {}
- '@emotion/react@11.6.0(@babel/core@7.23.6)(@types/react@17.0.75)(react@17.0.2)':
+ '@emotion/memoize@0.9.0': {}
+
+ '@emotion/react@11.14.0(@types/react@17.0.75)(react@17.0.2)':
dependencies:
'@babel/runtime': 7.26.0
- '@emotion/cache': 11.6.0
- '@emotion/serialize': 1.0.2
- '@emotion/sheet': 1.1.0
- '@emotion/utils': 1.0.0
- '@emotion/weak-memoize': 0.2.5
+ '@emotion/babel-plugin': 11.13.5
+ '@emotion/cache': 11.14.0
+ '@emotion/serialize': 1.3.3
+ '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@17.0.2)
+ '@emotion/utils': 1.4.2
+ '@emotion/weak-memoize': 0.4.0
hoist-non-react-statics: 3.3.2
react: 17.0.2
optionalDependencies:
- '@babel/core': 7.23.6
'@types/react': 17.0.75
+ transitivePeerDependencies:
+ - supports-color
- '@emotion/serialize@1.0.2':
+ '@emotion/serialize@1.3.3':
dependencies:
- '@emotion/hash': 0.8.0
- '@emotion/memoize': 0.7.5
- '@emotion/unitless': 0.7.5
- '@emotion/utils': 1.0.0
+ '@emotion/hash': 0.9.2
+ '@emotion/memoize': 0.9.0
+ '@emotion/unitless': 0.10.0
+ '@emotion/utils': 1.4.2
csstype: 3.0.10
'@emotion/sheet@1.1.0': {}
- '@emotion/unitless@0.7.5': {}
+ '@emotion/sheet@1.4.0': {}
+
+ '@emotion/unitless@0.10.0': {}
+
+ '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@17.0.2)':
+ dependencies:
+ react: 17.0.2
'@emotion/utils@1.0.0': {}
+ '@emotion/utils@1.4.2': {}
+
'@emotion/weak-memoize@0.2.5': {}
+ '@emotion/weak-memoize@0.4.0': {}
+
'@esbuild/aix-ppc64@0.27.3':
optional: true
@@ -10100,24 +10562,24 @@ snapshots:
'@esbuild/win32-x64@0.27.3':
optional: true
- '@eslint-community/eslint-plugin-eslint-comments@4.7.1(eslint@9.39.4(jiti@2.6.1))':
+ '@eslint-community/eslint-plugin-eslint-comments@4.7.1(eslint@9.39.4(jiti@2.7.0))':
dependencies:
escape-string-regexp: 4.0.0
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
ignore: 7.0.5
- '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
+ '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))':
dependencies:
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
- '@eslint/compat@2.0.5(eslint@9.39.4(jiti@2.6.1))':
+ '@eslint/compat@2.0.5(eslint@9.39.4(jiti@2.7.0))':
dependencies:
'@eslint/core': 1.2.1
optionalDependencies:
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
'@eslint/config-array@0.21.2':
dependencies:
@@ -10164,6 +10626,17 @@ snapshots:
'@exodus/schemasafe@1.0.0-rc.6': {}
+ '@floating-ui/core@1.7.5':
+ dependencies:
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/dom@1.7.6':
+ dependencies:
+ '@floating-ui/core': 1.7.5
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/utils@0.2.11': {}
+
'@hey-api/codegen-core@0.8.2(magicast@0.5.2)':
dependencies:
'@hey-api/types': 0.1.4
@@ -10225,31 +10698,30 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
- '@inquirer/ansi@1.0.2': {}
+ '@inquirer/ansi@2.0.7': {}
- '@inquirer/confirm@5.1.21(@types/node@25.0.3)':
+ '@inquirer/confirm@6.1.1(@types/node@25.0.3)':
dependencies:
- '@inquirer/core': 10.3.2(@types/node@25.0.3)
- '@inquirer/type': 3.0.10(@types/node@25.0.3)
+ '@inquirer/core': 11.2.1(@types/node@25.0.3)
+ '@inquirer/type': 4.0.7(@types/node@25.0.3)
optionalDependencies:
'@types/node': 25.0.3
- '@inquirer/core@10.3.2(@types/node@25.0.3)':
+ '@inquirer/core@11.2.1(@types/node@25.0.3)':
dependencies:
- '@inquirer/ansi': 1.0.2
- '@inquirer/figures': 1.0.15
- '@inquirer/type': 3.0.10(@types/node@25.0.3)
+ '@inquirer/ansi': 2.0.7
+ '@inquirer/figures': 2.0.7
+ '@inquirer/type': 4.0.7(@types/node@25.0.3)
cli-width: 4.1.0
- mute-stream: 2.0.0
+ fast-wrap-ansi: 0.2.2
+ mute-stream: 3.0.0
signal-exit: 4.1.0
- wrap-ansi: 6.2.0
- yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.0.3
- '@inquirer/figures@1.0.15': {}
+ '@inquirer/figures@2.0.7': {}
- '@inquirer/type@3.0.10(@types/node@25.0.3)':
+ '@inquirer/type@4.0.7(@types/node@25.0.3)':
optionalDependencies:
'@types/node': 25.0.3
@@ -10433,6 +10905,8 @@ snapshots:
'@lezer/common@1.2.3': {}
+ '@lezer/common@1.5.2': {}
+
'@lezer/highlight@1.2.1':
dependencies:
'@lezer/common': 1.2.3
@@ -10446,7 +10920,7 @@ snapshots:
'@lezer/lr@1.4.2':
dependencies:
- '@lezer/common': 1.2.3
+ '@lezer/common': 1.5.2
'@lezer/yaml@1.0.3':
dependencies:
@@ -10465,7 +10939,7 @@ snapshots:
'@types/react': 17.0.75
react: 17.0.2
- '@mswjs/interceptors@0.41.2':
+ '@mswjs/interceptors@0.41.9':
dependencies:
'@open-draft/deferred-promise': 2.2.0
'@open-draft/logger': 0.3.0
@@ -10481,7 +10955,14 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
- '@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)':
+ '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
+ '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)':
dependencies:
'@emnapi/core': 1.9.2
'@emnapi/runtime': 1.9.2
@@ -10508,6 +10989,8 @@ snapshots:
'@open-draft/deferred-promise@2.2.0': {}
+ '@open-draft/deferred-promise@3.0.0': {}
+
'@open-draft/logger@0.3.0':
dependencies:
is-node-process: 1.2.0
@@ -10515,7 +10998,134 @@ snapshots:
'@open-draft/until@2.1.0': {}
- '@oxc-project/types@0.124.0': {}
+ '@oxc-parser/binding-android-arm-eabi@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-android-arm64@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-darwin-arm64@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-darwin-x64@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-freebsd-x64@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm-musleabihf@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm64-gnu@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm64-musl@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-ppc64-gnu@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-riscv64-gnu@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-riscv64-musl@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-s390x-gnu@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-x64-gnu@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-x64-musl@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-openharmony-arm64@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-wasm32-wasi@0.127.0':
+ dependencies:
+ '@emnapi/core': 1.9.2
+ '@emnapi/runtime': 1.9.2
+ '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)
+ optional: true
+
+ '@oxc-parser/binding-win32-arm64-msvc@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-win32-ia32-msvc@0.127.0':
+ optional: true
+
+ '@oxc-parser/binding-win32-x64-msvc@0.127.0':
+ optional: true
+
+ '@oxc-project/types@0.127.0': {}
+
+ '@oxc-project/types@0.133.0': {}
+
+ '@oxc-resolver/binding-android-arm-eabi@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-android-arm64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-darwin-arm64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-darwin-x64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-freebsd-x64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm64-musl@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-riscv64-musl@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-s390x-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-x64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-x64-musl@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-openharmony-arm64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-wasm32-wasi@11.20.0':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ optional: true
+
+ '@oxc-resolver/binding-win32-arm64-msvc@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-win32-x64-msvc@11.20.0':
+ optional: true
'@peculiar/asn1-cms@2.6.0':
dependencies:
@@ -10916,62 +11526,62 @@ snapshots:
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- '@rolldown/binding-android-arm64@1.0.0-rc.15':
+ '@rolldown/binding-android-arm64@1.0.3':
optional: true
- '@rolldown/binding-darwin-arm64@1.0.0-rc.15':
+ '@rolldown/binding-darwin-arm64@1.0.3':
optional: true
- '@rolldown/binding-darwin-x64@1.0.0-rc.15':
+ '@rolldown/binding-darwin-x64@1.0.3':
optional: true
- '@rolldown/binding-freebsd-x64@1.0.0-rc.15':
+ '@rolldown/binding-freebsd-x64@1.0.3':
optional: true
- '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15':
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
optional: true
- '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
optional: true
- '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15':
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
optional: true
- '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
optional: true
- '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
optional: true
- '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
optional: true
- '@rolldown/binding-linux-x64-musl@1.0.0-rc.15':
+ '@rolldown/binding-linux-x64-musl@1.0.3':
optional: true
- '@rolldown/binding-openharmony-arm64@1.0.0-rc.15':
+ '@rolldown/binding-openharmony-arm64@1.0.3':
optional: true
- '@rolldown/binding-wasm32-wasi@1.0.0-rc.15':
+ '@rolldown/binding-wasm32-wasi@1.0.3':
dependencies:
- '@emnapi/core': 1.9.2
- '@emnapi/runtime': 1.9.2
- '@napi-rs/wasm-runtime': 1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
optional: true
- '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15':
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
optional: true
- '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15':
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
optional: true
- '@rolldown/pluginutils@1.0.0-rc.15': {}
+ '@rolldown/pluginutils@1.0.1': {}
'@rollup/pluginutils@5.3.0(rollup@4.54.0)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
- picomatch: 4.0.3
+ picomatch: 4.0.4
optionalDependencies:
rollup: 4.54.0
@@ -11098,97 +11708,109 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
- '@storybook/addon-docs@10.3.5(@types/react@17.0.75)(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.105.0)':
+ '@storybook/addon-docs@10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.107.2)':
dependencies:
'@mdx-js/react': 3.1.1(@types/react@17.0.75)(react@17.0.2)
- '@storybook/csf-plugin': 10.3.5(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.105.0)
- '@storybook/icons': 2.0.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
- '@storybook/react-dom-shim': 10.3.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
+ '@storybook/csf-plugin': 10.4.2(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.107.2)
+ '@storybook/icons': 2.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ '@storybook/react-dom-shim': 10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
ts-dedent: 2.2.0
+ optionalDependencies:
+ '@types/react': 17.0.75
transitivePeerDependencies:
- - '@types/react'
+ - '@types/react-dom'
- esbuild
- rollup
- vite
- webpack
- '@storybook/addon-links@10.3.5(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
+ '@storybook/addon-links@10.4.2(@types/react@17.0.75)(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
optionalDependencies:
+ '@types/react': 17.0.75
react: 17.0.2
- '@storybook/addon-styling-webpack@3.0.2(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.105.0)':
+ '@storybook/addon-styling-webpack@3.0.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.107.2)':
dependencies:
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
- '@storybook/addon-webpack5-compiler-swc@4.0.3(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.105.0)':
+ '@storybook/addon-webpack5-compiler-swc@4.0.3(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(webpack@5.107.2)':
dependencies:
'@swc/core': 1.15.11
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
- swc-loader: 0.2.7(@swc/core@1.15.11)(webpack@5.105.0)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ swc-loader: 0.2.7(@swc/core@1.15.11)(webpack@5.107.2)
transitivePeerDependencies:
- '@swc/helpers'
- webpack
- '@storybook/builder-webpack5@10.3.5(@swc/core@1.15.11)(esbuild@0.27.3)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)':
+ '@storybook/builder-webpack5@10.4.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)':
dependencies:
- '@storybook/core-webpack': 10.3.5(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
+ '@storybook/core-webpack': 10.4.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
case-sensitive-paths-webpack-plugin: 2.4.0
cjs-module-lexer: 1.4.3
- css-loader: 7.1.4(webpack@5.105.0)
+ css-loader: 7.1.4(webpack@5.107.2)
es-module-lexer: 1.7.0
- fork-ts-checker-webpack-plugin: 9.1.0(typescript@6.0.2)(webpack@5.105.0)
- html-webpack-plugin: 5.5.3(webpack@5.105.0)
+ fork-ts-checker-webpack-plugin: 9.1.0(typescript@6.0.2)(webpack@5.107.2)
+ html-webpack-plugin: 5.5.3(webpack@5.107.2)
magic-string: 0.30.21
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
- style-loader: 4.0.0(webpack@5.105.0)
- terser-webpack-plugin: 5.3.16(@swc/core@1.15.11)(esbuild@0.27.3)(webpack@5.105.0)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ style-loader: 4.0.0(webpack@5.107.2)
+ terser-webpack-plugin: 5.6.1(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack@5.107.2)
ts-dedent: 2.2.0
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
- webpack-dev-middleware: 6.1.3(webpack@5.105.0)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
+ webpack-dev-middleware: 6.1.3(webpack@5.107.2)
webpack-hot-middleware: 2.25.3
- webpack-virtual-modules: 0.6.1
+ webpack-virtual-modules: 0.6.2
optionalDependencies:
typescript: 6.0.2
transitivePeerDependencies:
+ - '@minify-html/node'
- '@rspack/core'
- '@swc/core'
+ - '@swc/css'
+ - '@swc/html'
+ - clean-css
+ - cssnano
+ - csso
- esbuild
+ - html-minifier-terser
+ - lightningcss
+ - postcss
- uglify-js
- webpack-cli
- '@storybook/core-webpack@10.3.5(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
+ '@storybook/core-webpack@10.4.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
dependencies:
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
ts-dedent: 2.2.0
- '@storybook/csf-plugin@10.3.5(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.105.0)':
+ '@storybook/csf-plugin@10.4.2(esbuild@0.27.3)(rollup@4.54.0)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))(webpack@5.107.2)':
dependencies:
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
unplugin: 2.3.11
optionalDependencies:
esbuild: 0.27.3
rollup: 4.54.0
- vite: 8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ vite: 8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
'@storybook/global@5.0.0': {}
- '@storybook/icons@2.0.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
+ '@storybook/icons@2.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- '@storybook/preset-react-webpack@10.3.5(@swc/core@1.15.11)(esbuild@0.27.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)':
+ '@storybook/preset-react-webpack@10.4.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)':
dependencies:
- '@storybook/core-webpack': 10.3.5(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
- '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@6.0.2)(webpack@5.105.0)
+ '@storybook/core-webpack': 10.4.2(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
+ '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@6.0.2)(webpack@5.107.2)
'@types/semver': 7.7.1
magic-string: 0.30.21
react: 17.0.2
@@ -11196,19 +11818,28 @@ snapshots:
react-dom: 17.0.2(react@17.0.2)
resolve: 1.22.11
semver: 7.7.3
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
tsconfig-paths: 4.2.0
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
optionalDependencies:
typescript: 6.0.2
transitivePeerDependencies:
+ - '@minify-html/node'
- '@swc/core'
+ - '@swc/css'
+ - '@swc/html'
+ - clean-css
+ - cssnano
+ - csso
- esbuild
+ - html-minifier-terser
+ - lightningcss
+ - postcss
- supports-color
- uglify-js
- webpack-cli
- '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@6.0.2)(webpack@5.105.0)':
+ '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@6.0.2)(webpack@5.107.2)':
dependencies:
debug: 4.4.3
endent: 2.1.0
@@ -11218,44 +11849,60 @@ snapshots:
react-docgen-typescript: 2.2.2(typescript@6.0.2)
tslib: 2.8.1
typescript: 6.0.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
transitivePeerDependencies:
- supports-color
- '@storybook/react-dom-shim@10.3.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
+ '@storybook/react-dom-shim@10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))':
dependencies:
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ optionalDependencies:
+ '@types/react': 17.0.75
+ '@types/react-dom': 17.0.25
- '@storybook/react-webpack5@10.3.5(@swc/core@1.15.11)(esbuild@0.27.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)':
+ '@storybook/react-webpack5@10.4.2(@swc/core@1.15.11)(@types/react-dom@17.0.25)(@types/react@17.0.75)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)':
dependencies:
- '@storybook/builder-webpack5': 10.3.5(@swc/core@1.15.11)(esbuild@0.27.3)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)
- '@storybook/preset-react-webpack': 10.3.5(@swc/core@1.15.11)(esbuild@0.27.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)
- '@storybook/react': 10.3.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)
+ '@storybook/builder-webpack5': 10.4.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)
+ '@storybook/preset-react-webpack': 10.4.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)(webpack-cli@6.0.1)
+ '@storybook/react': 10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
optionalDependencies:
typescript: 6.0.2
transitivePeerDependencies:
+ - '@minify-html/node'
- '@rspack/core'
- '@swc/core'
+ - '@swc/css'
+ - '@swc/html'
+ - '@types/react'
+ - '@types/react-dom'
+ - clean-css
+ - cssnano
+ - csso
- esbuild
+ - html-minifier-terser
+ - lightningcss
+ - postcss
- supports-color
- uglify-js
- webpack-cli
- '@storybook/react@10.3.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)':
+ '@storybook/react@10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2)':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/react-dom-shim': 10.3.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
+ '@storybook/react-dom-shim': 10.4.2(@types/react-dom@17.0.25)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))
react: 17.0.2
react-docgen: 8.0.3
react-docgen-typescript: 2.2.2(typescript@6.0.2)
react-dom: 17.0.2(react@17.0.2)
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
optionalDependencies:
+ '@types/react': 17.0.75
+ '@types/react-dom': 17.0.25
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
@@ -11480,10 +12127,6 @@ snapshots:
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- '@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4)':
- dependencies:
- '@testing-library/dom': 9.3.4
-
'@testing-library/user-event@14.6.1(@testing-library/dom@9.3.4)':
dependencies:
'@testing-library/dom': 9.3.4
@@ -11524,10 +12167,6 @@ snapshots:
'@babel/parser': 7.29.0
'@babel/types': 7.29.0
- '@types/babel__traverse@7.20.4':
- dependencies:
- '@babel/types': 7.29.0
-
'@types/babel__traverse@7.28.0':
dependencies:
'@babel/types': 7.29.0
@@ -11559,20 +12198,8 @@ snapshots:
'@types/doctrine@0.0.9': {}
- '@types/eslint-scope@3.7.7':
- dependencies:
- '@types/eslint': 9.6.1
- '@types/estree': 1.0.8
-
- '@types/eslint@9.6.1':
- dependencies:
- '@types/estree': 1.0.8
- '@types/json-schema': 7.0.15
-
'@types/estree@1.0.8': {}
- '@types/estree@1.0.9': {}
-
'@types/express-serve-static-core@4.17.34':
dependencies:
'@types/node': 25.0.3
@@ -11604,6 +12231,11 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/hoist-non-react-statics@3.3.7(@types/react@17.0.75)':
+ dependencies:
+ '@types/react': 17.0.75
+ hoist-non-react-statics: 3.3.2
+
'@types/html-minifier-terser@6.1.0': {}
'@types/http-errors@2.0.5': {}
@@ -11632,9 +12264,9 @@ snapshots:
'@types/lodash.memoize@4.1.9':
dependencies:
- '@types/lodash': 4.17.21
+ '@types/lodash': 4.17.24
- '@types/lodash@4.17.21': {}
+ '@types/lodash@4.17.24': {}
'@types/mdast@4.0.4':
dependencies:
@@ -11654,6 +12286,8 @@ snapshots:
'@types/nprogress@0.2.3': {}
+ '@types/parse-json@4.0.2': {}
+
'@types/prop-types@15.7.4': {}
'@types/qs@6.14.0': {}
@@ -11713,6 +12347,10 @@ snapshots:
'@types/node': 25.0.3
'@types/send': 0.17.6
+ '@types/set-cookie-parser@2.4.10':
+ dependencies:
+ '@types/node': 25.0.3
+
'@types/sizzle@2.3.10': {}
'@types/sockjs@0.3.36':
@@ -11739,15 +12377,15 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.0
- '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)':
+ '@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- '@typescript-eslint/scope-manager': 8.58.2
- '@typescript-eslint/type-utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- '@typescript-eslint/visitor-keys': 8.58.2
- eslint: 9.39.4(jiti@2.6.1)
+ '@typescript-eslint/parser': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ '@typescript-eslint/scope-manager': 8.60.1
+ '@typescript-eslint/type-utils': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ '@typescript-eslint/utils': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ '@typescript-eslint/visitor-keys': 8.60.1
+ eslint: 9.39.4(jiti@2.7.0)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.2)
@@ -11755,56 +12393,91 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)':
+ '@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)':
dependencies:
- '@typescript-eslint/scope-manager': 8.58.2
- '@typescript-eslint/types': 8.58.2
- '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2)
- '@typescript-eslint/visitor-keys': 8.58.2
+ '@typescript-eslint/scope-manager': 8.60.1
+ '@typescript-eslint/types': 8.60.1
+ '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.2)
+ '@typescript-eslint/visitor-keys': 8.60.1
debug: 4.4.3
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.58.2(typescript@6.0.2)':
+ '@typescript-eslint/project-service@8.58.1(typescript@6.0.2)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@6.0.2)
- '@typescript-eslint/types': 8.58.2
+ '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@6.0.2)
+ '@typescript-eslint/types': 8.58.1
debug: 4.4.3
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.58.2':
+ '@typescript-eslint/project-service@8.60.1(typescript@6.0.2)':
dependencies:
- '@typescript-eslint/types': 8.58.2
- '@typescript-eslint/visitor-keys': 8.58.2
+ '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.2)
+ '@typescript-eslint/types': 8.60.1
+ debug: 4.4.3
+ typescript: 6.0.2
+ transitivePeerDependencies:
+ - supports-color
- '@typescript-eslint/tsconfig-utils@8.58.2(typescript@6.0.2)':
+ '@typescript-eslint/scope-manager@8.58.1':
+ dependencies:
+ '@typescript-eslint/types': 8.58.1
+ '@typescript-eslint/visitor-keys': 8.58.1
+
+ '@typescript-eslint/scope-manager@8.60.1':
+ dependencies:
+ '@typescript-eslint/types': 8.60.1
+ '@typescript-eslint/visitor-keys': 8.60.1
+
+ '@typescript-eslint/tsconfig-utils@8.58.1(typescript@6.0.2)':
dependencies:
typescript: 6.0.2
- '@typescript-eslint/type-utils@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)':
+ '@typescript-eslint/tsconfig-utils@8.60.1(typescript@6.0.2)':
dependencies:
- '@typescript-eslint/types': 8.58.2
- '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2)
- '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
+ typescript: 6.0.2
+
+ '@typescript-eslint/type-utils@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.60.1
+ '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.2)
+ '@typescript-eslint/utils': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
debug: 4.4.3
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
ts-api-utils: 2.5.0(typescript@6.0.2)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.58.2': {}
+ '@typescript-eslint/types@8.58.1': {}
- '@typescript-eslint/typescript-estree@8.58.2(typescript@6.0.2)':
+ '@typescript-eslint/types@8.60.1': {}
+
+ '@typescript-eslint/typescript-estree@8.58.1(typescript@6.0.2)':
dependencies:
- '@typescript-eslint/project-service': 8.58.2(typescript@6.0.2)
- '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@6.0.2)
- '@typescript-eslint/types': 8.58.2
- '@typescript-eslint/visitor-keys': 8.58.2
+ '@typescript-eslint/project-service': 8.58.1(typescript@6.0.2)
+ '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@6.0.2)
+ '@typescript-eslint/types': 8.58.1
+ '@typescript-eslint/visitor-keys': 8.58.1
+ debug: 4.4.3
+ minimatch: 10.2.5
+ semver: 7.7.3
+ tinyglobby: 0.2.17
+ ts-api-utils: 2.5.0(typescript@6.0.2)
+ typescript: 6.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/typescript-estree@8.60.1(typescript@6.0.2)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.60.1(typescript@6.0.2)
+ '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.2)
+ '@typescript-eslint/types': 8.60.1
+ '@typescript-eslint/visitor-keys': 8.60.1
debug: 4.4.3
minimatch: 10.2.5
semver: 7.7.3
@@ -11814,20 +12487,36 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)':
+ '@typescript-eslint/utils@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)':
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
- '@typescript-eslint/scope-manager': 8.58.2
- '@typescript-eslint/types': 8.58.2
- '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2)
- eslint: 9.39.4(jiti@2.6.1)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
+ '@typescript-eslint/scope-manager': 8.58.1
+ '@typescript-eslint/types': 8.58.1
+ '@typescript-eslint/typescript-estree': 8.58.1(typescript@6.0.2)
+ eslint: 9.39.4(jiti@2.7.0)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.58.2':
+ '@typescript-eslint/utils@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)':
dependencies:
- '@typescript-eslint/types': 8.58.2
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
+ '@typescript-eslint/scope-manager': 8.60.1
+ '@typescript-eslint/types': 8.60.1
+ '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.2)
+ eslint: 9.39.4(jiti@2.7.0)
+ typescript: 6.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.58.1':
+ dependencies:
+ '@typescript-eslint/types': 8.58.1
+ eslint-visitor-keys: 5.0.1
+
+ '@typescript-eslint/visitor-keys@8.60.1':
+ dependencies:
+ '@typescript-eslint/types': 8.60.1
eslint-visitor-keys: 5.0.1
'@uirouter/angularjs@1.0.11(angular@1.8.2)':
@@ -11854,30 +12543,57 @@ snapshots:
prop-types: 15.8.1
react: 17.0.2
- '@uiw/codemirror-extensions-basic-setup@4.23.11(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
+ '@uiw/codemirror-extensions-basic-setup@4.23.11(@codemirror/autocomplete@6.20.2)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)':
dependencies:
- '@codemirror/autocomplete': 6.18.6
- '@codemirror/commands': 6.8.0
- '@codemirror/language': 6.10.8
- '@codemirror/lint': 6.8.4
- '@codemirror/search': 6.5.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/autocomplete': 6.20.2
+ '@codemirror/commands': 6.10.3
+ '@codemirror/language': 6.12.3
+ '@codemirror/lint': 6.9.6
+ '@codemirror/search': 6.7.0
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
- '@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
+ '@uiw/codemirror-extensions-basic-setup@4.25.10(@codemirror/autocomplete@6.20.2)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)':
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/autocomplete': 6.20.2
+ '@codemirror/commands': 6.10.3
+ '@codemirror/language': 6.12.3
+ '@codemirror/lint': 6.9.6
+ '@codemirror/search': 6.7.0
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
- '@uiw/react-codemirror@4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
+ '@uiw/codemirror-themes@4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)':
+ dependencies:
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
+
+ '@uiw/react-codemirror@4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
'@babel/runtime': 7.26.0
- '@codemirror/commands': 6.8.0
- '@codemirror/state': 6.5.1
- '@codemirror/theme-one-dark': 6.1.2
- '@codemirror/view': 6.36.2
- '@uiw/codemirror-extensions-basic-setup': 4.23.11(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
+ '@codemirror/commands': 6.10.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/theme-one-dark': 6.1.3
+ '@codemirror/view': 6.43.0
+ '@uiw/codemirror-extensions-basic-setup': 4.23.11(@codemirror/autocomplete@6.20.2)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)
+ codemirror: 6.0.1
+ react: 17.0.2
+ react-dom: 17.0.2(react@17.0.2)
+ transitivePeerDependencies:
+ - '@codemirror/autocomplete'
+ - '@codemirror/language'
+ - '@codemirror/lint'
+ - '@codemirror/search'
+
+ '@uiw/react-codemirror@4.25.10(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@codemirror/commands': 6.10.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/theme-one-dark': 6.1.3
+ '@codemirror/view': 6.43.0
+ '@uiw/codemirror-extensions-basic-setup': 4.25.10(@codemirror/autocomplete@6.20.2)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)
codemirror: 6.0.1
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
@@ -11960,17 +12676,17 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
+ vitest: 4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
- '@vitest/eslint-plugin@1.6.19(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.8)':
+ '@vitest/eslint-plugin@1.6.15(@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)(vitest@4.1.8)':
dependencies:
- '@typescript-eslint/scope-manager': 8.58.2
- '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- eslint: 9.39.4(jiti@2.6.1)
+ '@typescript-eslint/scope-manager': 8.58.1
+ '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ eslint: 9.39.4(jiti@2.7.0)
optionalDependencies:
- '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
+ '@typescript-eslint/eslint-plugin': 8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
typescript: 6.0.2
- vitest: 4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
+ vitest: 4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
transitivePeerDependencies:
- supports-color
@@ -11991,14 +12707,14 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.8(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))':
+ '@vitest/mocker@4.1.8(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))':
dependencies:
'@vitest/spy': 4.1.8
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- msw: 2.12.10(@types/node@25.0.3)(typescript@6.0.2)
- vite: 8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
+ msw: 2.14.6(@types/node@25.0.3)(typescript@6.0.2)
+ vite: 8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
'@vitest/pretty-format@3.2.4':
dependencies:
@@ -12120,22 +12836,22 @@ snapshots:
'@webcontainer/env@1.1.1': {}
- '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)':
+ '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.107.2)':
dependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
- webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
+ webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2)
- '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)':
+ '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.107.2)':
dependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
- webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
+ webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2)
- '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.3)(webpack@5.105.0)':
+ '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.4)(webpack@5.107.2)':
dependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
- webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
+ webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2)
optionalDependencies:
- webpack-dev-server: 5.2.3(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.105.0)
+ webpack-dev-server: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.2)
'@wojtekmaj/date-utils@1.5.1': {}
@@ -12169,9 +12885,9 @@ snapshots:
mime-types: 2.1.35
negotiator: 0.6.3
- acorn-import-phases@1.0.4(acorn@8.15.0):
+ acorn-import-phases@1.0.4(acorn@8.16.0):
dependencies:
- acorn: 8.15.0
+ acorn: 8.16.0
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
@@ -12181,6 +12897,14 @@ snapshots:
acorn@8.15.0: {}
+ acorn@8.16.0: {}
+
+ agent-base@6.0.2:
+ dependencies:
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
agent-base@7.1.4: {}
ajv-formats@2.1.1(ajv@8.17.1):
@@ -12429,24 +13153,23 @@ snapshots:
asynckit@0.4.0: {}
- auto-ngtemplate-loader@3.1.2(webpack@5.105.0):
+ auto-ngtemplate-loader@3.1.2(webpack@5.107.2):
dependencies:
- html-loader: 4.1.0(webpack@5.105.0)
+ html-loader: 4.1.0(webpack@5.107.2)
loader-utils: 3.2.2
- lodash: 4.17.21
+ lodash: 4.17.23
ngtemplate-loader: 2.1.0
var-validator: 0.0.3
transitivePeerDependencies:
- webpack
- autoprefixer@10.4.16(postcss@8.5.6):
+ autoprefixer@10.5.0(postcss@8.5.15):
dependencies:
- browserslist: 4.22.2
- caniuse-lite: 1.0.30001751
- fraction.js: 4.3.6
- normalize-range: 0.1.2
- picocolors: 1.0.0
- postcss: 8.5.6
+ browserslist: 4.28.2
+ caniuse-lite: 1.0.30001793
+ fraction.js: 5.3.4
+ picocolors: 1.1.1
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
available-typed-arrays@1.0.5: {}
@@ -12457,35 +13180,37 @@ snapshots:
axe-core@4.11.2: {}
- axios-cache-interceptor@1.11.2(axios@1.13.5):
+ axios-cache-interceptor@1.11.2(axios@1.16.1):
dependencies:
- axios: 1.13.5
+ axios: 1.16.1
cache-parser: 1.2.6
fast-defer: 1.1.9
http-vary: 1.0.3
object-code: 2.0.0
try: 1.0.3
- axios-progress-bar@https://codeload.github.com/portainer/progress-bar-4-axios/tar.gz/d424cd29f5b629af9ffc5c3d6db8d92d11d82f0f(axios@1.13.5):
+ axios-progress-bar@https://codeload.github.com/portainer/progress-bar-4-axios/tar.gz/d424cd29f5b629af9ffc5c3d6db8d92d11d82f0f(axios@1.16.1):
dependencies:
- axios: 1.13.5
+ axios: 1.16.1
- axios@1.13.5:
+ axios@1.16.1:
dependencies:
- follow-redirects: 1.15.11
+ follow-redirects: 1.16.0
form-data: 4.0.5
- proxy-from-env: 1.1.0
+ https-proxy-agent: 5.0.1
+ proxy-from-env: 2.1.0
transitivePeerDependencies:
- debug
+ - supports-color
axobject-query@4.1.0: {}
- babel-loader@9.1.3(@babel/core@7.23.6)(webpack@5.105.0):
+ babel-loader@9.1.3(@babel/core@7.23.6)(webpack@5.107.2):
dependencies:
'@babel/core': 7.23.6
find-cache-dir: 4.0.0
schema-utils: 4.0.0
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
babel-plugin-angularjs-annotate@0.10.0:
dependencies:
@@ -12508,9 +13233,15 @@ snapshots:
'@babel/helper-module-imports': 7.22.15
'@babel/types': 7.26.3
glob: 7.2.3
- lodash: 4.17.21
+ lodash: 4.18.1
require-package-name: 2.0.1
+ babel-plugin-macros@3.1.0:
+ dependencies:
+ '@babel/runtime': 7.26.0
+ cosmiconfig: 7.1.0
+ resolve: 1.22.11
+
babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.23.6):
dependencies:
'@babel/compat-data': 7.23.5
@@ -12541,7 +13272,7 @@ snapshots:
base64-js@1.5.1: {}
- baseline-browser-mapping@2.9.11: {}
+ baseline-browser-mapping@2.10.33: {}
batch@0.6.1: {}
@@ -12599,13 +13330,13 @@ snapshots:
node-releases: 2.0.14
update-browserslist-db: 1.0.13(browserslist@4.22.2)
- browserslist@4.28.1:
+ browserslist@4.28.2:
dependencies:
- baseline-browser-mapping: 2.9.11
- caniuse-lite: 1.0.30001762
- electron-to-chromium: 1.5.267
- node-releases: 2.0.27
- update-browserslist-db: 1.2.3(browserslist@4.28.1)
+ baseline-browser-mapping: 2.10.33
+ caniuse-lite: 1.0.30001793
+ electron-to-chromium: 1.5.364
+ node-releases: 2.0.46
+ update-browserslist-db: 1.2.3(browserslist@4.28.2)
buffer-from@1.1.2: {}
@@ -12630,11 +13361,11 @@ snapshots:
dotenv: 17.4.2
exsolve: 1.0.8
giget: 3.2.0
- jiti: 2.6.1
+ jiti: 2.7.0
ohash: 2.0.11
pathe: 2.0.3
perfect-debounce: 2.1.0
- pkg-types: 2.3.0
+ pkg-types: 2.3.1
rc9: 3.0.1
optionalDependencies:
magicast: 0.5.2
@@ -12694,14 +13425,14 @@ snapshots:
caniuse-api@3.0.0:
dependencies:
- browserslist: 4.28.1
- caniuse-lite: 1.0.30001751
+ browserslist: 4.28.2
+ caniuse-lite: 1.0.30001793
lodash.memoize: 4.1.2
lodash.uniq: 4.5.0
caniuse-lite@1.0.30001751: {}
- caniuse-lite@1.0.30001762: {}
+ caniuse-lite@1.0.30001793: {}
case-sensitive-paths-webpack-plugin@2.4.0: {}
@@ -12739,7 +13470,7 @@ snapshots:
chart.js@2.9.4:
dependencies:
chartjs-color: 2.4.1
- moment: 2.29.4
+ moment: 2.30.1
chartjs-color-string@0.6.0:
dependencies:
@@ -12800,10 +13531,10 @@ snapshots:
dependencies:
source-map: 0.6.1
- clean-webpack-plugin@4.0.0(webpack@5.105.0):
+ clean-webpack-plugin@4.0.0(webpack@5.107.2):
dependencies:
del: 4.1.1
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
cli-cursor@4.0.0:
dependencies:
@@ -12838,12 +13569,12 @@ snapshots:
clsx@2.1.1: {}
- codemirror-json-schema@0.8.0(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(@lezer/common@1.2.3):
+ codemirror-json-schema@0.8.1(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0)(@lezer/common@1.2.3):
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/lint': 6.8.4
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/lint': 6.9.6
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
'@lezer/common': 1.2.3
'@sagold/json-pointer': 5.1.2
'@shikijs/markdown-it': 1.29.2
@@ -12855,7 +13586,7 @@ snapshots:
shiki: 1.29.2
yaml: 2.7.0
optionalDependencies:
- '@codemirror/autocomplete': 6.18.6
+ '@codemirror/autocomplete': 6.20.2
'@codemirror/lang-json': 6.0.1
'@codemirror/lang-yaml': 6.1.2
codemirror-json5: 1.0.3
@@ -12863,9 +13594,9 @@ snapshots:
codemirror-json5@1.0.3:
dependencies:
- '@codemirror/language': 6.10.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/language': 6.12.3
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
json5: 2.2.3
@@ -12874,13 +13605,13 @@ snapshots:
codemirror@6.0.1:
dependencies:
- '@codemirror/autocomplete': 6.18.6
- '@codemirror/commands': 6.8.0
- '@codemirror/language': 6.10.8
- '@codemirror/lint': 6.8.4
- '@codemirror/search': 6.5.8
- '@codemirror/state': 6.5.1
- '@codemirror/view': 6.36.2
+ '@codemirror/autocomplete': 6.20.2
+ '@codemirror/commands': 6.10.3
+ '@codemirror/language': 6.12.3
+ '@codemirror/lint': 6.9.6
+ '@codemirror/search': 6.7.0
+ '@codemirror/state': 6.6.0
+ '@codemirror/view': 6.43.0
color-convert@1.9.3:
dependencies:
@@ -12896,8 +13627,6 @@ snapshots:
color-support@1.1.3: {}
- colord@2.9.2: {}
-
colorette@2.0.20: {}
combined-stream@1.0.8:
@@ -12910,6 +13639,8 @@ snapshots:
commander@11.0.0: {}
+ commander@11.1.0: {}
+
commander@12.1.0: {}
commander@14.0.3: {}
@@ -12954,6 +13685,8 @@ snapshots:
content-type@1.0.5: {}
+ convert-source-map@1.9.0: {}
+
convert-source-map@2.0.0: {}
cookie-signature@1.0.6: {}
@@ -12971,7 +13704,7 @@ snapshots:
dependencies:
is-what: 4.1.16
- copy-webpack-plugin@11.0.0(webpack@5.105.0):
+ copy-webpack-plugin@11.0.0(webpack@5.107.2):
dependencies:
fast-glob: 3.3.2
glob-parent: 6.0.2
@@ -12979,7 +13712,7 @@ snapshots:
normalize-path: 3.0.0
schema-utils: 4.0.0
serialize-javascript: 6.0.1
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
core-js-compat@3.34.0:
dependencies:
@@ -12989,6 +13722,14 @@ snapshots:
core-util-is@1.0.3: {}
+ cosmiconfig@7.1.0:
+ dependencies:
+ '@types/parse-json': 4.0.2
+ import-fresh: 3.3.0
+ parse-json: 5.2.0
+ path-type: 4.0.0
+ yaml: 1.10.2
+
cosmiconfig@8.2.0:
dependencies:
import-fresh: 3.3.0
@@ -13010,34 +13751,30 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
- css-declaration-sorter@6.4.0(postcss@8.5.6):
+ css-loader@6.8.1(webpack@5.107.2):
dependencies:
- postcss: 8.5.6
-
- css-loader@6.8.1(webpack@5.105.0):
- dependencies:
- icss-utils: 5.1.0(postcss@8.5.6)
- postcss: 8.5.6
- postcss-modules-extract-imports: 3.0.0(postcss@8.5.6)
- postcss-modules-local-by-default: 4.0.3(postcss@8.5.6)
- postcss-modules-scope: 3.0.0(postcss@8.5.6)
- postcss-modules-values: 4.0.0(postcss@8.5.6)
+ icss-utils: 5.1.0(postcss@8.5.15)
+ postcss: 8.5.15
+ postcss-modules-extract-imports: 3.0.0(postcss@8.5.15)
+ postcss-modules-local-by-default: 4.0.3(postcss@8.5.15)
+ postcss-modules-scope: 3.0.0(postcss@8.5.15)
+ postcss-modules-values: 4.0.0(postcss@8.5.15)
postcss-value-parser: 4.2.0
semver: 7.5.4
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
- css-loader@7.1.4(webpack@5.105.0):
+ css-loader@7.1.4(webpack@5.107.2):
dependencies:
- icss-utils: 5.1.0(postcss@8.5.9)
- postcss: 8.5.9
- postcss-modules-extract-imports: 3.1.0(postcss@8.5.9)
- postcss-modules-local-by-default: 4.2.0(postcss@8.5.9)
- postcss-modules-scope: 3.2.1(postcss@8.5.9)
- postcss-modules-values: 4.0.0(postcss@8.5.9)
+ icss-utils: 5.1.0(postcss@8.5.15)
+ postcss: 8.5.15
+ postcss-modules-extract-imports: 3.1.0(postcss@8.5.15)
+ postcss-modules-local-by-default: 4.2.0(postcss@8.5.15)
+ postcss-modules-scope: 3.2.1(postcss@8.5.15)
+ postcss-modules-values: 4.0.0(postcss@8.5.15)
postcss-value-parser: 4.2.0
semver: 7.7.3
optionalDependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
css-select@4.3.0:
dependencies:
@@ -13065,54 +13802,59 @@ snapshots:
mdn-data: 2.0.30
source-map-js: 1.2.1
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
css-what@6.1.0: {}
css.escape@1.5.1: {}
cssesc@3.0.0: {}
- cssnano-preset-default@6.0.1(postcss@8.5.6):
+ cssnano-preset-default@8.0.1(postcss@8.5.15):
dependencies:
- css-declaration-sorter: 6.4.0(postcss@8.5.6)
- cssnano-utils: 4.0.0(postcss@8.5.6)
- postcss: 8.5.6
- postcss-calc: 9.0.1(postcss@8.5.6)
- postcss-colormin: 6.0.0(postcss@8.5.6)
- postcss-convert-values: 6.0.0(postcss@8.5.6)
- postcss-discard-comments: 6.0.0(postcss@8.5.6)
- postcss-discard-duplicates: 6.0.0(postcss@8.5.6)
- postcss-discard-empty: 6.0.0(postcss@8.5.6)
- postcss-discard-overridden: 6.0.0(postcss@8.5.6)
- postcss-merge-longhand: 6.0.0(postcss@8.5.6)
- postcss-merge-rules: 6.0.1(postcss@8.5.6)
- postcss-minify-font-values: 6.0.0(postcss@8.5.6)
- postcss-minify-gradients: 6.0.0(postcss@8.5.6)
- postcss-minify-params: 6.0.0(postcss@8.5.6)
- postcss-minify-selectors: 6.0.0(postcss@8.5.6)
- postcss-normalize-charset: 6.0.0(postcss@8.5.6)
- postcss-normalize-display-values: 6.0.0(postcss@8.5.6)
- postcss-normalize-positions: 6.0.0(postcss@8.5.6)
- postcss-normalize-repeat-style: 6.0.0(postcss@8.5.6)
- postcss-normalize-string: 6.0.0(postcss@8.5.6)
- postcss-normalize-timing-functions: 6.0.0(postcss@8.5.6)
- postcss-normalize-unicode: 6.0.0(postcss@8.5.6)
- postcss-normalize-url: 6.0.0(postcss@8.5.6)
- postcss-normalize-whitespace: 6.0.0(postcss@8.5.6)
- postcss-ordered-values: 6.0.0(postcss@8.5.6)
- postcss-reduce-initial: 6.0.0(postcss@8.5.6)
- postcss-reduce-transforms: 6.0.0(postcss@8.5.6)
- postcss-svgo: 6.0.0(postcss@8.5.6)
- postcss-unique-selectors: 6.0.0(postcss@8.5.6)
+ browserslist: 4.28.2
+ cssnano-utils: 6.0.0(postcss@8.5.15)
+ postcss: 8.5.15
+ postcss-calc: 10.1.1(postcss@8.5.15)
+ postcss-colormin: 8.0.0(postcss@8.5.15)
+ postcss-convert-values: 8.0.0(postcss@8.5.15)
+ postcss-discard-comments: 8.0.0(postcss@8.5.15)
+ postcss-discard-duplicates: 8.0.0(postcss@8.5.15)
+ postcss-discard-empty: 8.0.0(postcss@8.5.15)
+ postcss-discard-overridden: 8.0.0(postcss@8.5.15)
+ postcss-merge-longhand: 8.0.0(postcss@8.5.15)
+ postcss-merge-rules: 8.0.0(postcss@8.5.15)
+ postcss-minify-font-values: 8.0.0(postcss@8.5.15)
+ postcss-minify-gradients: 8.0.0(postcss@8.5.15)
+ postcss-minify-params: 8.0.0(postcss@8.5.15)
+ postcss-minify-selectors: 8.0.1(postcss@8.5.15)
+ postcss-normalize-charset: 8.0.0(postcss@8.5.15)
+ postcss-normalize-display-values: 8.0.0(postcss@8.5.15)
+ postcss-normalize-positions: 8.0.0(postcss@8.5.15)
+ postcss-normalize-repeat-style: 8.0.0(postcss@8.5.15)
+ postcss-normalize-string: 8.0.0(postcss@8.5.15)
+ postcss-normalize-timing-functions: 8.0.0(postcss@8.5.15)
+ postcss-normalize-unicode: 8.0.0(postcss@8.5.15)
+ postcss-normalize-url: 8.0.0(postcss@8.5.15)
+ postcss-normalize-whitespace: 8.0.0(postcss@8.5.15)
+ postcss-ordered-values: 8.0.0(postcss@8.5.15)
+ postcss-reduce-initial: 8.0.0(postcss@8.5.15)
+ postcss-reduce-transforms: 8.0.0(postcss@8.5.15)
+ postcss-svgo: 8.0.0(postcss@8.5.15)
+ postcss-unique-selectors: 8.0.0(postcss@8.5.15)
- cssnano-utils@4.0.0(postcss@8.5.6):
+ cssnano-utils@6.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- cssnano@6.0.1(postcss@8.5.6):
+ cssnano@8.0.1(postcss@8.5.15):
dependencies:
- cssnano-preset-default: 6.0.1(postcss@8.5.6)
- lilconfig: 2.1.0
- postcss: 8.5.6
+ cssnano-preset-default: 8.0.1(postcss@8.5.15)
+ lilconfig: 3.1.3
+ postcss: 8.5.15
csso@5.0.5:
dependencies:
@@ -13351,10 +14093,10 @@ snapshots:
dependencies:
dotenv: 8.6.0
- dotenv-webpack@8.0.1(webpack@5.105.0):
+ dotenv-webpack@8.0.1(webpack@5.107.2):
dependencies:
dotenv-defaults: 2.0.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
dotenv@17.4.2: {}
@@ -13374,7 +14116,7 @@ snapshots:
electron-to-chromium@1.4.616: {}
- electron-to-chromium@1.5.267: {}
+ electron-to-chromium@1.5.364: {}
emoji-regex-xs@1.0.0: {}
@@ -13392,10 +14134,10 @@ snapshots:
fast-json-parse: 1.0.3
objectorarray: 1.0.5
- enhanced-resolve@5.19.0:
+ enhanced-resolve@5.22.1:
dependencies:
graceful-fs: 4.2.11
- tapable: 2.3.0
+ tapable: 2.3.3
entities@2.1.0: {}
@@ -13616,8 +14358,6 @@ snapshots:
'@esbuild/win32-ia32': 0.27.3
'@esbuild/win32-x64': 0.27.3
- escalade@3.1.1: {}
-
escalade@3.2.0: {}
escape-html@1.0.3: {}
@@ -13626,9 +14366,9 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.6.1)):
+ eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)):
dependencies:
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
eslint-import-context@0.1.9(unrs-resolver@1.11.1):
dependencies:
@@ -13639,7 +14379,7 @@ snapshots:
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0):
dependencies:
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.7.0))
eslint-import-resolver-node@0.3.10:
dependencies:
@@ -13649,10 +14389,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)):
+ eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)):
dependencies:
debug: 4.4.3
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
eslint-import-context: 0.1.9(unrs-resolver@1.11.1)
get-tsconfig: 4.13.7
is-bun-module: 2.0.0
@@ -13660,28 +14400,28 @@ snapshots:
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.7.0))
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.6.1)):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.7.0)):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- eslint: 9.39.4(jiti@2.6.1)
+ '@typescript-eslint/parser': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ eslint: 9.39.4(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
- eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1))
+ eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0))
transitivePeerDependencies:
- supports-color
- eslint-plugin-eslint-comments@3.2.0(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-eslint-comments@3.2.0(eslint@9.39.4(jiti@2.7.0)):
dependencies:
escape-string-regexp: 1.0.5
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
ignore: 5.2.4
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.7.0)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -13690,9 +14430,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.6.1))
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.4(jiti@2.7.0))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -13704,13 +14444,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
+ '@typescript-eslint/parser': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.7.0)):
dependencies:
aria-query: 5.3.2
array-includes: 3.1.9
@@ -13720,7 +14460,7 @@ snapshots:
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
@@ -13729,23 +14469,23 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
- eslint-plugin-promise@7.2.1(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-promise@7.3.0(eslint@9.39.4(jiti@2.7.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
- eslint: 9.39.4(jiti@2.6.1)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
+ eslint: 9.39.4(jiti@2.7.0)
- eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)):
dependencies:
'@babel/core': 7.29.0
'@babel/parser': 7.29.0
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
hermes-parser: 0.25.1
zod: 4.3.6
zod-validation-error: 4.0.2(zod@4.3.6)
transitivePeerDependencies:
- supports-color
- eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)):
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
@@ -13753,7 +14493,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.3.2
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.3
@@ -13767,15 +14507,15 @@ snapshots:
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
- eslint-plugin-regex@1.10.0(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-regex@1.10.0(eslint@9.39.4(jiti@2.7.0)):
dependencies:
- eslint: 9.39.4(jiti@2.6.1)
+ eslint: 9.39.4(jiti@2.7.0)
- eslint-plugin-storybook@10.3.5(eslint@9.39.4(jiti@2.6.1))(storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2):
+ eslint-plugin-storybook@10.4.2(eslint@9.39.4(jiti@2.7.0))(storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(typescript@6.0.2):
dependencies:
- '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- eslint: 9.39.4(jiti@2.6.1)
- storybook: 10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ '@typescript-eslint/utils': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ eslint: 9.39.4(jiti@2.7.0)
+ storybook: 10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
transitivePeerDependencies:
- supports-color
- typescript
@@ -13796,9 +14536,9 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
- eslint@9.39.4(jiti@2.6.1):
+ eslint@9.39.4(jiti@2.7.0):
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.2
'@eslint/config-helpers': 0.4.2
@@ -13833,7 +14573,7 @@ snapshots:
natural-compare: 1.4.0
optionator: 0.9.3
optionalDependencies:
- jiti: 2.6.1
+ jiti: 2.7.0
transitivePeerDependencies:
- supports-color
@@ -13861,7 +14601,7 @@ snapshots:
estree-walker@3.0.3:
dependencies:
- '@types/estree': 1.0.9
+ '@types/estree': 1.0.8
esutils@2.0.3: {}
@@ -13903,7 +14643,7 @@ snapshots:
etag: 1.8.1
finalhandler: 1.3.2
fresh: 0.5.2
- http-errors: 2.0.0
+ http-errors: 2.0.1
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.4.1
@@ -13916,7 +14656,7 @@ snapshots:
send: 0.19.2
serve-static: 1.16.3
setprototypeof: 1.2.0
- statuses: 2.0.1
+ statuses: 2.0.2
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
@@ -13949,8 +14689,18 @@ snapshots:
fast-safe-stringify@2.1.1: {}
+ fast-string-truncated-width@3.0.3: {}
+
+ fast-string-width@3.0.2:
+ dependencies:
+ fast-string-truncated-width: 3.0.3
+
fast-uri@3.1.0: {}
+ fast-wrap-ansi@0.2.2:
+ dependencies:
+ fast-string-width: 3.0.2
+
fastest-levenshtein@1.0.12: {}
fastq@1.15.0:
@@ -13961,9 +14711,9 @@ snapshots:
dependencies:
websocket-driver: 0.7.4
- fdir@6.5.0(picomatch@4.0.3):
+ fdir@6.5.0(picomatch@4.0.4):
optionalDependencies:
- picomatch: 4.0.3
+ picomatch: 4.0.4
file-entry-cache@8.0.0:
dependencies:
@@ -14004,6 +14754,8 @@ snapshots:
common-path-prefix: 3.0.0
pkg-dir: 7.0.0
+ find-root@1.1.0: {}
+
find-up@4.1.0:
dependencies:
locate-path: 5.0.0
@@ -14039,7 +14791,7 @@ snapshots:
dependencies:
tslib: 2.8.1
- follow-redirects@1.15.11: {}
+ follow-redirects@1.16.0: {}
for-each@0.3.3:
dependencies:
@@ -14054,7 +14806,7 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
- fork-ts-checker-webpack-plugin@9.1.0(typescript@6.0.2)(webpack@5.105.0):
+ fork-ts-checker-webpack-plugin@9.1.0(typescript@6.0.2)(webpack@5.107.2):
dependencies:
'@babel/code-frame': 7.29.0
chalk: 4.1.2
@@ -14067,9 +14819,9 @@ snapshots:
node-abort-controller: 3.1.1
schema-utils: 3.3.0
semver: 7.7.3
- tapable: 2.3.0
+ tapable: 2.3.3
typescript: 6.0.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
form-data@4.0.4:
dependencies:
@@ -14087,20 +14839,23 @@ snapshots:
hasown: 2.0.2
mime-types: 2.1.35
- formik@2.2.9(react@17.0.2):
+ formik@2.4.9(@types/react@17.0.75)(react@17.0.2):
dependencies:
+ '@types/hoist-non-react-statics': 3.3.7(@types/react@17.0.75)
deepmerge: 2.2.1
hoist-non-react-statics: 3.3.2
- lodash: 4.17.21
+ lodash: 4.18.1
lodash-es: 4.17.21
react: 17.0.2
react-fast-compare: 2.0.4
tiny-warning: 1.0.3
- tslib: 1.14.1
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - '@types/react'
forwarded@0.2.0: {}
- fraction.js@4.3.6: {}
+ fraction.js@5.3.4: {}
fresh@0.5.2: {}
@@ -14258,7 +15013,7 @@ snapshots:
graceful-fs@4.2.11: {}
- graphql@16.12.0: {}
+ graphql@16.14.1: {}
growly@1.3.0: {}
@@ -14318,7 +15073,10 @@ snapshots:
he@1.2.0: {}
- headers-polyfill@4.0.2: {}
+ headers-polyfill@5.0.1:
+ dependencies:
+ '@types/set-cookie-parser': 2.4.10
+ set-cookie-parser: 3.1.0
hermes-estree@0.25.1: {}
@@ -14345,17 +15103,17 @@ snapshots:
html-escaper@2.0.2: {}
- html-loader@4.1.0(webpack@5.105.0):
+ html-loader@4.1.0(webpack@5.107.2):
dependencies:
html-minifier-terser: 6.1.0
parse5: 7.1.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
- html-loader@5.1.0(webpack@5.105.0):
+ html-loader@5.1.0(webpack@5.107.2):
dependencies:
html-minifier-terser: 7.2.0
parse5: 7.1.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
html-minifier-terser@6.1.0:
dependencies:
@@ -14383,14 +15141,14 @@ snapshots:
html-void-elements@3.0.0: {}
- html-webpack-plugin@5.5.3(webpack@5.105.0):
+ html-webpack-plugin@5.5.3(webpack@5.107.2):
dependencies:
'@types/html-minifier-terser': 6.1.0
html-minifier-terser: 6.1.0
- lodash: 4.17.21
+ lodash: 4.18.1
pretty-error: 4.0.0
tapable: 2.3.0
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
htmlparser2@6.1.0:
dependencies:
@@ -14415,14 +15173,6 @@ snapshots:
setprototypeof: 1.1.0
statuses: 1.5.0
- http-errors@2.0.0:
- dependencies:
- depd: 2.0.0
- inherits: 2.0.4
- setprototypeof: 1.2.0
- statuses: 2.0.1
- toidentifier: 1.0.1
-
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@@ -14455,7 +15205,7 @@ snapshots:
http-proxy@1.18.1:
dependencies:
eventemitter3: 4.0.7
- follow-redirects: 1.15.11
+ follow-redirects: 1.16.0
requires-port: 1.0.0
transitivePeerDependencies:
- debug
@@ -14464,6 +15214,13 @@ snapshots:
http2-client@1.3.5: {}
+ https-proxy-agent@5.0.1:
+ dependencies:
+ agent-base: 6.0.2
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
@@ -14499,13 +15256,9 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
- icss-utils@5.1.0(postcss@8.5.6):
+ icss-utils@5.1.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
-
- icss-utils@5.1.0(postcss@8.5.9):
- dependencies:
- postcss: 8.5.9
+ postcss: 8.5.15
ieee754@1.2.1: {}
@@ -14833,7 +15586,7 @@ snapshots:
jiti@1.18.2: {}
- jiti@2.6.1: {}
+ jiti@2.7.0: {}
jquery@3.6.0: {}
@@ -14970,7 +15723,7 @@ snapshots:
image-size: 0.5.5
make-dir: 2.1.0
mime: 1.6.0
- needle: 3.5.0
+ needle: 3.3.1
source-map: 0.6.1
optional: true
@@ -15035,6 +15788,8 @@ snapshots:
lilconfig@2.1.0: {}
+ lilconfig@3.1.3: {}
+
lines-and-columns@1.2.4: {}
linkify-it@3.0.3:
@@ -15066,7 +15821,7 @@ snapshots:
rfdc: 1.3.0
wrap-ansi: 8.1.0
- loader-runner@4.3.1: {}
+ loader-runner@4.3.2: {}
loader-utils@1.4.2:
dependencies:
@@ -15090,10 +15845,10 @@ snapshots:
lodash-es@4.17.21: {}
- lodash-webpack-plugin@0.11.6(webpack@5.105.0):
+ lodash-webpack-plugin@0.11.6(webpack@5.107.2):
dependencies:
- lodash: 4.17.21
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ lodash: 4.18.1
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
lodash.debounce@4.0.8: {}
@@ -15107,7 +15862,9 @@ snapshots:
lodash.uniq@4.5.0: {}
- lodash@4.17.21: {}
+ lodash@4.17.23: {}
+
+ lodash@4.18.1: {}
log-update@5.0.1:
dependencies:
@@ -15199,6 +15956,8 @@ snapshots:
mdn-data@2.0.30: {}
+ mdn-data@2.27.1: {}
+
mdurl@1.0.1: {}
media-typer@0.3.0: {}
@@ -15224,7 +15983,7 @@ snapshots:
tree-dump: 1.1.0(tslib@2.8.1)
tslib: 2.8.1
- memoize-one@5.2.1: {}
+ memoize-one@6.0.0: {}
merge-descriptors@1.0.3: {}
@@ -15281,10 +16040,10 @@ snapshots:
min-indent@1.0.1: {}
- mini-css-extract-plugin@2.7.6(webpack@5.105.0):
+ mini-css-extract-plugin@2.7.6(webpack@5.107.2):
dependencies:
schema-utils: 4.0.0
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
minimalistic-assert@1.0.1: {}
@@ -15302,11 +16061,11 @@ snapshots:
minimist@1.2.8: {}
- moment-timezone@0.5.41:
+ moment-timezone@0.6.2:
dependencies:
- moment: 2.29.4
+ moment: 2.30.1
- moment@2.29.4: {}
+ moment@2.30.1: {}
moo@0.5.2: {}
@@ -15318,29 +16077,29 @@ snapshots:
ms@2.1.3: {}
- msw-storybook-addon@2.0.7(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2)):
+ msw-storybook-addon@2.0.7(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2)):
dependencies:
is-node-process: 1.2.0
- msw: 2.12.10(@types/node@25.0.3)(typescript@6.0.2)
+ msw: 2.14.6(@types/node@25.0.3)(typescript@6.0.2)
- msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2):
+ msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2):
dependencies:
- '@inquirer/confirm': 5.1.21(@types/node@25.0.3)
- '@mswjs/interceptors': 0.41.2
- '@open-draft/deferred-promise': 2.2.0
+ '@inquirer/confirm': 6.1.1(@types/node@25.0.3)
+ '@mswjs/interceptors': 0.41.9
+ '@open-draft/deferred-promise': 3.0.0
'@types/statuses': 2.0.6
cookie: 1.1.1
- graphql: 16.12.0
- headers-polyfill: 4.0.2
+ graphql: 16.14.1
+ headers-polyfill: 5.0.1
is-node-process: 1.2.0
outvariant: 1.4.3
path-to-regexp: 6.3.0
picocolors: 1.1.1
- rettime: 0.10.1
+ rettime: 0.11.11
statuses: 2.0.2
strict-event-emitter: 0.5.1
- tough-cookie: 6.0.0
- type-fest: 5.4.4
+ tough-cookie: 6.0.1
+ type-fest: 5.7.0
until-async: 3.0.2
yargs: 17.7.2
optionalDependencies:
@@ -15355,7 +16114,7 @@ snapshots:
mustache@4.2.0: {}
- mute-stream@2.0.0: {}
+ mute-stream@3.0.0: {}
mz@2.7.0:
dependencies:
@@ -15365,7 +16124,7 @@ snapshots:
nanoclone@0.2.1: {}
- nanoid@3.3.11: {}
+ nanoid@3.3.12: {}
napi-postinstall@0.3.4: {}
@@ -15378,7 +16137,7 @@ snapshots:
railroad-diagrams: 1.0.0
randexp: 0.4.6
- needle@3.5.0:
+ needle@3.3.1:
dependencies:
iconv-lite: 0.6.3
sax: 1.6.0
@@ -15434,12 +16193,10 @@ snapshots:
node-releases@2.0.14: {}
- node-releases@2.0.27: {}
+ node-releases@2.0.46: {}
normalize-path@3.0.0: {}
- normalize-range@0.1.2: {}
-
npm-run-path@5.1.0:
dependencies:
path-key: 4.0.0
@@ -15614,6 +16371,53 @@ snapshots:
object-keys: 1.1.1
safe-push-apply: 1.0.0
+ oxc-parser@0.127.0:
+ dependencies:
+ '@oxc-project/types': 0.127.0
+ optionalDependencies:
+ '@oxc-parser/binding-android-arm-eabi': 0.127.0
+ '@oxc-parser/binding-android-arm64': 0.127.0
+ '@oxc-parser/binding-darwin-arm64': 0.127.0
+ '@oxc-parser/binding-darwin-x64': 0.127.0
+ '@oxc-parser/binding-freebsd-x64': 0.127.0
+ '@oxc-parser/binding-linux-arm-gnueabihf': 0.127.0
+ '@oxc-parser/binding-linux-arm-musleabihf': 0.127.0
+ '@oxc-parser/binding-linux-arm64-gnu': 0.127.0
+ '@oxc-parser/binding-linux-arm64-musl': 0.127.0
+ '@oxc-parser/binding-linux-ppc64-gnu': 0.127.0
+ '@oxc-parser/binding-linux-riscv64-gnu': 0.127.0
+ '@oxc-parser/binding-linux-riscv64-musl': 0.127.0
+ '@oxc-parser/binding-linux-s390x-gnu': 0.127.0
+ '@oxc-parser/binding-linux-x64-gnu': 0.127.0
+ '@oxc-parser/binding-linux-x64-musl': 0.127.0
+ '@oxc-parser/binding-openharmony-arm64': 0.127.0
+ '@oxc-parser/binding-wasm32-wasi': 0.127.0
+ '@oxc-parser/binding-win32-arm64-msvc': 0.127.0
+ '@oxc-parser/binding-win32-ia32-msvc': 0.127.0
+ '@oxc-parser/binding-win32-x64-msvc': 0.127.0
+
+ oxc-resolver@11.20.0:
+ optionalDependencies:
+ '@oxc-resolver/binding-android-arm-eabi': 11.20.0
+ '@oxc-resolver/binding-android-arm64': 11.20.0
+ '@oxc-resolver/binding-darwin-arm64': 11.20.0
+ '@oxc-resolver/binding-darwin-x64': 11.20.0
+ '@oxc-resolver/binding-freebsd-x64': 11.20.0
+ '@oxc-resolver/binding-linux-arm-gnueabihf': 11.20.0
+ '@oxc-resolver/binding-linux-arm-musleabihf': 11.20.0
+ '@oxc-resolver/binding-linux-arm64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-arm64-musl': 11.20.0
+ '@oxc-resolver/binding-linux-ppc64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-riscv64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-riscv64-musl': 11.20.0
+ '@oxc-resolver/binding-linux-s390x-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-x64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-x64-musl': 11.20.0
+ '@oxc-resolver/binding-openharmony-arm64': 11.20.0
+ '@oxc-resolver/binding-wasm32-wasi': 11.20.0
+ '@oxc-resolver/binding-win32-arm64-msvc': 11.20.0
+ '@oxc-resolver/binding-win32-x64-msvc': 11.20.0
+
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
@@ -15714,8 +16518,6 @@ snapshots:
picomatch@2.3.1: {}
- picomatch@4.0.3: {}
-
picomatch@4.0.4: {}
pidtree@0.6.0: {}
@@ -15740,7 +16542,7 @@ snapshots:
dependencies:
find-up: 6.3.0
- pkg-types@2.3.0:
+ pkg-types@2.3.1:
dependencies:
confbox: 0.2.4
exsolve: 1.0.8
@@ -15757,214 +16559,213 @@ snapshots:
possible-typed-array-names@1.1.0: {}
- postcss-calc@9.0.1(postcss@8.5.6):
+ postcss-calc@10.1.1(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
- postcss-selector-parser: 6.0.13
+ postcss: 8.5.15
+ postcss-selector-parser: 7.1.1
postcss-value-parser: 4.2.0
- postcss-colormin@6.0.0(postcss@8.5.6):
+ postcss-colormin@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
+ '@colordx/core': 5.4.3
+ browserslist: 4.28.2
caniuse-api: 3.0.0
- colord: 2.9.2
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-convert-values@6.0.0(postcss@8.5.6):
+ postcss-convert-values@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
- postcss: 8.5.6
+ browserslist: 4.28.2
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-discard-comments@6.0.0(postcss@8.5.6):
+ postcss-discard-comments@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
+ postcss-selector-parser: 7.1.1
- postcss-discard-duplicates@6.0.0(postcss@8.5.6):
+ postcss-discard-duplicates@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-discard-empty@6.0.0(postcss@8.5.6):
+ postcss-discard-empty@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-discard-overridden@6.0.0(postcss@8.5.6):
+ postcss-discard-overridden@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-import@15.1.0(postcss@8.5.6):
+ postcss-import@15.1.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.6
- postcss-js@4.0.1(postcss@8.5.6):
+ postcss-js@4.0.1(postcss@8.5.15):
dependencies:
camelcase-css: 2.0.1
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-load-config@4.0.1(postcss@8.5.6):
+ postcss-load-config@4.0.1(postcss@8.5.15):
dependencies:
lilconfig: 2.1.0
yaml: 2.7.0
optionalDependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-loader@7.3.3(postcss@8.5.6)(webpack@5.105.0):
+ postcss-loader@7.3.3(postcss@8.5.15)(webpack@5.107.2):
dependencies:
cosmiconfig: 8.2.0
jiti: 1.18.2
- postcss: 8.5.6
+ postcss: 8.5.15
semver: 7.5.4
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
- postcss-merge-longhand@6.0.0(postcss@8.5.6):
+ postcss-merge-longhand@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- stylehacks: 6.0.0(postcss@8.5.6)
+ stylehacks: 8.0.0(postcss@8.5.15)
- postcss-merge-rules@6.0.1(postcss@8.5.6):
+ postcss-merge-rules@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
+ browserslist: 4.28.2
caniuse-api: 3.0.0
- cssnano-utils: 4.0.0(postcss@8.5.6)
- postcss: 8.5.6
- postcss-selector-parser: 6.0.13
+ cssnano-utils: 6.0.0(postcss@8.5.15)
+ postcss: 8.5.15
+ postcss-selector-parser: 7.1.1
- postcss-minify-font-values@6.0.0(postcss@8.5.6):
+ postcss-minify-font-values@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-minify-gradients@6.0.0(postcss@8.5.6):
+ postcss-minify-gradients@8.0.0(postcss@8.5.15):
dependencies:
- colord: 2.9.2
- cssnano-utils: 4.0.0(postcss@8.5.6)
- postcss: 8.5.6
+ '@colordx/core': 5.4.3
+ cssnano-utils: 6.0.0(postcss@8.5.15)
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-minify-params@6.0.0(postcss@8.5.6):
+ postcss-minify-params@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
- cssnano-utils: 4.0.0(postcss@8.5.6)
- postcss: 8.5.6
+ browserslist: 4.28.2
+ cssnano-utils: 6.0.0(postcss@8.5.15)
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-minify-selectors@6.0.0(postcss@8.5.6):
+ postcss-minify-selectors@8.0.1(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
- postcss-selector-parser: 6.0.13
+ browserslist: 4.28.2
+ caniuse-api: 3.0.0
+ cssesc: 3.0.0
+ postcss: 8.5.15
+ postcss-selector-parser: 7.1.1
- postcss-modules-extract-imports@3.0.0(postcss@8.5.6):
+ postcss-modules-extract-imports@3.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-modules-extract-imports@3.1.0(postcss@8.5.9):
+ postcss-modules-extract-imports@3.1.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.9
+ postcss: 8.5.15
- postcss-modules-local-by-default@4.0.3(postcss@8.5.6):
+ postcss-modules-local-by-default@4.0.3(postcss@8.5.15):
dependencies:
- icss-utils: 5.1.0(postcss@8.5.6)
- postcss: 8.5.6
+ icss-utils: 5.1.0(postcss@8.5.15)
+ postcss: 8.5.15
postcss-selector-parser: 6.0.13
postcss-value-parser: 4.2.0
- postcss-modules-local-by-default@4.2.0(postcss@8.5.9):
+ postcss-modules-local-by-default@4.2.0(postcss@8.5.15):
dependencies:
- icss-utils: 5.1.0(postcss@8.5.9)
- postcss: 8.5.9
+ icss-utils: 5.1.0(postcss@8.5.15)
+ postcss: 8.5.15
postcss-selector-parser: 7.1.1
postcss-value-parser: 4.2.0
- postcss-modules-scope@3.0.0(postcss@8.5.6):
+ postcss-modules-scope@3.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-selector-parser: 6.0.13
- postcss-modules-scope@3.2.1(postcss@8.5.9):
+ postcss-modules-scope@3.2.1(postcss@8.5.15):
dependencies:
- postcss: 8.5.9
+ postcss: 8.5.15
postcss-selector-parser: 7.1.1
- postcss-modules-values@4.0.0(postcss@8.5.6):
+ postcss-modules-values@4.0.0(postcss@8.5.15):
dependencies:
- icss-utils: 5.1.0(postcss@8.5.6)
- postcss: 8.5.6
+ icss-utils: 5.1.0(postcss@8.5.15)
+ postcss: 8.5.15
- postcss-modules-values@4.0.0(postcss@8.5.9):
+ postcss-nested@6.0.1(postcss@8.5.15):
dependencies:
- icss-utils: 5.1.0(postcss@8.5.9)
- postcss: 8.5.9
-
- postcss-nested@6.0.1(postcss@8.5.6):
- dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-selector-parser: 6.0.13
- postcss-normalize-charset@6.0.0(postcss@8.5.6):
+ postcss-normalize-charset@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-normalize-display-values@6.0.0(postcss@8.5.6):
+ postcss-normalize-display-values@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-positions@6.0.0(postcss@8.5.6):
+ postcss-normalize-positions@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-repeat-style@6.0.0(postcss@8.5.6):
+ postcss-normalize-repeat-style@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-string@6.0.0(postcss@8.5.6):
+ postcss-normalize-string@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-timing-functions@6.0.0(postcss@8.5.6):
+ postcss-normalize-timing-functions@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-unicode@6.0.0(postcss@8.5.6):
+ postcss-normalize-unicode@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
- postcss: 8.5.6
+ browserslist: 4.28.2
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-url@6.0.0(postcss@8.5.6):
+ postcss-normalize-url@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-normalize-whitespace@6.0.0(postcss@8.5.6):
+ postcss-normalize-whitespace@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-ordered-values@6.0.0(postcss@8.5.6):
+ postcss-ordered-values@8.0.0(postcss@8.5.15):
dependencies:
- cssnano-utils: 4.0.0(postcss@8.5.6)
- postcss: 8.5.6
+ cssnano-utils: 6.0.0(postcss@8.5.15)
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- postcss-reduce-initial@6.0.0(postcss@8.5.6):
+ postcss-reduce-initial@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
+ browserslist: 4.28.2
caniuse-api: 3.0.0
- postcss: 8.5.6
+ postcss: 8.5.15
- postcss-reduce-transforms@6.0.0(postcss@8.5.6):
+ postcss-reduce-transforms@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
postcss-selector-parser@6.0.13:
@@ -15977,28 +16778,22 @@ snapshots:
cssesc: 3.0.0
util-deprecate: 1.0.2
- postcss-svgo@6.0.0(postcss@8.5.6):
+ postcss-svgo@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
+ postcss: 8.5.15
postcss-value-parser: 4.2.0
- svgo: 3.0.2
+ svgo: 4.0.1
- postcss-unique-selectors@6.0.0(postcss@8.5.6):
+ postcss-unique-selectors@8.0.0(postcss@8.5.15):
dependencies:
- postcss: 8.5.6
- postcss-selector-parser: 6.0.13
+ postcss: 8.5.15
+ postcss-selector-parser: 7.1.1
postcss-value-parser@4.2.0: {}
- postcss@8.5.6:
+ postcss@8.5.15:
dependencies:
- nanoid: 3.3.11
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
- postcss@8.5.9:
- dependencies:
- nanoid: 3.3.11
+ nanoid: 3.3.12
picocolors: 1.1.1
source-map-js: 1.2.1
@@ -16014,7 +16809,7 @@ snapshots:
pretty-error@4.0.0:
dependencies:
- lodash: 4.17.21
+ lodash: 4.18.1
renderkid: 3.0.0
pretty-format@27.4.2:
@@ -16041,7 +16836,7 @@ snapshots:
forwarded: 0.2.0
ipaddr.js: 1.9.1
- proxy-from-env@1.1.0: {}
+ proxy-from-env@2.1.0: {}
prr@1.0.1:
optional: true
@@ -16132,14 +16927,14 @@ snapshots:
optionalDependencies:
'@types/react': 17.0.75
- react-codemirror-merge@4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
+ react-codemirror-merge@4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
dependencies:
'@babel/runtime': 7.26.0
'@codemirror/merge': 6.10.0
- '@codemirror/state': 6.5.1
- '@codemirror/theme-one-dark': 6.1.2
- '@codemirror/view': 6.36.2
- '@uiw/react-codemirror': 4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ '@codemirror/state': 6.6.0
+ '@codemirror/theme-one-dark': 6.1.3
+ '@codemirror/view': 6.43.0
+ '@uiw/react-codemirror': 4.23.11(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
codemirror: 6.0.1
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
@@ -16185,7 +16980,7 @@ snapshots:
transitivePeerDependencies:
- '@types/react-dom'
- react-docgen-typescript-plugin@1.0.5(typescript@6.0.2)(webpack@5.105.0):
+ react-docgen-typescript-plugin@1.0.5(typescript@6.0.2)(webpack@5.107.2):
dependencies:
debug: 4.4.3
endent: 2.1.0
@@ -16195,7 +16990,7 @@ snapshots:
react-docgen-typescript: 2.2.2(typescript@6.0.2)
tslib: 2.8.1
typescript: 6.0.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
transitivePeerDependencies:
- supports-color
@@ -16209,7 +17004,7 @@ snapshots:
'@babel/traverse': 7.29.0
'@babel/types': 7.29.0
'@types/babel__core': 7.20.5
- '@types/babel__traverse': 7.20.4
+ '@types/babel__traverse': 7.28.0
'@types/doctrine': 0.0.9
'@types/resolve': 1.20.6
doctrine: 3.0.0
@@ -16284,7 +17079,7 @@ snapshots:
react-is@17.0.2: {}
- react-json-view-lite@1.2.1(react@17.0.2):
+ react-json-view-lite@1.5.0(react@17.0.2):
dependencies:
react: 17.0.2
@@ -16307,33 +17102,35 @@ snapshots:
optionalDependencies:
'@types/react': 17.0.75
- react-select-async-paginate@0.7.11(@types/react@17.0.75)(react-select@5.2.1(@babel/core@7.23.6)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2):
+ react-select-async-paginate@0.7.11(@types/react@17.0.75)(react-select@5.10.2(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2):
dependencies:
'@seznam/compose-react-refs': 1.0.6
'@vtaits/use-lazy-ref': 0.1.4(react@17.0.2)
krustykrab: 1.1.0
react: 17.0.2
- react-select: 5.2.1(@babel/core@7.23.6)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ react-select: 5.10.2(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
sleep-promise: 9.1.0
use-is-mounted-ref: 1.5.0(react@17.0.2)
use-latest: 1.3.0(@types/react@17.0.75)(react@17.0.2)
transitivePeerDependencies:
- '@types/react'
- react-select@5.2.1(@babel/core@7.23.6)(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
+ react-select@5.10.2(@types/react@17.0.75)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
dependencies:
'@babel/runtime': 7.26.0
'@emotion/cache': 11.6.0
- '@emotion/react': 11.6.0(@babel/core@7.23.6)(@types/react@17.0.75)(react@17.0.2)
+ '@emotion/react': 11.14.0(@types/react@17.0.75)(react@17.0.2)
+ '@floating-ui/dom': 1.7.6
'@types/react-transition-group': 4.4.4
- memoize-one: 5.2.1
+ memoize-one: 6.0.0
prop-types: 15.8.1
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
react-transition-group: 4.4.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ use-isomorphic-layout-effect: 1.2.1(@types/react@17.0.75)(react@17.0.2)
transitivePeerDependencies:
- - '@babel/core'
- '@types/react'
+ - supports-color
react-style-singleton@2.2.3(@types/react@17.0.75)(react@17.0.2):
dependencies:
@@ -16494,7 +17291,7 @@ snapshots:
css-select: 4.3.0
dom-converter: 0.2.0
htmlparser2: 6.1.0
- lodash: 4.17.21
+ lodash: 4.18.1
strip-ansi: 6.0.1
require-directory@2.1.1: {}
@@ -16547,7 +17344,7 @@ snapshots:
retry@0.13.1: {}
- rettime@0.10.1: {}
+ rettime@0.11.11: {}
reusify@1.0.4: {}
@@ -16561,26 +17358,26 @@ snapshots:
dependencies:
glob: 7.2.3
- rolldown@1.0.0-rc.15:
+ rolldown@1.0.3:
dependencies:
- '@oxc-project/types': 0.124.0
- '@rolldown/pluginutils': 1.0.0-rc.15
+ '@oxc-project/types': 0.133.0
+ '@rolldown/pluginutils': 1.0.1
optionalDependencies:
- '@rolldown/binding-android-arm64': 1.0.0-rc.15
- '@rolldown/binding-darwin-arm64': 1.0.0-rc.15
- '@rolldown/binding-darwin-x64': 1.0.0-rc.15
- '@rolldown/binding-freebsd-x64': 1.0.0-rc.15
- '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.15
- '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.15
- '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-x64-musl': 1.0.0-rc.15
- '@rolldown/binding-openharmony-arm64': 1.0.0-rc.15
- '@rolldown/binding-wasm32-wasi': 1.0.0-rc.15
- '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.15
- '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.15
+ '@rolldown/binding-android-arm64': 1.0.3
+ '@rolldown/binding-darwin-arm64': 1.0.3
+ '@rolldown/binding-darwin-x64': 1.0.3
+ '@rolldown/binding-freebsd-x64': 1.0.3
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.3
+ '@rolldown/binding-linux-arm64-gnu': 1.0.3
+ '@rolldown/binding-linux-arm64-musl': 1.0.3
+ '@rolldown/binding-linux-ppc64-gnu': 1.0.3
+ '@rolldown/binding-linux-s390x-gnu': 1.0.3
+ '@rolldown/binding-linux-x64-gnu': 1.0.3
+ '@rolldown/binding-linux-x64-musl': 1.0.3
+ '@rolldown/binding-openharmony-arm64': 1.0.3
+ '@rolldown/binding-wasm32-wasi': 1.0.3
+ '@rolldown/binding-win32-arm64-msvc': 1.0.3
+ '@rolldown/binding-win32-x64-msvc': 1.0.3
rollup@4.54.0:
dependencies:
@@ -16666,10 +17463,9 @@ snapshots:
htmlparser2: 8.0.2
is-plain-object: 5.0.0
parse-srcset: 1.0.2
- postcss: 8.5.6
+ postcss: 8.5.15
- sax@1.6.0:
- optional: true
+ sax@1.6.0: {}
saxes@6.0.0:
dependencies:
@@ -16742,10 +17538,6 @@ snapshots:
dependencies:
randombytes: 2.1.0
- serialize-javascript@6.0.2:
- dependencies:
- randombytes: 2.1.0
-
serve-index@1.9.1:
dependencies:
accepts: 1.3.8
@@ -16769,6 +17561,8 @@ snapshots:
set-blocking@2.0.0: {}
+ set-cookie-parser@3.1.0: {}
+
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
@@ -16920,18 +17714,20 @@ snapshots:
source-map-js@1.2.1: {}
- source-map-loader@4.0.1(webpack@5.105.0):
+ source-map-loader@4.0.1(webpack@5.107.2):
dependencies:
abab: 2.0.6
iconv-lite: 0.6.3
source-map-js: 1.2.1
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
+ source-map@0.5.7: {}
+
source-map@0.6.1: {}
space-separated-tokens@2.0.2: {}
@@ -16957,10 +17753,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- speed-measure-webpack-plugin@1.5.0(webpack@5.105.0):
+ speed-measure-webpack-plugin@1.5.0(webpack@5.107.2):
dependencies:
chalk: 4.1.2
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
spinkit@2.0.1: {}
@@ -16972,8 +17768,6 @@ snapshots:
statuses@1.5.0: {}
- statuses@2.0.1: {}
-
statuses@2.0.2: {}
std-env@4.1.0: {}
@@ -16989,10 +17783,10 @@ snapshots:
storybook-css-modules-preset@1.1.1: {}
- storybook@10.3.5(@testing-library/dom@9.3.4)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
+ storybook@10.4.2(@testing-library/dom@9.3.4)(@types/react@17.0.75)(prettier@3.8.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
dependencies:
'@storybook/global': 5.0.0
- '@storybook/icons': 2.0.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ '@storybook/icons': 2.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@testing-library/jest-dom': 6.9.1
'@testing-library/user-event': 14.6.1(@testing-library/dom@9.3.4)
'@vitest/expect': 3.2.4
@@ -17000,11 +17794,14 @@ snapshots:
'@webcontainer/env': 1.1.1
esbuild: 0.27.3
open: 10.2.0
+ oxc-parser: 0.127.0
+ oxc-resolver: 11.20.0
recast: 0.23.11
semver: 7.7.3
use-sync-external-store: 1.6.0(react@17.0.2)
ws: 8.19.0
optionalDependencies:
+ '@types/react': 17.0.75
prettier: 3.8.3
transitivePeerDependencies:
- '@testing-library/dom'
@@ -17132,24 +17929,26 @@ snapshots:
strip-json-comments@3.1.1: {}
- style-loader@3.3.3(webpack@5.105.0):
+ style-loader@3.3.3(webpack@5.107.2):
dependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
- style-loader@4.0.0(webpack@5.105.0):
+ style-loader@4.0.0(webpack@5.107.2):
dependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
style-mod@4.1.2: {}
- stylehacks@6.0.0(postcss@8.5.6):
+ stylehacks@8.0.0(postcss@8.5.15):
dependencies:
- browserslist: 4.28.1
- postcss: 8.5.6
- postcss-selector-parser: 6.0.13
+ browserslist: 4.28.2
+ postcss: 8.5.15
+ postcss-selector-parser: 7.1.1
stylis@4.0.10: {}
+ stylis@4.2.0: {}
+
sucrase@3.34.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.5
@@ -17189,6 +17988,16 @@ snapshots:
csso: 5.0.5
picocolors: 1.1.1
+ svgo@4.0.1:
+ dependencies:
+ commander: 11.1.0
+ css-select: 5.1.0
+ css-tree: 3.2.1
+ css-what: 6.1.0
+ csso: 5.0.5
+ picocolors: 1.1.1
+ sax: 1.6.0
+
swagger2openapi@7.0.8:
dependencies:
call-me-maybe: 1.0.2
@@ -17205,11 +18014,11 @@ snapshots:
transitivePeerDependencies:
- encoding
- swc-loader@0.2.7(@swc/core@1.15.11)(webpack@5.105.0):
+ swc-loader@0.2.7(@swc/core@1.15.11)(webpack@5.107.2):
dependencies:
'@swc/core': 1.15.11
'@swc/counter': 0.1.3
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
symbol-tree@3.2.4: {}
@@ -17239,11 +18048,11 @@ snapshots:
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.0
- postcss: 8.5.6
- postcss-import: 15.1.0(postcss@8.5.6)
- postcss-js: 4.0.1(postcss@8.5.6)
- postcss-load-config: 4.0.1(postcss@8.5.6)
- postcss-nested: 6.0.1(postcss@8.5.6)
+ postcss: 8.5.15
+ postcss-import: 15.1.0(postcss@8.5.15)
+ postcss-js: 4.0.1(postcss@8.5.15)
+ postcss-load-config: 4.0.1(postcss@8.5.15)
+ postcss-nested: 6.0.1(postcss@8.5.15)
postcss-selector-parser: 6.0.13
resolve: 1.22.6
sucrase: 3.34.0
@@ -17252,22 +18061,25 @@ snapshots:
tapable@2.3.0: {}
- terser-webpack-plugin@5.3.16(@swc/core@1.15.11)(esbuild@0.27.3)(webpack@5.105.0):
+ tapable@2.3.3: {}
+
+ terser-webpack-plugin@5.6.1(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack@5.107.2):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.3
- serialize-javascript: 6.0.2
terser: 5.44.1
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
optionalDependencies:
'@swc/core': 1.15.11
+ cssnano: 8.0.1(postcss@8.5.15)
esbuild: 0.27.3
+ postcss: 8.5.15
terser@5.44.1:
dependencies:
'@jridgewell/source-map': 0.3.11
- acorn: 8.15.0
+ acorn: 8.16.0
commander: 2.20.3
source-map-support: 0.5.21
@@ -17301,8 +18113,13 @@ snapshots:
tinyglobby@0.2.15:
dependencies:
- fdir: 6.5.0(picomatch@4.0.3)
- picomatch: 4.0.3
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
tinyrainbow@2.0.0: {}
@@ -17343,7 +18160,7 @@ snapshots:
universalify: 0.2.0
url-parse: 1.5.10
- tough-cookie@6.0.0:
+ tough-cookie@6.0.1:
dependencies:
tldts: 7.0.23
@@ -17404,7 +18221,7 @@ snapshots:
type-fest@1.4.0: {}
- type-fest@5.4.4:
+ type-fest@5.7.0:
dependencies:
tagged-tag: 1.0.0
@@ -17473,13 +18290,13 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
- typescript-eslint@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2):
+ typescript-eslint@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2):
dependencies:
- '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2)
- '@typescript-eslint/utils': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)
- eslint: 9.39.4(jiti@2.6.1)
+ '@typescript-eslint/eslint-plugin': 8.60.1(@typescript-eslint/parser@8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ '@typescript-eslint/parser': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.2)
+ '@typescript-eslint/utils': 8.60.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.2)
+ eslint: 9.39.4(jiti@2.7.0)
typescript: 6.0.2
transitivePeerDependencies:
- supports-color
@@ -17547,7 +18364,7 @@ snapshots:
unplugin@2.3.11:
dependencies:
'@jridgewell/remapping': 2.3.5
- acorn: 8.15.0
+ acorn: 8.16.0
picomatch: 4.0.4
webpack-virtual-modules: 0.6.2
@@ -17580,12 +18397,12 @@ snapshots:
update-browserslist-db@1.0.13(browserslist@4.22.2):
dependencies:
browserslist: 4.22.2
- escalade: 3.1.1
+ escalade: 3.2.0
picocolors: 1.1.1
- update-browserslist-db@1.2.3(browserslist@4.28.1):
+ update-browserslist-db@1.2.3(browserslist@4.28.2):
dependencies:
- browserslist: 4.28.1
+ browserslist: 4.28.2
escalade: 3.2.0
picocolors: 1.1.1
@@ -17674,53 +18491,53 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
- vite-plugin-svgr@4.5.0(rollup@4.54.0)(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)):
+ vite-plugin-svgr@4.5.0(rollup@4.54.0)(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.54.0)
'@svgr/core': 8.1.0
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0)
- vite: 8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
+ vite: 8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
transitivePeerDependencies:
- rollup
- supports-color
- vite-tsconfig-paths@4.3.1(typescript@6.0.2)(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)):
+ vite-tsconfig-paths@4.3.1(typescript@6.0.2)(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.0.1(typescript@6.0.2)
optionalDependencies:
- vite: 8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
+ vite: 8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
transitivePeerDependencies:
- supports-color
- typescript
- vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2):
+ vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
- postcss: 8.5.9
- rolldown: 1.0.0-rc.15
- tinyglobby: 0.2.15
+ postcss: 8.5.15
+ rolldown: 1.0.3
+ tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 25.0.3
esbuild: 0.27.3
fsevents: 2.3.3
- jiti: 2.6.1
+ jiti: 2.7.0
less: 4.4.2
terser: 5.44.1
yaml: 1.10.2
- vitest@4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)):
+ vitest@4.1.8(@types/node@25.0.3)(@vitest/coverage-v8@4.1.8)(jsdom@24.1.3)(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)):
dependencies:
'@vitest/expect': 4.1.8
- '@vitest/mocker': 4.1.8(msw@2.12.10(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
+ '@vitest/mocker': 4.1.8(msw@2.14.6(@types/node@25.0.3)(typescript@6.0.2))(vite@8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2))
'@vitest/pretty-format': 4.1.8
'@vitest/runner': 4.1.8
'@vitest/snapshot': 4.1.8
'@vitest/spy': 4.1.8
'@vitest/utils': 4.1.8
- es-module-lexer: 2.1.0
+ es-module-lexer: 2.0.0
expect-type: 1.3.0
magic-string: 0.30.21
obug: 2.1.1
@@ -17729,9 +18546,9 @@ snapshots:
std-env: 4.1.0
tinybench: 2.9.0
tinyexec: 1.0.2
- tinyglobby: 0.2.15
+ tinyglobby: 0.2.17
tinyrainbow: 3.1.0
- vite: 8.0.8(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.6.1)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
+ vite: 8.0.16(@types/node@25.0.3)(esbuild@0.27.3)(jiti@2.7.0)(less@4.4.2)(terser@5.44.1)(yaml@1.10.2)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 25.0.3
@@ -17765,11 +18582,11 @@ snapshots:
webidl-conversions@7.0.0: {}
- webpack-build-notifier@3.1.0(webpack@5.105.0):
+ webpack-build-notifier@3.1.0(webpack@5.107.2):
dependencies:
node-notifier: 10.0.1
strip-ansi: 6.0.1
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
webpack-bundle-analyzer@5.2.0:
dependencies:
@@ -17788,12 +18605,12 @@ snapshots:
- bufferutil
- utf-8-validate
- webpack-cli@6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0):
+ webpack-cli@6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2):
dependencies:
'@discoveryjs/json-ext': 0.6.3
- '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)
- '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)
- '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.107.2)
+ '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.107.2)
+ '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.4)(webpack@5.107.2)
colorette: 2.0.20
commander: 12.1.0
cross-spawn: 7.0.6
@@ -17802,13 +18619,13 @@ snapshots:
import-local: 3.0.3
interpret: 3.1.1
rechoir: 0.8.0
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
webpack-merge: 6.0.1
optionalDependencies:
webpack-bundle-analyzer: 5.2.0
- webpack-dev-server: 5.2.3(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.105.0)
+ webpack-dev-server: 5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.2)
- webpack-dev-middleware@6.1.3(webpack@5.105.0):
+ webpack-dev-middleware@6.1.3(webpack@5.107.2):
dependencies:
colorette: 2.0.20
memfs: 3.6.0
@@ -17816,9 +18633,9 @@ snapshots:
range-parser: 1.2.1
schema-utils: 4.3.3
optionalDependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
- webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.105.0):
+ webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.107.2):
dependencies:
colorette: 2.0.20
memfs: 4.56.10(tslib@2.8.1)
@@ -17827,11 +18644,11 @@ snapshots:
range-parser: 1.2.1
schema-utils: 4.3.3
optionalDependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
transitivePeerDependencies:
- tslib
- webpack-dev-server@5.2.3(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.105.0):
+ webpack-dev-server@5.2.4(tslib@2.8.1)(webpack-cli@6.0.1)(webpack@5.107.2):
dependencies:
'@types/bonjour': 3.5.13
'@types/connect-history-api-fallback': 1.5.4
@@ -17859,11 +18676,11 @@ snapshots:
serve-index: 1.9.1
sockjs: 0.3.24
spdy: 4.0.2
- webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.105.0)
+ webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.107.2)
ws: 8.19.0
optionalDependencies:
- webpack: 5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1)
- webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ webpack: 5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1)
+ webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2)
transitivePeerDependencies:
- bufferutil
- debug
@@ -17883,44 +18700,49 @@ snapshots:
flat: 5.0.2
wildcard: 2.0.1
- webpack-sources@3.3.3: {}
-
- webpack-virtual-modules@0.6.1: {}
+ webpack-sources@3.5.0: {}
webpack-virtual-modules@0.6.2: {}
- webpack@5.105.0(@swc/core@1.15.11)(esbuild@0.27.3)(webpack-cli@6.0.1):
+ webpack@5.107.2(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack-cli@6.0.1):
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.4(acorn@8.15.0)
- browserslist: 4.28.1
+ acorn: 8.16.0
+ acorn-import-phases: 1.0.4(acorn@8.16.0)
+ browserslist: 4.28.2
chrome-trace-event: 1.0.3
- enhanced-resolve: 5.19.0
- es-module-lexer: 2.0.0
+ enhanced-resolve: 5.22.1
+ es-module-lexer: 2.1.0
eslint-scope: 5.1.1
events: 3.3.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.35
+ loader-runner: 4.3.2
+ mime-db: 1.54.0
neo-async: 2.6.2
schema-utils: 4.3.3
tapable: 2.3.0
- terser-webpack-plugin: 5.3.16(@swc/core@1.15.11)(esbuild@0.27.3)(webpack@5.105.0)
+ terser-webpack-plugin: 5.6.1(@swc/core@1.15.11)(cssnano@8.0.1(postcss@8.5.15))(esbuild@0.27.3)(postcss@8.5.15)(webpack@5.107.2)
watchpack: 2.5.1
- webpack-sources: 3.3.3
+ webpack-sources: 3.5.0
optionalDependencies:
- webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.3)(webpack@5.105.0)
+ webpack-cli: 6.0.1(webpack-bundle-analyzer@5.2.0)(webpack-dev-server@5.2.4)(webpack@5.107.2)
transitivePeerDependencies:
+ - '@minify-html/node'
- '@swc/core'
+ - '@swc/css'
+ - '@swc/html'
+ - clean-css
+ - cssnano
+ - csso
- esbuild
+ - html-minifier-terser
+ - lightningcss
+ - postcss
- uglify-js
websocket-driver@0.7.4:
@@ -18099,7 +18921,7 @@ snapshots:
yargs@17.7.2:
dependencies:
cliui: 8.0.1
- escalade: 3.1.1
+ escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
@@ -18110,13 +18932,11 @@ snapshots:
yocto-queue@1.0.0: {}
- yoctocolors-cjs@2.1.3: {}
-
yup@0.32.11:
dependencies:
'@babel/runtime': 7.26.0
- '@types/lodash': 4.17.21
- lodash: 4.17.21
+ '@types/lodash': 4.17.24
+ lodash: 4.18.1
lodash-es: 4.17.21
nanoclone: 0.2.1
property-expr: 2.0.4