diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index fba37b1d..0c18e711 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,17 +14,18 @@ jobs: deploy-docs: name: Deploy docs runs-on: ubuntu-latest + concurrency: ci-${{ github.ref }} steps: - name: Checkout main - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v2 with: node-version: '16' - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: - go-version: 1.18 + go-version-file: go.mod - name: Generate docs run: go generate -x diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..83979b1f --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,38 @@ +name: Lint + +on: + push: + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + concurrency: ci-lint-${{ github.ref }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install pre-commit deps + run: | + sudo apt install -y pre-commit + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + go install golang.org/x/tools/cmd/goimports@latest + go install github.com/fzipp/gocyclo/cmd/gocyclo@latest + go install -v github.com/go-critic/go-critic/cmd/gocritic@latest + + - name: Hash key for Cache pre-commit + uses: seepine/hash-files@v1 + id: key-hash + with: + patterns: ".pre-commit-config.yaml" + + - name: Cache pre-commit + uses: actions/cache@v3 + with: + path: ~/.cache/pre-commit/ + key: pre-commit-${{ steps.key-hash.outputs.hash }} + + - run: pre-commit run --all-files diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 14e137b2..cce86f68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,13 +10,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: - go-version: 1.18 + go-version-file: go.mod - name: Install PDF generation dependencies run: | diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 9327d198..dddeeefc 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -26,10 +26,9 @@ jobs: body: | Starting snapshot release [`${{ steps.snapshot_id.outputs.SNAPSHOT_ID }}`](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) - - name: Set up Go - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: - go-version: 1.18 + go-version-file: go.mod - name: Install PDF generation dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 76c53958..bd339247 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,37 +2,24 @@ name: Test on: push: - pull_request: jobs: test: name: test runs-on: ubuntu-latest + concurrency: ci-test-${{ github.ref }} steps: - - name: Install bgpq4 and bird2 + - name: Install bgpq4 run: | wget http://ftp.us.debian.org/debian/pool/main/b/bgpq4/bgpq4_0.0.6-2_amd64.deb sudo dpkg -i bgpq4*.deb - sudo apt install -y bird2 - - uses: actions/setup-go@v4 - with: - go-version: '1.20' - cache: false - - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + - uses: actions/setup-go@v5 with: - args: -E gosec + go-version-file: go.mod - - run: go get -v -t -d ./... - - run: sudo chown $(whoami):$(whoami) /run/bird/bird.ctl - - run: sudo chown -R $(whoami):$(whoami) /etc/bird/ - - run: go generate -x - - run: go build -v . - - run: make dep - run: make test-setup - run: make test diff --git a/.gitignore b/.gitignore index 80eac27a..013bacca 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ vendorbuild/cisco/ioxclient vendorbuild/mikrotik/docker-buildx # Testing -test-cache/ test-conf.yml nohup.out coverage.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..885604b8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,51 @@ +exclude: '^\.idea/' +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-added-large-files + - id: check-json + - id: check-toml + - id: check-yaml + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace + + # TODO: upstream + - repo: https://github.com/natesales/goimports-reviser + rev: a807b1af1da00a44880668d880ef7d4ce51feb42 + hooks: + - id: goimports-reviser + + # - repo: https://github.com/igorshubovych/markdownlint-cli + # rev: v0.35.0 + # hooks: + # - id: markdownlint + # # MD013: line too long + # # MD033: no inline HTML + # # MD041: first line in a file should be a top-level heading + # args: [ --disable, MD013, MD033, MD041, "--" ] + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: [ + --skip, "go.*", + -L, "statics,socio-economic" + ] + stages: [ commit, commit-msg ] + exclude_types: [ json ] + + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.1 + hooks: + - id: go-fmt + - id: go-vet + - id: go-imports + - id: go-cyclo + args: [ -over=25 ] + - id: golangci-lint + - id: go-critic + - id: go-build + - id: go-mod-tidy diff --git a/Makefile b/Makefile index fe27bb15..b1357b9d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ -dep: - pip3 install flask +down: + docker rm -f pathvector-peeringdb-test-api || true + docker rm -f pathvector-bird || true + sudo ip link del dev dummy0 || true dummy-iface: # Allow UDP ping. For more information, see https://github.com/go-ping/ping#linux @@ -9,17 +11,36 @@ dummy-iface: sudo ip addr add dev dummy0 2001:db8::1/64 sudo ip link set dev dummy0 up -peeringdb-test-harness: - nohup python3 tests/peeringdb/peeringdb-test-api.py & +build-pdb: + docker build -t peeringdb-test-api tests/peeringdb -test-setup: dummy-iface peeringdb-test-harness +run-pdb: + docker rm -f pathvector-peeringdb-test-api || true + docker run --name pathvector-peeringdb-test-api -d -p 5001:5001 peeringdb-test-api + +run-bird: + docker rm -f pathvector-bird || true + rm -rf /tmp/bird-conf || true + mkdir -p /tmp/bird-conf + echo "protocol device {}" > /tmp/bird-conf/bird.conf + docker run \ + -d \ + --privileged \ + --name pathvector-bird \ + -p 5002:5002 \ + -v $(shell pwd)/tests/bird-entrypoint.sh:/entrypoint.sh \ + -v /tmp/bird-conf/:/etc/bird/ \ + -v /tmp/test-cache/:/tmp/test-cache/ \ + pierky/bird:2.16 /entrypoint.sh + +pdb-api: build-pdb run-pdb + +test-setup: dummy-iface run-bird pdb-api test: - export PATHVECTOR_TEST=1 && go test -v -race -coverprofile=coverage.txt -covermode=atomic ./pkg/... ./cmd/... + go test -v -p 1 -coverprofile=coverage.txt -covermode=atomic ./pkg/... ./cmd/... -test-teardown: - pkill -f tests/peeringdb/peeringdb-test-api.py - sudo ip link del dev dummy0 - rm -f nohup.out +test-sequence: test-setup test down -test-sequence: test-setup test test-teardown +snapshot: + goreleaser --snapshot --clean diff --git a/cmd/bird_fmt.go b/cmd/bird_fmt.go new file mode 100644 index 00000000..9a6ed872 --- /dev/null +++ b/cmd/bird_fmt.go @@ -0,0 +1,80 @@ +package cmd + +import ( + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + "github.com/natesales/pathvector/pkg/bird" + "github.com/natesales/pathvector/pkg/util/log" +) + +func init() { + rootCmd.AddCommand(birdFmtCmd) +} + +func isDir(path string) (bool, error) { + fi, err := os.Stat(path) + if err != nil { + return false, err + } + + return fi.IsDir(), nil +} + +func formatFile(path string) error { + if !strings.HasSuffix(path, ".conf") { + log.Debugf("Skipping %s", path) + return nil + } + + log.Infof("Formatting %s", path) + + unformatted, err := os.ReadFile(path) + if err != nil { + return err + } + + formatted := bird.Reformat(string(unformatted)) + + if err := os.WriteFile(path, []byte(formatted), 0644); err != nil { + return err + } + + return nil +} + +var birdFmtCmd = &cobra.Command{ + Use: "bird-fmt [file/directory]", + Short: "Format BIRD config", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + log.Fatal("No file/directory specified") + } + + target := args[0] + + // Check if directory + dir, err := isDir(target) + if err != nil { + log.Fatal(err) + } + if dir { + // For file in walk directory + if err := filepath.Walk(target, func(path string, info os.FileInfo, err error) error { + if err != nil { + log.Fatal(err) + } + return formatFile(path) + }); err != nil { + log.Fatal(err) + } + } else { + if err := formatFile(target); err != nil { + log.Fatal(err) + } + } + }, +} diff --git a/cmd/birdsh.go b/cmd/birdsh.go index 0fbc7106..9c67e818 100644 --- a/cmd/birdsh.go +++ b/cmd/birdsh.go @@ -2,15 +2,14 @@ package cmd import ( "bufio" - "fmt" "net" "os" "strings" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/bird" + "github.com/natesales/pathvector/pkg/util/log" ) var socket = "" @@ -50,7 +49,7 @@ var birdshCmd = &cobra.Command{ r := bufio.NewReader(os.Stdin) for { - fmt.Print("bird> ") + log.Printf("bird> ") cmd, _ := r.ReadString('\n') cmd = strings.ReplaceAll(cmd, "\n", "") if cmd != "" { diff --git a/cmd/config.go b/cmd/config.go index 82978f38..b7f8ed12 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -10,11 +10,11 @@ import ( "time" "github.com/natesales/logknife" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/bird" "github.com/natesales/pathvector/pkg/process" + "github.com/natesales/pathvector/pkg/util/log" ) var sensitiveKeys = []string{ @@ -75,7 +75,7 @@ var configCmd = &cobra.Command{ buf += "# Config" } buf += fmt.Sprintf(" exported from %s on %s\n", configFile, time.Now().Format(time.RFC822Z)) - fmt.Println(buf) + log.Println(buf) if sanitize { // Apply sanitized keys @@ -84,9 +84,11 @@ var configCmd = &cobra.Command{ config = re.ReplaceAllString(config, fmt.Sprintf("${1}%s: REDACTED", key)) } - logknife.Knife(bytes.NewBuffer([]byte(config)), false, true, false, "") + var outBuf bytes.Buffer + logknife.Knife(bytes.NewBuffer([]byte(config)), &outBuf, false, true, false, "") + log.Println(outBuf.String()) } else { - fmt.Print(config) + log.Println(config) } }, } diff --git a/cmd/config_test.go b/cmd/config_test.go index f6a444aa..6f119c1d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -2,6 +2,10 @@ package cmd import ( "testing" + + "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/util/log" ) func TestConfig(t *testing.T) { @@ -9,9 +13,12 @@ func TestConfig(t *testing.T) { "config", "-c", "../tests/generate-complex.yml", }) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } + + out := log.Capture() + defer log.ResetCapture() + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "# Pathvector devel") + assert.Contains(t, out.String(), "asn: 65530") } func TestSanitizeConfig(t *testing.T) { @@ -20,7 +27,11 @@ func TestSanitizeConfig(t *testing.T) { "-c", "../tests/generate-complex.yml", "--sanitize", }) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } + + out := log.Capture() + defer log.ResetCapture() + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "# Pathvector devel") + assert.Contains(t, out.String(), "asn: 65530") + assert.Contains(t, out.String(), "- 2001:db8:") } diff --git a/cmd/ctl.go b/cmd/ctl.go new file mode 100644 index 00000000..3afbe87b --- /dev/null +++ b/cmd/ctl.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + "path" + "strings" + + "github.com/lithammer/fuzzysearch/fuzzy" + + "github.com/natesales/pathvector/pkg/templating" + "github.com/natesales/pathvector/pkg/util/log" +) + +func protocols(birdDirectory string) (map[string]*templating.Protocol, error) { + // Read protocol names map + var protos = map[string]*templating.Protocol{} + contents, err := os.ReadFile(path.Join(birdDirectory, "protocols.json")) + if err != nil { + return nil, fmt.Errorf("reading protocol names: %v", err) + } + if err := json.Unmarshal(contents, &protos); err != nil { + return nil, fmt.Errorf("unmarshalling protocol names: %v", err) + } + + return protos, nil +} + +// normalize makes a string all lowercase and removes spaces, dashes, and underscores +func normalize(s string) string { + return strings.ReplaceAll( + strings.ReplaceAll( + strings.ReplaceAll(strings.ToLower(s), " ", ""), + "-", ""), + "_", "") +} + +// protocolByQuery returns a BIRD BGP protocol string by a given name +func protocolByQuery(query string, protocols map[string]*templating.Protocol) (string, string) { + if query == "all" { + return "all", "all" + } + + // Expand AFI suffix + if strings.HasSuffix(query, "4") && !strings.HasSuffix(query, "v4") { + query = strings.TrimSuffix(query, "4") + " v4" + } else if strings.HasSuffix(query, "6") && !strings.HasSuffix(query, "v6") { + query = strings.TrimSuffix(query, "6") + " v6" + } + + // TODO: This doesn't return the same result for an identical query + query = normalize(query) + for birdProto, meta := range protocols { + if fuzzy.Match(query, normalize(birdProto)) || fuzzy.Match(query, normalize(meta.Name)) { + return birdProto, meta.Name + } + } + return "", "" +} + +// confirmYesNo asks a [y/N] question and returns true if the user selects yes +func confirmYesNo(question string) bool { + log.Printf("%s [y/N] ", question) + var response string + _, _ = fmt.Scanln(&response) + return response == "y" || response == "Y" +} diff --git a/cmd/ctl_test.go b/cmd/ctl_test.go new file mode 100644 index 00000000..82a17039 --- /dev/null +++ b/cmd/ctl_test.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/templating" +) + +func TestCtlProtocolByQuery(t *testing.T) { + protocolsJSON := `{"HURRICANE_ELECTRIC_AS6939_v4":{"Name":"Hurricane Electric","Tags":null},"HURRICANE_ELECTRIC_AS6939_v6":{"Name":"Hurricane Electric","Tags":null}}` + var protocols map[string]*templating.Protocol + assert.Nil(t, json.Unmarshal([]byte(protocolsJSON), &protocols)) + + for _, tc := range []struct { + expected string + query string + }{ + {"HURRICANE_ELECTRIC_AS6939_v4", "Hurricane Electric v4"}, + {"HURRICANE_ELECTRIC_AS6939_v4", "hurricane v4"}, + {"HURRICANE_ELECTRIC_AS6939_v6", "he v6"}, + } { + t.Run(tc.query, func(t *testing.T) { + birdProto, _ := protocolByQuery(tc.query, protocols) + assert.Equal(t, tc.expected, birdProto) + }) + } +} diff --git a/cmd/docs_test.go b/cmd/docs_test.go index b5a6600c..727cabc9 100644 --- a/cmd/docs_test.go +++ b/cmd/docs_test.go @@ -1,20 +1,19 @@ package cmd import ( - "os" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/util/log" ) func TestDocs(t *testing.T) { - old := os.Stdout - _, w, _ := os.Pipe() - os.Stdout = w rootCmd.SetArgs([]string{ "docs", }) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } - w.Close() - os.Stdout = old + out := log.Capture() + defer log.ResetCapture() + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "# Configuration\n") } diff --git a/cmd/dump.go b/cmd/dump.go index 34a0568a..432ab0b6 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -4,11 +4,11 @@ import ( "fmt" "strings" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "gopkg.in/yaml.v3" "github.com/natesales/pathvector/pkg/util" + "github.com/natesales/pathvector/pkg/util/log" ) var ( @@ -34,7 +34,7 @@ var dumpCmd = &cobra.Command{ if err != nil { log.Fatal(err) } - fmt.Println(string(yamlBytes)) + log.Println(string(yamlBytes)) } else { var data [][]string for peerName, peerData := range c.Peers { diff --git a/cmd/dump_test.go b/cmd/dump_test.go index 68cd3a02..8a1792cf 100644 --- a/cmd/dump_test.go +++ b/cmd/dump_test.go @@ -1,57 +1,41 @@ package cmd import ( - "os" - "path/filepath" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/util/log" ) func TestDumpTable(t *testing.T) { - old := os.Stdout - _, w, _ := os.Pipe() - os.Stdout = w - - // Make temporary cache directory - if err := os.Mkdir("test-cache", 0755); err != nil && !os.IsExist(err) { - t.Error(err) - } + mkTmpCache(t) args := []string{ "dump", "--verbose", "--dry-run", } - files, err := filepath.Glob("../tests/generate-*.yml") - if err != nil { - t.Error(err) - } - if len(files) < 1 { - t.Fatal("No test files found") - } - for _, testFile := range files { + + withGenerateConfigs(t, func(testFile string) { args = append(args, []string{ "--config", testFile, }...) t.Logf("running dump integration with args %v", args) - rootCmd.SetArgs(args) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } - } - w.Close() - os.Stdout = old + out := log.Capture() + defer log.ResetCapture() + + rootCmd.SetArgs(args) + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "PREPENDS") + assert.Contains(t, out.String(), "NAME") + assert.Contains(t, out.String(), "ASN") + }) } func TestDumpYAML(t *testing.T) { - old := os.Stdout - _, w, _ := os.Pipe() - os.Stdout = w - - // Make temporary cache directory - if err := os.Mkdir("test-cache", 0755); err != nil && !os.IsExist(err) { - t.Error(err) - } + mkTmpCache(t) args := []string{ "dump", @@ -59,24 +43,17 @@ func TestDumpYAML(t *testing.T) { "--verbose", "--dry-run", } - files, err := filepath.Glob("../tests/generate-*.yml") - if err != nil { - t.Error(err) - } - if len(files) < 1 { - t.Fatal("No test files found") - } - for _, testFile := range files { + withGenerateConfigs(t, func(testFile string) { args = append(args, []string{ "--config", testFile, }...) t.Logf("running dump integration with args %v", args) - rootCmd.SetArgs(args) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } - } - w.Close() - os.Stdout = old + out := log.Capture() + defer log.ResetCapture() + + rootCmd.SetArgs(args) + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "global-config: \"\"") + }) } diff --git a/cmd/generate_test.go b/cmd/generate_test.go index 32aa6452..dca8ef93 100644 --- a/cmd/generate_test.go +++ b/cmd/generate_test.go @@ -1,37 +1,30 @@ package cmd import ( - "os" - "path/filepath" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/util/log" ) func TestGenerate(t *testing.T) { - // Make temporary cache directory - if err := os.Mkdir("test-cache", 0755); err != nil && !os.IsExist(err) { - t.Error(err) - } + mkTmpCache(t) - args := []string{ + baseArgs := []string{ "generate", "--verbose", "--dry-run", } - files, err := filepath.Glob("../tests/generate-*.yml") - if err != nil { - t.Error(err) - } - if len(files) < 1 { - t.Fatal("No test files found") - } - for _, testFile := range files { - args = append(args, []string{ + + withGenerateConfigs(t, func(testFile string) { + args := append(baseArgs, []string{ "--config", testFile, }...) t.Logf("running generate integration with args %v", args) rootCmd.SetArgs(args) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } - } + _ = log.Capture() + defer log.ResetCapture() + assert.Nil(t, rootCmd.Execute()) + }) } diff --git a/cmd/match.go b/cmd/match.go index 98132b28..feb8dba9 100644 --- a/cmd/match.go +++ b/cmd/match.go @@ -1,14 +1,13 @@ package cmd import ( - "fmt" "strconv" "strings" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/match" + "github.com/natesales/pathvector/pkg/util/log" ) var ( @@ -47,6 +46,6 @@ var matchCmd = &cobra.Command{ log.Fatal(err) } - fmt.Println(match.CommonIXs(uint32(matchLocalASN), uint32(peerASN), yamlFormat, peeringDbTimeout, c.PeeringDBAPIKey)) + log.Println(match.CommonIXs(uint32(matchLocalASN), uint32(peerASN), yamlFormat, peeringDbTimeout, c.PeeringDBAPIKey)) }, } diff --git a/cmd/match_test.go b/cmd/match_test.go index c9619d35..ba902418 100644 --- a/cmd/match_test.go +++ b/cmd/match_test.go @@ -2,14 +2,14 @@ package cmd import ( "fmt" - "os" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/util/log" ) func TestMatch(t *testing.T) { - old := os.Stdout - _, w, _ := os.Pipe() - os.Stdout = w baseArgs := []string{ "match", "--verbose", @@ -23,25 +23,25 @@ func TestMatch(t *testing.T) { {112, 44977}, } for _, tc := range testCases { + out := log.Capture() rootCmd.SetArgs(append(baseArgs, []string{ "-l", fmt.Sprintf("%d", tc.asnA), "-c", "../tests/generate-simple.yml", fmt.Sprintf("%d", tc.asnB), }...)) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "Finished loading config") + log.ResetCapture() // Local ASN from config file + out = log.Capture() rootCmd.SetArgs(append(baseArgs, []string{ "-c", "../tests/generate-simple.yml", "-l", "0", fmt.Sprintf("%d", tc.asnB), }...)) - if err := rootCmd.Execute(); err != nil { - t.Error(err) - } + assert.Nil(t, rootCmd.Execute()) + assert.Contains(t, out.String(), "Finished loading config") + log.ResetCapture() } - w.Close() - os.Stdout = old } diff --git a/cmd/optimizer.go b/cmd/optimizer.go index 0b0ce87c..33b1dee7 100644 --- a/cmd/optimizer.go +++ b/cmd/optimizer.go @@ -3,10 +3,10 @@ package cmd import ( "fmt" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/optimizer" + "github.com/natesales/pathvector/pkg/util/log" ) func init() { diff --git a/cmd/optimizer_test.go b/cmd/optimizer_test.go index 8584d580..5aa7073d 100644 --- a/cmd/optimizer_test.go +++ b/cmd/optimizer_test.go @@ -7,38 +7,45 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/natesales/pathvector/pkg/util" ) func TestOptimizer(t *testing.T) { - args := []string{ - "--verbose", - } + t.Skip() + files, err := filepath.Glob("../tests/probe-*.yml") assert.Nil(t, err) assert.GreaterOrEqual(t, 1, len(files)) for _, testFile := range files { - // Run pathvector to generate config first, so there is a config to modify - rootCmd.SetArgs(append(args, []string{ - "generate", - "--config", testFile, - }...)) - t.Logf("Running pre-optimizer generate: %v", args) - assert.Nil(t, rootCmd.Execute()) - - args = append(args, []string{ - "optimizer", - "--config", testFile, - }...) - t.Logf("running probe integration with args %v", args) - rootCmd.SetArgs(args) - assert.Nil(t, rootCmd.Execute()) - - // Check if local pref is lowered - checkFile, err := os.ReadFile("test-cache/AS65510_EXAMPLE.conf") - assert.Nil(t, err) - if !strings.Contains(string(checkFile), "bgp_local_pref = 80; # pathvector:localpref") { - t.Errorf("expected bgp_local_pref = 80 but not found in file") - } + t.Run(testFile, func(t *testing.T) { + for _, dir := range []string{"/tmp/test-cache", "/tmp/bird-conf"} { + if err := util.RemoveFileGlob(dir + "/*"); err != nil { + t.Errorf("failed to remove %s: %v", dir, err) + } + } + + baseArgs := []string{ + "--verbose", + "--config", testFile, + } + + // Run pathvector to generate config first, so there is a config to modify + rootCmd.SetArgs(append(baseArgs, "generate")) + t.Log("Running pre-optimizer generate") + assert.Nil(t, rootCmd.Execute()) + + rootCmd.SetArgs(append(baseArgs, "optimizer")) + t.Log("Running probe integration") + assert.Nil(t, rootCmd.Execute()) + + // Check if local pref is lowered + checkFile, err := os.ReadFile("/tmp/bird-conf/AS65510_EXAMPLE.conf") + assert.Nil(t, err) + if !strings.Contains(string(checkFile), "bgp_local_pref = 80; # pathvector:localpref") { + t.Errorf("expected bgp_local_pref = 80 but not found in file") + } + }) } } diff --git a/cmd/reload_restart.go b/cmd/reload_restart.go new file mode 100644 index 00000000..8e039587 --- /dev/null +++ b/cmd/reload_restart.go @@ -0,0 +1,87 @@ +package cmd + +import ( + "fmt" + "slices" + "strings" + + "github.com/spf13/cobra" + + "github.com/natesales/pathvector/pkg/bird" + "github.com/natesales/pathvector/pkg/util/log" +) + +func init() { + rootCmd.AddCommand(&cobra.Command{ + Use: "reload [in|out] [session]", + Short: "Reload a session", + Run: func(cmd *cobra.Command, args []string) { + reloadRestartHandler(args, "reload") + }, + }) + rootCmd.AddCommand(&cobra.Command{ + Use: "restart [session]", + Short: "Restart a session", + Run: func(cmd *cobra.Command, args []string) { + reloadRestartHandler(args, "restart") + }, + }) +} + +func usage() { + log.Fatal("Usage: pathvector reload [direction] [session]") +} + +func parseArgs(args []string) (string, string) { + if len(args) == 0 { + usage() + } + + direction := args[0] + query := strings.Join(args[1:], " ") + if !slices.Contains([]string{"in", "out"}, direction) { + direction = "both" + query = strings.Join(args, " ") + } + + return query, direction +} + +func reloadRestartHandler(args []string, verb string) { + // Load config file + c, err := loadConfig() + if err != nil { + log.Fatal(err) + } + + query, direction := parseArgs(args) + + // Load protocol names map + protos, err := protocols(c.BIRDDirectory) + if err != nil { + log.Fatal(err) + } + + log.Debugf("Looking for protocol for %s", query) + birdProtoName, richName := protocolByQuery(query, protos) + if birdProtoName == "" { + log.Fatalf("no protocol found for query: %s", query) + } + + if !confirmYesNo(fmt.Sprintf("Are you sure you want to %s %s (%s)?", verb, richName, birdProtoName)) { + log.Fatal("Cancelled") + } + + // Reload protocol + birdCmd := verb + if verb == "reload" && direction != "both" { + birdCmd += " " + direction + } + birdCmd += " " + birdProtoName + + out, _, err := bird.RunCommand(birdCmd, c.BIRDSocket) + if err != nil { + log.Fatal(err) + } + log.Info(out) +} diff --git a/cmd/reload_restart_test.go b/cmd/reload_restart_test.go new file mode 100644 index 00000000..57bd0704 --- /dev/null +++ b/cmd/reload_restart_test.go @@ -0,0 +1,29 @@ +package cmd + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCtlReloadResetParseArgs(t *testing.T) { + for _, tc := range []struct { + args []string + expQuery string + expDirection string + }{ + {[]string{"in", "all"}, "all", "in"}, + {[]string{"out", "all"}, "all", "out"}, + {[]string{"all"}, "all", "both"}, + {[]string{"in", "he"}, "he", "in"}, + {[]string{"out", "he"}, "he", "out"}, + {[]string{"he"}, "he", "both"}, + } { + t.Run(strings.Join(tc.args, " "), func(t *testing.T) { + query, direction := parseArgs(tc.args) + assert.Equal(t, tc.expQuery, query) + assert.Equal(t, tc.expDirection, direction) + }) + } +} diff --git a/cmd/root.go b/cmd/root.go index ca15b45a..0fb4a180 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,12 +3,12 @@ package cmd import ( "os" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/config" "github.com/natesales/pathvector/pkg/plugin" "github.com/natesales/pathvector/pkg/process" + "github.com/natesales/pathvector/pkg/util/log" ) // These are set indirectly by the build process. The cmd.Execute() function takes these from the main package and sets them in this (cmd) package. diff --git a/cmd/status.go b/cmd/status.go index 0d8f6f45..b1d0f7c8 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -1,19 +1,16 @@ package cmd import ( - "encoding/json" "fmt" - "os" - "path" "strings" "github.com/fatih/color" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/bird" "github.com/natesales/pathvector/pkg/templating" "github.com/natesales/pathvector/pkg/util" + "github.com/natesales/pathvector/pkg/util/log" ) var ( @@ -32,7 +29,7 @@ func init() { } var statusCmd = &cobra.Command{ - Use: "status", + Use: "status [session]", Aliases: []string{"s", "status"}, Short: "Show protocol status", Run: func(cmd *cobra.Command, args []string) { @@ -40,71 +37,91 @@ var statusCmd = &cobra.Command{ if err != nil { log.Warnf("Error loading config, falling back to no-config output parsing: %s", err) } + // TODO: Use defaults - commandOutput, _, err := bird.RunCommand("show protocols all", c.BIRDSocket) - if err != nil { - log.Fatal(err) - } + if len(args) > 0 { + query := strings.Join(args, " ") - // Read protocol names map - var protocols map[string]*templating.Protocol - if !realProtocolNames { - contents, err := os.ReadFile(path.Join("/etc/bird/", "protocols.json")) + // Load protocol names map + protos, err := protocols(c.BIRDDirectory) if err != nil { - log.Fatalf("Reading protocol names: %v", err) + log.Fatal(err) } - if err := json.Unmarshal(contents, &protocols); err != nil { - log.Fatalf("Unmarshalling protocol names: %v", err) + + log.Debugf("Looking for protocol for %s", query) + birdProtoName, richName := protocolByQuery(query, protos) + if birdProtoName == "" { + log.Fatalf("no protocol found for query: %s", query) } - } - protocolStates, err := bird.ParseProtocols(commandOutput) - if err != nil { - log.Fatal(err) - } + log.Infof("Showing status for %s (%s)", richName, birdProtoName) + commandOutput, _, err := bird.RunCommand("show protocols all "+birdProtoName, c.BIRDSocket) + if err != nil { + log.Fatal(err) + } + log.Println(commandOutput) + } else { + commandOutput, _, err := bird.RunCommand("show protocols all", c.BIRDSocket) + if err != nil { + log.Fatal(err) + } - header := []string{"Peer", "AS", "Neighbor", "State", "In", "Out", "Since", "Info"} - if showTags { - header = append(header, "Tags") - } - util.PrintTable(header, func() [][]string { - var table [][]string - for _, protocolState := range protocolStates { - if !onlyBGP || protocolState.BGP != nil { - neighborAddr, neighborAS := "-", "-" - if protocolState.BGP != nil { - neighborAS = parseTableInt(protocolState.BGP.NeighborAS) - neighborAddr = protocolState.BGP.NeighborAddress - } + var protos map[string]*templating.Protocol + if !realProtocolNames { + protos, err = protocols(c.BIRDDirectory) + if err != nil { + log.Fatal(err) + } + } - // Lookup peer in protocol JSON - protocolName := protocolState.Name - var tags []string - if p, found := protocols[protocolState.Name]; found { - protocolName = p.Name - tags = p.Tags - } + protocolStates, err := bird.ParseProtocols(commandOutput) + if err != nil { + log.Fatal(err) + } - if len(tagFilter) == 0 || containsAny(tagFilter, tags) { - row := []string{ - protocolName, - neighborAS, - neighborAddr, - colorStatus(protocolState.State), - parseTableInt(protocolState.Routes.Imported), - parseTableInt(protocolState.Routes.Exported), - protocolState.Since, - colorStatus(protocolState.Info), + header := []string{"Peer", "AS", "Neighbor", "State", "In", "Out", "Since", "Info"} + if showTags { + header = append(header, "Tags") + } + util.PrintTable(header, func() [][]string { + var table [][]string + for _, protocolState := range protocolStates { + if !onlyBGP || protocolState.BGP != nil { + neighborAddr, neighborAS := "-", "-" + if protocolState.BGP != nil { + neighborAS = parseTableInt(protocolState.BGP.NeighborAS) + neighborAddr = protocolState.BGP.NeighborAddress } - if showTags { - row = append(row, strings.Join(tags, ", ")) + + // Lookup peer in protocol JSON + protocolName := protocolState.Name + var tags []string + if p, found := protos[protocolState.Name]; found { + protocolName = p.Name + tags = p.Tags + } + + if len(tagFilter) == 0 || containsAny(tagFilter, tags) { + row := []string{ + protocolName, + neighborAS, + neighborAddr, + colorStatus(protocolState.State), + parseTableInt(protocolState.Routes.Imported), + parseTableInt(protocolState.Routes.Exported), + protocolState.Since, + colorStatus(protocolState.Info), + } + if showTags { + row = append(row, strings.Join(tags, ", ")) + } + table = append(table, row) } - table = append(table, row) } } - } - return table - }()) + return table + }()) + } }, } @@ -128,12 +145,14 @@ func parseTableInt(i int) string { } func colorStatus(s string) string { - if s == "up" || s == "Established" { + switch { + case s == "up" || s == "Established": return color.GreenString(s) - } else if strings.Contains(s, "Error") || s == "down" { + case strings.Contains(s, "Error") || s == "down": return color.RedString(s) - } else if strings.Contains(s, "Connect") || s == "start" { + case strings.Contains(s, "Connect") || s == "start": return color.YellowString(s) + default: + return s } - return s } diff --git a/cmd/status_test.go b/cmd/status_test.go index ba599ea8..fb6ccc38 100644 --- a/cmd/status_test.go +++ b/cmd/status_test.go @@ -9,7 +9,7 @@ import ( func TestStatus(t *testing.T) { //nolint:golint,gosec - err := os.WriteFile("/etc/bird/protocols.json", []byte(`{"EXAMPLE_AS65510_v4":{"Name":"Example","Tags":null},"EXAMPLE_AS65510_v6":{"Name":"Example","Tags":null}}`), 0644) + err := os.WriteFile("/tmp/bird-conf/protocols.json", []byte(`{"EXAMPLE_AS65510_v4":{"Name":"Example","Tags":null},"EXAMPLE_AS65510_v6":{"Name":"Example","Tags":null}}`), 0644) assert.Nil(t, err) rootCmd.SetArgs([]string{ diff --git a/cmd/test_utils.go b/cmd/test_utils.go new file mode 100644 index 00000000..71ff924c --- /dev/null +++ b/cmd/test_utils.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +// withGenerateConfigs runs a callback on all generate config files +func withGenerateConfigs(t *testing.T, callback func(string)) { + files, err := filepath.Glob("../tests/generate-*.yml") + assert.Nil(t, err) + assert.Greater(t, len(files), 1) + + for _, testFile := range files { + t.Run(testFile, func(t *testing.T) { + callback(testFile) + }) + } +} + +// mkTmpCache makes the test-cache directory +func mkTmpCache(t *testing.T) { + dir := "/tmp/test-cache" + _ = os.RemoveAll(dir) + if err := os.Mkdir(dir, 0755); err != nil { + assert.Nil(t, err) + } +} diff --git a/cmd/version.go b/cmd/version.go index f8fd0153..bdc50cb8 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -5,12 +5,12 @@ import ( "os" "reflect" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/bird" "github.com/natesales/pathvector/pkg/plugin" "github.com/natesales/pathvector/pkg/process" + "github.com/natesales/pathvector/pkg/util/log" ) func init() { @@ -34,7 +34,7 @@ Built %s on %s } func printVersionBanner() { - fmt.Println(versionBanner()) + log.Println(versionBanner()) } var versionCmd = &cobra.Command{ @@ -58,6 +58,6 @@ var versionCmd = &cobra.Command{ if err != nil { log.Fatal(err) } - fmt.Printf("BIRD: %s\n", birdVersion) + log.Printf("BIRD: %s\n", birdVersion) }, } diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 0adaad24..1eab517a 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -10,6 +10,7 @@ Usage: pathvector [command] Available Commands: + bird-fmt Format BIRD config birdsh Lightweight BIRD shell completion Generate the autocompletion script for the specified shell config Export configuration, optionally sanitized with logknife @@ -18,6 +19,8 @@ Available Commands: help Help about any command match Find common IXPs for a given ASN optimizer Start optimization daemon + reload Reload a session + restart Restart a session status Show protocol status version Show version information diff --git a/docs/docs/configuration.md b/docs/docs/configuration.md index 60737933..a50c2c6f 100644 --- a/docs/docs/configuration.md +++ b/docs/docs/configuration.md @@ -221,6 +221,14 @@ Additional command line arguments to pass to bgpq4 |------|---------|------------| | string | | | +### `bgpq-binary` + +Path to bgpq4 binary + +| Type | Default | Validation | +|------|---------|------------| +| string | bgpq4 | | + ### `keep-filtered` Should filtered routes be kept in memory? @@ -489,6 +497,14 @@ List of static routes to include in BIRD |------|---------|------------| | map[string]string | | | +### `kstatics` + +List of static routes to include in BIRD and send to the kernel + +| Type | Default | Validation | +|------|---------|------------| +| map[string]string | | | + ### `srd-communities` List of communities to filter routes exported to kernel (if list is not empty, all other prefixes will not be exported) @@ -1528,5 +1544,3 @@ List of virtual IPs | Type | Default | Validation | |------|---------|------------| | []string | | required,cidr | - - diff --git a/docs/docs/interactive.md b/docs/docs/interactive.md deleted file mode 100644 index d420e08c..00000000 --- a/docs/docs/interactive.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Interactive CLI -sidebar_position: 7 ---- - -Pathvector supports an interactive CLI for configuration. - -``` -$ pathvector cli -pathvector [empty] > enable -pathvector [empty] # init AS65530 192.0.2.1 -Are you sure you want to create a new config with AS65530 (192.0.2.1)? [y/N] y -Config created -pathvector (altair) # set prefixes 192.0.2.0/24,2001:db8::/48 -pathvector (altair) # create peers Example -pathvector (altair) # set peers Example asn 65510 -pathvector (altair) # set peers Example neighbors 192.0.2.1,2001:db8::1 -pathvector (altair) # commit -Persistent configuration updated -pathvector (altair) # run -Starting Pathvector... -``` diff --git a/docs/package-lock.json b/docs/package-lock.json index 3a2d7e2b..a3799608 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -70,66 +70,66 @@ } }, "node_modules/@algolia/cache-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.22.1.tgz", - "integrity": "sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==" + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.16.0.tgz", + "integrity": "sha512-4iHjkSYQYw46pITrNQgXXhvUmcekI8INz1m+SzmqLX8jexSSy4Ky4zfGhZzhhhLHXUP3+x/PK/c0qPjxEvRwKQ==" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz", - "integrity": "sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.16.0.tgz", + "integrity": "sha512-p7RYykvA6Ip6QENxrh99nOD77otVh1sJRivcgcVpnjoZb5sIN3t33eUY1DpB9QSBizcrW+qk19rNkdnZ43a+PQ==", "dependencies": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.16.0" } }, "node_modules/@algolia/client-account": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.22.1.tgz", - "integrity": "sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.16.0.tgz", + "integrity": "sha512-eydcfpdIyuWoKgUSz5iZ/L0wE/Wl7958kACkvTHLDNXvK/b8Z1zypoJavh6/km1ZNQmFpeYS2jrmq0kUSFn02w==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.16.0", + "@algolia/client-search": "4.16.0", + "@algolia/transporter": "4.16.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.22.1.tgz", - "integrity": "sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.16.0.tgz", + "integrity": "sha512-cONWXH3BfilgdlCofUm492bJRWtpBLVW/hsUlfoFtiX1u05xoBP7qeiDwh9RR+4pSLHLodYkHAf5U4honQ55Qg==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.16.0", + "@algolia/client-search": "4.16.0", + "@algolia/requester-common": "4.16.0", + "@algolia/transporter": "4.16.0" } }, "node_modules/@algolia/client-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.22.1.tgz", - "integrity": "sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.16.0.tgz", + "integrity": "sha512-QVdR4019ukBH6f5lFr27W60trRxQF1SfS1qo0IP6gjsKhXhUVJuHxOCA6ArF87jrNkeuHEoRoDU+GlvaecNo8g==", "dependencies": { - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/requester-common": "4.16.0", + "@algolia/transporter": "4.16.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.22.1.tgz", - "integrity": "sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.16.0.tgz", + "integrity": "sha512-irtLafssDGPuhYqIwxqOxiWlVYvrsBD+EMA1P9VJtkKi3vSNBxiWeQ0f0Tn53cUNdSRNEssfoEH84JL97SV2SQ==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.16.0", + "@algolia/requester-common": "4.16.0", + "@algolia/transporter": "4.16.0" } }, "node_modules/@algolia/client-search": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", - "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.16.0.tgz", + "integrity": "sha512-xsfrAE1jO/JDh1wFrRz+alVyW+aA6qnkzmbWWWZWEgVF3EaFqzIf9r1l/aDtDdBtNTNhX9H3Lg31+BRtd5izQA==", "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.16.0", + "@algolia/requester-common": "4.16.0", + "@algolia/transporter": "4.16.0" } }, "node_modules/@algolia/events": { @@ -138,47 +138,47 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "node_modules/@algolia/logger-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.22.1.tgz", - "integrity": "sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==" + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.16.0.tgz", + "integrity": "sha512-U9H8uCzSDuePJmbnjjTX21aPDRU6x74Tdq3dJmdYu2+pISx02UeBJm4kSgc9RW5jcR5j35G9gnjHY9Q3ngWbyQ==" }, "node_modules/@algolia/logger-console": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.22.1.tgz", - "integrity": "sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.16.0.tgz", + "integrity": "sha512-+qymusiM+lPZKrkf0tDjCQA158eEJO2IU+Nr/sJ9TFyI/xkFPjNPzw/Qbc8Iy/xcOXGlc6eMgmyjtVQqAWq6UA==", "dependencies": { - "@algolia/logger-common": "4.22.1" + "@algolia/logger-common": "4.16.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz", - "integrity": "sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.16.0.tgz", + "integrity": "sha512-gK+kvs6LHl/PaOJfDuwjkopNbG1djzFLsVBklGBsSU6h6VjFkxIpo6Qq80IK14p9cplYZfhfaL12va6Q9p3KVQ==", "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.16.0" } }, "node_modules/@algolia/requester-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.22.1.tgz", - "integrity": "sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==" + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.16.0.tgz", + "integrity": "sha512-3Zmcs/iMubcm4zqZ3vZG6Zum8t+hMWxGMzo0/uY2BD8o9q5vMxIYI0c4ocdgQjkXcix189WtZNkgjSOBzSbkdw==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz", - "integrity": "sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.16.0.tgz", + "integrity": "sha512-L8JxM2VwZzh8LJ1Zb8TFS6G3icYsCKZsdWW+ahcEs1rGWmyk9SybsOe1MLnjonGBaqPWJkn9NjS7mRdjEmBtKA==", "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.16.0" } }, "node_modules/@algolia/transporter": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.22.1.tgz", - "integrity": "sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.16.0.tgz", + "integrity": "sha512-H9BVB2EAjT65w7XGBNf5drpsW39x2aSZ942j4boSAAJPPlLmjtj5IpAP7UAtsV8g9Beslonh0bLa1XGmE/P0BA==", "dependencies": { - "@algolia/cache-common": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/requester-common": "4.22.1" + "@algolia/cache-common": "4.16.0", + "@algolia/logger-common": "4.16.0", + "@algolia/requester-common": "4.16.0" } }, "node_modules/@ampproject/remapping": { @@ -194,12 +194,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -270,9 +269,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", + "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", "engines": { "node": ">=6.9.0" } @@ -353,24 +352,25 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", + "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.21.4", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -387,19 +387,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", - "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz", + "integrity": "sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -417,9 +416,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz", + "integrity": "sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", @@ -456,20 +455,31 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dependencies": { + "@babel/types": "^7.18.6" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -487,37 +497,40 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dependencies": { - "@babel/types": "^7.23.0" + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/types": "^7.21.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -546,13 +559,14 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -562,13 +576,16 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -611,37 +628,38 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" }, "engines": { "node": ">=6.9.0" @@ -661,12 +679,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "engines": { @@ -738,9 +756,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -749,9 +767,9 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -763,13 +781,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -778,19 +796,206 @@ "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { @@ -863,9 +1068,9 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -877,9 +1082,9 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -913,9 +1118,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1021,9 +1226,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1050,11 +1255,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1063,15 +1268,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1080,14 +1284,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1096,12 +1298,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1110,12 +1312,20 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/@babel/plugin-transform-classes": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" @@ -1124,13 +1334,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -1139,35 +1349,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", + "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.12.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1176,13 +1378,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1191,12 +1392,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1205,13 +1407,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1220,12 +1421,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1234,13 +1437,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1249,12 +1451,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1264,13 +1465,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1279,13 +1480,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1294,14 +1496,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -1310,119 +1513,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", - "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1447,59 +1544,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1509,43 +1558,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1555,43 +1573,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", + "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1601,9 +1587,9 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1615,11 +1601,11 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz", - "integrity": "sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", + "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1629,9 +1615,9 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", - "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1643,15 +1629,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz", + "integrity": "sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -1675,9 +1661,9 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", - "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1690,12 +1676,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" @@ -1705,9 +1691,9 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1746,9 +1732,9 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1760,9 +1746,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" @@ -1775,9 +1761,9 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1789,9 +1775,9 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1803,9 +1789,9 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1817,14 +1803,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", - "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -1834,26 +1820,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -1863,12 +1834,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1877,42 +1848,38 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/@babel/preset-env": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz", + "integrity": "sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==", + "dependencies": { + "@babel/compat-data": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", + "@babel/plugin-proposal-async-generator-functions": "^7.20.7", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.21.0", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.21.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1922,61 +1889,45 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" + "@babel/plugin-transform-arrow-functions": "^7.20.7", + "@babel/plugin-transform-async-to-generator": "^7.20.7", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.21.0", + "@babel/plugin-transform-classes": "^7.21.0", + "@babel/plugin-transform-computed-properties": "^7.20.7", + "@babel/plugin-transform-destructuring": "^7.21.3", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.21.0", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.20.11", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-modules-systemjs": "^7.20.11", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.21.3", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.20.5", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.20.7", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.21.4", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -1985,6 +1936,57 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.31.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1994,9 +1996,9 @@ } }, "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2007,16 +2009,16 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", - "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.23.3", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.23.3" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2026,15 +2028,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", - "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" }, "engines": { "node": ">=6.9.0" @@ -2072,13 +2074,13 @@ } }, "node_modules/@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -2105,12 +2107,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", + "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2135,19 +2137,19 @@ } }, "node_modules/@docsearch/css": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.2.tgz", - "integrity": "sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.3.3.tgz", + "integrity": "sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==" }, "node_modules/@docsearch/react": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.2.tgz", - "integrity": "sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.3.3.tgz", + "integrity": "sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==", "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.5.2", - "algoliasearch": "^4.19.1" + "@algolia/autocomplete-core": "1.7.4", + "@algolia/autocomplete-preset-algolia": "1.7.4", + "@docsearch/css": "3.3.3", + "algoliasearch": "^4.0.0" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", @@ -2171,27 +2173,27 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.1.1.tgz", - "integrity": "sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.0.tgz", + "integrity": "sha512-J55/WEoIpRcLf3afO5POHPguVZosKmJEQWKBL+K7TAnfuE7i+Y0NPLlkKtnWCehagGsgTqClfQEexH/UT4kELA==", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/runtime": "^7.22.6", - "@babel/runtime-corejs3": "^7.22.6", - "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@slorber/static-site-generator-webpack-plugin": "^4.0.7", "@svgr/webpack": "^6.5.1", "autoprefixer": "^10.4.14", @@ -2252,22 +2254,20 @@ "node": ">=18.0" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/core/node_modules/@docusaurus/mdx-loader": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", - "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", - "dependencies": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", + "node_modules/@docusaurus/mdx-loader": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.0.tgz", + "integrity": "sha512-GWoH4izZKOmFoC+gbI2/y8deH/xKLvzz/T5BsEexBye8EHQlwsA7FMrVa48N063bJBH4FUOiRRXxk5rq9cC36g==", + "dependencies": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@mdx-js/mdx": "^1.6.22", "escape-html": "^1.0.3", "estree-util-value-to-estree": "^3.0.1", "file-loader": "^6.2.0", @@ -2345,10 +2345,9 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.0.tgz", "integrity": "sha512-YEQO2D3UXs72qCn8Cr+RlycSQXVGN9iEUyuHwTuK4/uL/HFomB2FHSU0vSDM23oLd+X/KibQ3Ez6nGjQLqXcHg==", - "dev": true, "dependencies": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "2.4.0", + "@docusaurus/types": "2.4.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2361,64 +2360,18 @@ "react-dom": "*" } }, - "node_modules/@docusaurus/preset-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz", - "integrity": "sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/plugin-debug": "3.1.1", - "@docusaurus/plugin-google-analytics": "3.1.1", - "@docusaurus/plugin-google-gtag": "3.1.1", - "@docusaurus/plugin-google-tag-manager": "3.1.1", - "@docusaurus/plugin-sitemap": "3.1.1", - "@docusaurus/theme-classic": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-search-algolia": "3.1.1", - "@docusaurus/types": "3.1.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/module-type-aliases": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz", - "integrity": "sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A==", - "dependencies": { - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "3.1.1", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-blog": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz", - "integrity": "sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg==", + "node_modules/@docusaurus/plugin-content-blog": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.0.tgz", + "integrity": "sha512-YwkAkVUxtxoBAIj/MCb4ohN0SCtHBs4AS75jMhPpf67qf3j+U/4n33cELq7567hwyZ6fMz2GPJcVmctzlGGThQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -2497,19 +2450,19 @@ } } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-docs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz", - "integrity": "sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A==", + "node_modules/@docusaurus/plugin-content-docs": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.0.tgz", + "integrity": "sha512-ic/Z/ZN5Rk/RQo+Io6rUGpToOtNbtPloMR2JcGwC1xT2riMu6zzfSwmBi9tHJgdXH6CB5jG+0dOZZO8QS5tmDg==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@types/react-router-config": "^5.0.7", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "@types/react-router-config": "^5.0.6", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", @@ -2524,39 +2477,21 @@ "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" - } - }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-docs/node_modules/@docusaurus/mdx-loader": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", - "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", - "dependencies": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^1.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" + } + }, + "node_modules/@docusaurus/plugin-content-pages": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.0.tgz", + "integrity": "sha512-Pk2pOeOxk8MeU3mrTU0XLIgP9NZixbdcJmJ7RUFrZp1Aj42nd0RhIT14BGvXXyqb8yTQlk4DmYGAzqOfBsFyGw==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "fs-extra": "^10.1.0", + "tslib": "^2.4.0", + "webpack": "^5.73.0" }, "engines": { "node": ">=18.0" @@ -2566,19 +2501,17 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-pages": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz", - "integrity": "sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA==", + "node_modules/@docusaurus/plugin-debug": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.0.tgz", + "integrity": "sha512-KC56DdYjYT7Txyux71vXHXGYZuP6yYtqwClvYpjKreWIHWus5Zt6VNi23rMZv3/QKhOCrN64zplUbdfQMvddBQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "fs-extra": "^10.1.0", + "react-json-view": "^1.21.3", + "tslib": "^2.4.0" }, "engines": { "node": ">=18.0" @@ -2588,37 +2521,15 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-content-pages/node_modules/@docusaurus/mdx-loader": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", - "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", + "node_modules/@docusaurus/plugin-google-analytics": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.0.tgz", + "integrity": "sha512-uGUzX67DOAIglygdNrmMOvEp8qG03X20jMWadeqVQktS6nADvozpSLGx4J0xbkblhJkUzN21WiilsP9iVP+zkw==", "dependencies": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^1.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "tslib": "^2.4.0" }, "engines": { "node": ">=18.0" @@ -2628,17 +2539,15 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-debug": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz", - "integrity": "sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ==", + "node_modules/@docusaurus/plugin-google-gtag": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.0.tgz", + "integrity": "sha512-adj/70DANaQs2+TF/nRdMezDXFAV/O/pjAbUgmKBlyOTq5qoMe0Tk4muvQIwWUmiUQxFJe+sKlZGM771ownyOg==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^1.2.0", - "tslib": "^2.6.0" + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "tslib": "^2.4.0" }, "engines": { "node": ">=18.0" @@ -2648,15 +2557,15 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz", - "integrity": "sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ==", + "node_modules/@docusaurus/plugin-google-tag-manager": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.0.tgz", + "integrity": "sha512-E66uGcYs4l7yitmp/8kMEVQftFPwV9iC62ORh47Veqzs6ExwnhzBkJmwDnwIysHBF1vlxnzET0Fl2LfL5fRR3A==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "tslib": "^2.6.0" + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "tslib": "^2.4.0" }, "engines": { "node": ">=18.0" @@ -2666,16 +2575,20 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz", - "integrity": "sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw==", + "node_modules/@docusaurus/plugin-sitemap": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.0.tgz", + "integrity": "sha512-pZxh+ygfnI657sN8a/FkYVIAmVv0CGk71QMKqJBOfMmDHNN1FeDeFkBjWP49ejBqpqAhjufkv5UWq3UOu2soCw==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@types/gtag.js": "^0.0.12", - "tslib": "^2.6.0" + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "fs-extra": "^10.1.0", + "sitemap": "^7.1.1", + "tslib": "^2.4.0" }, "engines": { "node": ">=18.0" @@ -2685,15 +2598,24 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz", - "integrity": "sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "tslib": "^2.6.0" + "node_modules/@docusaurus/preset-classic": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.0.tgz", + "integrity": "sha512-/5z5o/9bc6+P5ool2y01PbJhoGddEGsC0ej1MF6mCoazk8A+kW4feoUd68l7Bnv01rCnG3xy7kHUQP97Y0grUA==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/plugin-debug": "2.4.0", + "@docusaurus/plugin-google-analytics": "2.4.0", + "@docusaurus/plugin-google-gtag": "2.4.0", + "@docusaurus/plugin-google-tag-manager": "2.4.0", + "@docusaurus/plugin-sitemap": "2.4.0", + "@docusaurus/theme-classic": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-search-algolia": "2.4.0", + "@docusaurus/types": "2.4.0" }, "engines": { "node": ">=18.0" @@ -2726,45 +2648,26 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-sitemap/node_modules/@docusaurus/utils-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.1.1.tgz", - "integrity": "sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg==", - "dependencies": { - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/types": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/types": { - "optional": true - } - } - }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/theme-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz", - "integrity": "sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==", - "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "copy-text-to-clipboard": "^3.2.0", + "node_modules/@docusaurus/theme-classic": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.0.tgz", + "integrity": "sha512-GMDX5WU6Z0OC65eQFgl3iNNEbI9IMJz9f6KnOyuMxNUR6q0qVLsKCNopFUDfFNJ55UU50o7P7o21yVhkwpfJ9w==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "copy-text-to-clipboard": "^3.0.1", "infima": "0.2.0-alpha.43", "lodash": "^4.17.21", "nprogress": "^0.2.0", @@ -2843,18 +2746,18 @@ } } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/theme-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.1.1.tgz", - "integrity": "sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg==", - "dependencies": { - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", + "node_modules/@docusaurus/theme-common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.0.tgz", + "integrity": "sha512-IkG/l5f/FLY6cBIxtPmFnxpuPzc5TupuqlOx+XDN+035MdQcAh8wHXXZJAkTeYDeZ3anIUSUIvWa7/nRKoQEfg==", + "dependencies": { + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2931,24 +2834,24 @@ } } }, - "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/theme-search-algolia": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz", - "integrity": "sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==", - "dependencies": { - "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "algoliasearch": "^4.18.0", - "algoliasearch-helper": "^3.13.3", - "clsx": "^2.0.0", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", + "node_modules/@docusaurus/theme-search-algolia": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.0.tgz", + "integrity": "sha512-pPCJSCL1Qt4pu/Z0uxBAuke0yEBbxh0s4fOvimna7TEcBLPq0x06/K78AaABXrTVQM6S0vdocFl9EoNgU17hqA==", + "dependencies": { + "@docsearch/react": "^3.1.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "algoliasearch": "^4.13.1", + "algoliasearch-helper": "^3.10.0", + "clsx": "^1.2.1", + "eta": "^2.0.0", + "fs-extra": "^10.1.0", "lodash": "^4.17.21", "tslib": "^2.6.0", "utility-types": "^3.10.0" @@ -3030,9 +2933,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz", - "integrity": "sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.0.tgz", + "integrity": "sha512-kEoITnPXzDPUMBHk3+fzEzbopxLD3fR5sDoayNH0vXkpUukA88/aDL1bqkhxWZHA3LOfJ3f0vJbOwmnXW5v85Q==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3045,7 +2948,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.0.tgz", "integrity": "sha512-xaBXr+KIPDkIaef06c+i2HeTqVNixB7yFut5fBXPGI2f1rrmEV2vLMznNGsFwvZ5XmA3Quuefd4OGRkdo97Dhw==", - "devOptional": true, "dependencies": { "@types/history": "^4.7.11", "@types/react": "*", @@ -3062,12 +2964,12 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-89hLYkvtRX92j+C+ERYTuSUK6nF9bGM32QThcHPg2EDDHVw6FzYQXmX6/p+pU5SDyyx5nBlE4qXR92RxCAOqfg==", "dependencies": { - "@docusaurus/logger": "3.1.1", - "@svgr/webpack": "^6.5.1", + "@docusaurus/logger": "2.4.0", + "@svgr/webpack": "^6.2.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", @@ -3085,7 +2987,26 @@ "webpack": "^5.88.1" }, "engines": { - "node": ">=18.0" + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.0.tgz", + "integrity": "sha512-zIMf10xuKxddYfLg5cS19x44zud/E9I7lj3+0bv8UIs0aahpErfNrGhijEfJpAfikhQ8tL3m35nH3hJ3sOG82A==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" }, "peerDependencies": { "@docusaurus/types": "*" @@ -3097,13 +3018,13 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz", - "integrity": "sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.0.tgz", + "integrity": "sha512-IrBsBbbAp6y7mZdJx4S4pIA7dUyWSA0GNosPk6ZJ0fX3uYIEQgcQSGIgTeSC+8xPEx3c16o03en1jSDpgQgz/w==", "dependencies": { - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "joi": "^17.9.2", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "joi": "^17.6.0", "js-yaml": "^4.1.0", "tslib": "^2.6.0" }, @@ -3421,9 +3342,9 @@ } }, "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", "engines": { "node": ">=14" }, @@ -3436,9 +3357,9 @@ } }, "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz", - "integrity": "sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", "engines": { "node": ">=14" }, @@ -3758,11 +3679,11 @@ "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" }, "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", "dependencies": { - "@types/unist": "*" + "@types/unist": "^2" } }, "node_modules/@types/history": { @@ -3820,11 +3741,11 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "node_modules/@types/mdast": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", - "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", "dependencies": { - "@types/unist": "*" + "@types/unist": "^2" } }, "node_modules/@types/mdx": { @@ -3973,9 +3894,9 @@ } }, "node_modules/@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, "node_modules/@types/ws": { "version": "8.5.10", @@ -4290,30 +4211,30 @@ } }, "node_modules/algoliasearch": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", - "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.22.1", - "@algolia/cache-common": "4.22.1", - "@algolia/cache-in-memory": "4.22.1", - "@algolia/client-account": "4.22.1", - "@algolia/client-analytics": "4.22.1", - "@algolia/client-common": "4.22.1", - "@algolia/client-personalization": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/logger-console": "4.22.1", - "@algolia/requester-browser-xhr": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/requester-node-http": "4.22.1", - "@algolia/transporter": "4.22.1" + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.16.0.tgz", + "integrity": "sha512-HAjKJ6bBblaXqO4dYygF4qx251GuJ6zCZt+qbJ+kU7sOC+yc84pawEjVpJByh+cGP2APFCsao2Giz50cDlKNPA==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.16.0", + "@algolia/cache-common": "4.16.0", + "@algolia/cache-in-memory": "4.16.0", + "@algolia/client-account": "4.16.0", + "@algolia/client-analytics": "4.16.0", + "@algolia/client-common": "4.16.0", + "@algolia/client-personalization": "4.16.0", + "@algolia/client-search": "4.16.0", + "@algolia/logger-common": "4.16.0", + "@algolia/logger-console": "4.16.0", + "@algolia/requester-browser-xhr": "4.16.0", + "@algolia/requester-common": "4.16.0", + "@algolia/requester-node-http": "4.16.0", + "@algolia/transporter": "4.16.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.16.2.tgz", - "integrity": "sha512-Yl/Gu5Cq4Z5s/AJ0jR37OPI1H3+z7PHz657ibyaXgMOaWvPlZ3OACN13N+7HCLPUlB0BN+8BtmrG/CqTilowBA==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.12.0.tgz", + "integrity": "sha512-/j1U3PEwdan0n6P/QqSnSpNSLC5+cEMvyljd5CnmNmUjDlGrys+vFEOwjVEnqELIiAGMHEA/Nl3CiKVFBUYqyQ==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4670,9 +4591,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "funding": [ { "type": "opencollective", @@ -4688,10 +4609,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" }, "bin": { "browserslist": "cli.js" @@ -4807,9 +4728,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001588", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", - "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", + "version": "1.0.30001473", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", + "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", "funding": [ { "type": "opencollective", @@ -5456,11 +5377,11 @@ } }, "node_modules/core-js-compat": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", - "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", + "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", "dependencies": { - "browserslist": "^4.22.3" + "browserslist": "^4.21.5" }, "funding": { "type": "opencollective", @@ -5497,6 +5418,14 @@ "node": ">=10" } }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -6094,13 +6023,13 @@ } }, "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" @@ -6145,9 +6074,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.678", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.678.tgz", - "integrity": "sha512-NbdGC2p0O5Q5iVhLEsNBSfytaw7wbEFJlIvaF71wi6QDtLAph5/rVogjyOpf/QggJIt8hNK3KdwNJnc2bzckbw==" + "version": "1.4.348", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", + "integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -6197,9 +6126,9 @@ } }, "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "engines": { "node": ">=0.12" }, @@ -6662,6 +6591,33 @@ "node": ">=0.8.0" } }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.30" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -6693,9 +6649,9 @@ } }, "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -10835,9 +10791,17 @@ } }, "node_modules/node-emoji": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", - "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", @@ -10868,9 +10832,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -12465,6 +12429,22 @@ "react": ">=15" } }, + "node_modules/react-textarea-autosize": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", + "integrity": "sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -12689,23 +12669,28 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", - "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", + "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", @@ -12970,43 +12955,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", - "peer": true - }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -13970,6 +13918,37 @@ "node": ">=12.20" } }, + "node_modules/ua-parser-js": { + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dependencies": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -14162,9 +14141,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "funding": [ { "type": "opencollective", @@ -14322,9 +14301,9 @@ } }, "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", diff --git a/go.mod b/go.mod index 1a1edbed..979e68bc 100644 --- a/go.mod +++ b/go.mod @@ -1,37 +1,47 @@ module github.com/natesales/pathvector -go 1.18 +go 1.21 require ( + github.com/charmbracelet/log v0.4.0 github.com/creasty/defaults v1.7.0 - github.com/fatih/color v1.15.0 + github.com/fatih/color v1.17.0 github.com/go-ping/ping v1.1.0 - github.com/go-playground/validator/v10 v10.12.0 - github.com/natesales/logknife v0.0.4-0.20230403055117-5e928ad4153b + github.com/go-playground/validator/v10 v10.22.0 + github.com/lithammer/fuzzysearch v1.1.8 + github.com/natesales/logknife v0.0.4-0.20240904212444-784fa10b2080 github.com/olekukonko/tablewriter v0.0.5 - github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.7.0 - github.com/stretchr/testify v1.8.2 - golang.org/x/mod v0.17.0 + github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.9.0 + golang.org/x/mod v0.19.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/lipgloss v0.11.0 // indirect + github.com/charmbracelet/x/ansi v0.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/leodido/go-urn v1.2.3 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect ) diff --git a/go.sum b/go.sum index 6ef4e5ec..961c27b5 100644 --- a/go.sum +++ b/go.sum @@ -1,86 +1,133 @@ +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= +github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= +github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw= github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/natesales/logknife v0.0.4-0.20230403055117-5e928ad4153b h1:oDEZbVwq3gZzqimMCrxDUWrn2e9RWws0q48mdsvUz7g= -github.com/natesales/logknife v0.0.4-0.20230403055117-5e928ad4153b/go.mod h1:tQLjEyj4CHMjYpG5icbEKjWtByZxU7b6CU1wEVIdp2k= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/natesales/logknife v0.0.4-0.20240904212444-784fa10b2080 h1:x0IxtZknLd9Zx7IVzbV4TKsYMW3LDP0/AYclZfkXO3A= +github.com/natesales/logknife v0.0.4-0.20240904212444-784fa10b2080/go.mod h1:tQLjEyj4CHMjYpG5icbEKjWtByZxU7b6CU1wEVIdp2k= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go index 5ac13c5f..d0d4373f 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,8 @@ import ( "os" "strings" - log "github.com/sirupsen/logrus" - "github.com/natesales/pathvector/cmd" + "github.com/natesales/pathvector/pkg/util/log" ) // Build process flags @@ -23,7 +22,7 @@ func main() { if //goland:noinspection GoBoolExpressions version == "devel" || strings.Contains(version, "SNAPSHOT") { fmt.Fprintln(os.Stderr, `******************************************************************************* -WARNING: This is a development build. It is not recommended for production use. +WARNING: This is a development build. It may not be ready for production use. Please submit any bugs to https://github.com/natesales/pathvector/issues *******************************************************************************`) } diff --git a/pkg/autodoc/documentation.go b/pkg/autodoc/documentation.go index 36084919..f9fd95f8 100644 --- a/pkg/autodoc/documentation.go +++ b/pkg/autodoc/documentation.go @@ -7,9 +7,8 @@ import ( "strings" "unicode" - log "github.com/sirupsen/logrus" - "github.com/natesales/pathvector/pkg/config" + "github.com/natesales/pathvector/pkg/util/log" ) func sanitizeConfigName(s string) string { @@ -43,7 +42,7 @@ func (s byReflectType) Less(i, j int) bool { func documentConfigTypes(t reflect.Type, output bool) { childTypesSet := map[reflect.Type]bool{} if output { - fmt.Println("## " + sanitizeConfigName(t.String())) + log.Println("## " + sanitizeConfigName(t.String())) } // Handle pointer types if t.Kind() == reflect.Ptr { @@ -70,7 +69,7 @@ func documentConfigTypes(t reflect.Type, output bool) { } } if output { - fmt.Printf(`### `+"`"+`%s`+"`"+` + log.Printf(`### `+"`"+`%s`+"`"+` %s @@ -80,11 +79,11 @@ func documentConfigTypes(t reflect.Type, output bool) { `, key, description, addLink(sanitizeConfigName(field.Type.String())), fDefault, validation) } - //fmt.Printf("| %s | %s | %s | %s | %s |\n", key, addLink(sanitizeConfigName(field.Type.String())), fDefault, validation, description) + // log.Printf("| %s | %s | %s | %s | %s |\n", key, addLink(sanitizeConfigName(field.Type.String())), fDefault, validation, description) } } if output { - fmt.Println() + log.Println() } // Convert the set into a slice and sort it @@ -102,7 +101,7 @@ func documentConfigTypes(t reflect.Type, output bool) { // DocumentConfig prints a YAML file with autogenerated configuration documentation func DocumentConfig(output bool) { if output { - fmt.Println("# Configuration") + log.Println("# Configuration") } documentConfigTypes(reflect.TypeOf(config.Config{}), output) } diff --git a/pkg/bird/bird.go b/pkg/bird/bird.go index 424b02cb..7feb3812 100644 --- a/pkg/bird/bird.go +++ b/pkg/bird/bird.go @@ -3,6 +3,7 @@ package bird import ( "bufio" "bytes" + "flag" "fmt" "io" "net" @@ -14,10 +15,10 @@ import ( "strconv" "strings" - log "github.com/sirupsen/logrus" "golang.org/x/mod/semver" "github.com/natesales/pathvector/pkg/util" + "github.com/natesales/pathvector/pkg/util/log" ) // Minimum supported BIRD version @@ -92,13 +93,19 @@ func ReadClean(r io.Reader) { resp = strings.ReplaceAll(resp, "\n\n", "\n") resp = strings.TrimSuffix(resp, "\n") - fmt.Println(resp) + log.Println(resp) } // RunCommand runs a BIRD command and returns the output, version, and error func RunCommand(command string, socket string) (string, string, error) { - log.Debugln("Connecting to BIRD socket") - conn, err := net.Dial("unix", socket) + network := "unix" + if strings.HasPrefix(socket, "tcp") { + network = "tcp" + socket = strings.TrimPrefix(socket, "tcp://") + } + + log.Debugf("Connecting to BIRD socket %s://%s", network, socket) + conn, err := net.Dial(network, socket) if err != nil { return "", "", err } @@ -110,7 +117,7 @@ func RunCommand(command string, socket string) (string, string, error) { if err != nil { return "", "", err } - log.Debugf("BIRD init response: %s", resp) + log.Tracef("BIRD init response: %s", resp) // Check BIRD version birdVersion := strings.Split(resp, " ")[1] @@ -120,39 +127,64 @@ func RunCommand(command string, socket string) (string, string, error) { log.Debugf("Sending BIRD command: %s", command) _, err = conn.Write([]byte(strings.Trim(command, "\n") + "\n")) - log.Debugf("Sent BIRD command: %s", command) if err != nil { return "", "", err } - log.Debugln("Reading from socket") + log.Trace("Reading from socket") resp, err = Read(conn) if err != nil { return "", "", err } - log.Debugln("Done reading from socket") + log.Trace("Done reading from socket") return resp, birdVersion, nil // nil error } -// Validate checks if the cached configuration is syntactically valid -func Validate(binary string, cacheDir string) { +func Validate(binary, cacheDir string) error { + if flag.Lookup("test.v") != nil { + return dockerValidate(binary, cacheDir) + } else { + return localValidate(binary, cacheDir) + } +} + +func dockerValidate(_, _ string) error { + args := []string{ + "docker", "exec", + "pathvector-bird", + "bird", "-c", "/tmp/test-cache/bird.conf", "-p", + } + log.Infof("[DOCKER] Running command: %s", strings.Join(args, " ")) + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + if err := cmd.Run(); err != nil { + return fmt.Errorf("BIRD docker validation: %s", err) + } + + log.Infof("[DOCKER] BIRD config validation passed") + return nil +} + +// localValidate checks if the cached configuration is syntactically valid +func localValidate(binary string, cacheDir string) error { log.Debugf("Validating BIRD config") - var outb, errb bytes.Buffer + var outBuf, errBuf bytes.Buffer birdCmd := exec.Command(binary, "-c", "bird.conf", "-p") birdCmd.Dir = cacheDir - birdCmd.Stdout = &outb - birdCmd.Stderr = &errb + birdCmd.Stdout = &outBuf + birdCmd.Stderr = &errBuf var errbT string if err := birdCmd.Run(); err != nil { origErr := err - errbT = strings.TrimSuffix(errb.String(), "\n") + errbT = strings.TrimSuffix(errBuf.String(), "\n") // Check for validation error in format: // bird: ./AS65530_EXAMPLE.conf:20:43 syntax error, unexpected '%' match, err := regexp.MatchString(`bird:.*:\d+:\d+.*`, errbT) if err != nil { - log.Fatalf("BIRD error regex match: %s", err) + return fmt.Errorf("BIRD error regex match: %s", err) } errorMessageToLog := errbT if match { @@ -163,18 +195,18 @@ func Validate(binary string, cacheDir string) { errorFile := respPartsColon[0] errorLine, err := strconv.Atoi(respPartsColon[1]) if err != nil { - log.Fatalf("BIRD error line int parse: %s", err) + return fmt.Errorf("BIRD error line int parse: %s", err) } errorChar, err := strconv.Atoi(respPartsColon[2]) if err != nil { - log.Fatalf("BIRD error line int parse: %s", err) + return fmt.Errorf("BIRD error line int parse: %s", err) } log.Debugf("Found error in %s:%d:%d message %s", errorFile, errorLine, errorChar, errorMessage) // Read output file file, err := os.Open(path.Join(cacheDir, errorFile)) if err != nil { - log.Fatalf("unable to read BIRD output file for error parsing: %s", err) + return fmt.Errorf("unable to read BIRD output file for error parsing: %s", err) } defer file.Close() @@ -190,16 +222,17 @@ func Validate(binary string, cacheDir string) { line++ } if err := scanner.Err(); err != nil { - log.Fatalf("BIRD output file scan: %s", err) + return fmt.Errorf("BIRD output file scan: %s", err) } } if errorMessageToLog == "" { errorMessageToLog = origErr.Error() } - log.Fatalf("BIRD: %s\n", errorMessageToLog) + return fmt.Errorf("BIRD: %s", errorMessageToLog) } log.Infof("BIRD config validation passed") + return nil } // MoveCacheAndReconfigure moves cached files to the production BIRD directory and reconfigures @@ -212,22 +245,22 @@ func MoveCacheAndReconfigure(birdDirectory string, cacheDirectory string, birdSo for _, f := range birdConfigFiles { log.Debugf("Removing old BIRD config file %s", f) if err := os.Remove(f); err != nil { - log.Fatalf("Removing old BIRD config files: %v", err) + log.Fatalf("Unable to remove old BIRD config files: %v", err) } } - // Copy from cache to bird config + // Copy from cache dir to bird config dir files, err := filepath.Glob(path.Join(cacheDirectory, "*.conf")) if err != nil { log.Fatal(err) } - for _, f := range files { + for _, f := range append(files, path.Join(cacheDirectory, "protocols.json")) { fileNameParts := strings.Split(f, "/") fileNameTail := fileNameParts[len(fileNameParts)-1] newFileLoc := path.Join(birdDirectory, fileNameTail) log.Debugf("Moving %s to %s", f, newFileLoc) if err := util.MoveFile(f, newFileLoc); err != nil { - log.Fatalf("Moving cache file to bird directory: %v", err) + log.Warnf("Moving %s to %s: %v", f, newFileLoc, err) } } @@ -242,40 +275,118 @@ func MoveCacheAndReconfigure(birdDirectory string, cacheDirectory string, birdSo } if !noConfigure { - log.Info("Reconfiguring BIRD") - resp, _, err := RunCommand("configure", birdSocket) - if err != nil { - log.Fatal(err) - } - // Print bird output as multiple lines - for _, line := range strings.Split(strings.Trim(resp, "\n"), "\n") { - log.Printf("BIRD response (multiline): %s", line) - } + Configure(birdSocket) } } -// Reformat takes a BIRD config file as a string and outputs a nicely formatted version as a string -func Reformat(input string) string { +// Configure applies a runtime BIRD configuration +func Configure(birdSocket string) { + log.Info("Reconfiguring BIRD") + resp, _, err := RunCommand("configure", birdSocket) + if err != nil { + log.Fatalf("BIRD configure error: %v", err) + } + // Print bird output as multiple lines + for _, line := range strings.Split(strings.Trim(resp, "\n"), "\n") { + log.Infof("BIRD response (multiline): %s", line) + } +} + +// noSpace removes all leading/trailing whitespace from every line, and every empty line +func noSpace(input string) string { formatted := "" - for _, line := range strings.Split(input, "\n") { - if strings.HasSuffix(line, "{") || strings.HasSuffix(line, "[") { - formatted += "\n" + lines := strings.Split(input, "\n") + + for i := range lines { + line := lines[i] + line = strings.TrimSpace(line) + if line == "" { + continue } + formatted += line + "\n" + } + return formatted +} - if !func(input string) bool { - for _, chr := range input { - if string(chr) != " " { - return false - } +// restoreIndent indents a file, one tab per curly brace or bracket +func restoreIndent(input string) string { + formatted := "" + lines := strings.Split(input, "\n") + + indent := 0 + for i := range lines { + line := strings.TrimSpace(lines[i]) + + switch { + case line == "" || line == "\n": + continue + case strings.HasSuffix(line, "{") && strings.HasPrefix(line, "}"): + if indent == 0 { + formatted += strings.Repeat(" ", indent) + line + "\n" + } else { + formatted += strings.Repeat(" ", indent-1) + line + "\n" } - return true - }(line) { - formatted += line + "\n" + case strings.HasSuffix(line, "{") || strings.HasSuffix(line, "["): // Opening + formatted += strings.Repeat(" ", indent) + line + "\n" + indent++ + case strings.HasPrefix(line, "}") || strings.HasPrefix(line, "]"): + indent-- + formatted += strings.Repeat(" ", indent) + line + "\n" + default: + formatted += strings.Repeat(" ", indent) + line + "\n" } } + return formatted } +// restoreNewlines adds a newline after every comment and after every zero indented curly brace or bracket +func restoreNewlines(input string) string { + out := "" + for _, line := range strings.Split(input, "\n") { + if strings.HasPrefix(line, "#") { + line += "\n" + } else if line == "}" || line == "];" { + line += "\n" + } + + out += line + "\n" + } + return out +} + +// fixStatementBracket spacing adds a newline between statements and open braces/brackets +func fixStatementBracketSpacing(input string) string { + out := "" + lines := strings.Split(input, "\n") + for i := range lines { + line := lines[i] + nextLine := "" + if i+1 < len(lines) { + nextLine = lines[i+1] + } + nextLine = strings.TrimSpace(nextLine) + + if (strings.HasSuffix(line, ";") || strings.HasSuffix(line, "}")) && + ((strings.HasSuffix(nextLine, "{") && !strings.HasPrefix(nextLine, "}")) || strings.HasSuffix(nextLine, "[")) { + line += "\n" + } + + out += line + "\n" + } + return out +} + +// Reformat takes a BIRD config file as a string and outputs a nicely formatted version as a string +func Reformat(input string) string { + input = noSpace(input) + input = restoreIndent(input) + input = restoreNewlines(input) + input = fixStatementBracketSpacing(input) + input = strings.TrimRight(input, "\n") + "\n" + return input +} + type Routes struct { Imported int Filtered int diff --git a/pkg/bird/bird_test.go b/pkg/bird/bird_test.go index 2df67aa1..fe2a71a7 100644 --- a/pkg/bird/bird_test.go +++ b/pkg/bird/bird_test.go @@ -35,16 +35,14 @@ func TestBirdConn(t *testing.T) { defer l.Close() t.Logf("Accepting connection on %s", unixSocket) conn, err := l.Accept() - if err != nil { - return - } + assert.Nil(t, err) defer conn.Close() _, err = conn.Write([]byte("0001 Fake BIRD response 1\n")) assert.Nil(t, err) buf := make([]byte, 1024) - n, err := conn.Read(buf[:]) + n, err := conn.Read(buf) assert.Nil(t, err) assert.Equal(t, "bird command test\n", string(buf[:n])) @@ -80,7 +78,7 @@ static4 Static master4 up 2023-03-15 19:18:50 assert.Equal(t, 2, p.Routes.Exported) assert.Equal(t, 1, p.Routes.Preferred) - p, err = ParseProtocol(`EXAMPLE_AS65522_v6 BGP --- up 2023-03-26 03:53:56 Established + p, err = ParseProtocol(`EXAMPLE_AS65522_v6 BGP --- up 2023-03-26 03:53:56 Established BGP state: Established Neighbor address: 2001:db8::1 Neighbor AS: 65522 @@ -412,9 +410,9 @@ EXAMPLE_AS65522_v6 BGP --- up 2023-03-26 03:53:56 Established protocols, err = ParseProtocols(` BIRD 2.13 ready. Name Proto Table State Since Info -device1 Device --- up 21:26:25.230 +device1 Device --- up 21:26:25.230 -direct1 Direct --- down 21:26:25.230 +direct1 Direct --- down 21:26:25.230 Channel ipv4 State: DOWN Table: master4 @@ -428,7 +426,7 @@ direct1 Direct --- down 21:26:25.230 Input filter: ACCEPT Output filter: REJECT -kernel1 Kernel master4 up 21:26:25.230 +kernel1 Kernel master4 up 21:26:25.230 Channel ipv4 State: UP Table: master4 @@ -442,7 +440,7 @@ kernel1 Kernel master4 up 21:26:25.230 Export updates: 0 0 0 --- 0 Export withdraws: 0 --- --- --- 0 -kernel2 Kernel master6 up 21:26:25.230 +kernel2 Kernel master6 up 21:26:25.230 Channel ipv6 State: UP Table: master6 @@ -456,7 +454,7 @@ kernel2 Kernel master6 up 21:26:25.230 Export updates: 0 0 0 --- 0 Export withdraws: 0 --- --- --- 0 -static1 Static master4 up 21:26:25.230 +static1 Static master4 up 21:26:25.230 Channel ipv4 State: UP Table: master4 diff --git a/pkg/block/block.go b/pkg/block/block.go index 79ebba44..0cf207c4 100644 --- a/pkg/block/block.go +++ b/pkg/block/block.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - log "github.com/sirupsen/logrus" + "github.com/natesales/pathvector/pkg/util/log" ) // parseASN parses an ASN into a string and returns -1 if invalid @@ -68,13 +68,15 @@ func Parse(blocklist []string) ([]uint32, []string, error) { // Remove whitespace token = strings.TrimSpace(token) - if asn := parseASN(token); asn != -1 { + asn := parseASN(token) + switch { + case asn != -1: log.Debugf("Adding ASN to blocklist: %d", asn) asns = append(asns, uint32(asn)) - } else if validPrefix(token) { + case validPrefix(token): log.Debugf("Adding prefix to blocklist: %s", token) prefixes = append(prefixes, token) - } else if validIP(token) { + case validIP(token): log.Debugf("Adding IP to blocklist: %s", token) afiSuffix := "/32" @@ -82,7 +84,7 @@ func Parse(blocklist []string) ([]uint32, []string, error) { afiSuffix = "/128" } prefixes = append(prefixes, token+afiSuffix) - } else { + default: return nil, nil, fmt.Errorf("invalid blocklist token: %s", token) } } diff --git a/pkg/config/config.go b/pkg/config/config.go index ef435e5c..885f2ba9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -230,6 +230,7 @@ type Peer struct { OptimizeInbound *bool `yaml:"optimize-inbound" description:"Should the optimizer modify inbound policy?" default:"false"` ProtocolName *string `yaml:"-" description:"-" default:"-"` + UserSpecifiedName *string `yaml:"-" description:"-" default:"-"` Protocols *[]string `yaml:"-" description:"-" default:"-"` PrefixSet4 *[]string `yaml:"-" description:"-" default:"-"` PrefixSet6 *[]string `yaml:"-" description:"-" default:"-"` @@ -280,6 +281,7 @@ type Kernel struct { Reject4 []string `yaml:"reject4" description:"List of BIRD protocols to not import into the IPv4 table"` Reject6 []string `yaml:"reject6" description:"List of BIRD protocols to not import into the IPv6 table"` Statics map[string]string `yaml:"statics" description:"List of static routes to include in BIRD"` + KStatics map[string]string `yaml:"kstatics" description:"List of static routes to include in BIRD and send to the kernel"` SRDCommunities []string `yaml:"srd-communities" description:"List of communities to filter routes exported to kernel (if list is not empty, all other prefixes will not be exported)"` Learn bool `yaml:"learn" description:"Should routes from the kernel be learned into BIRD?" default:"false"` Export bool `yaml:"export" description:"Export routes to kernel routing table" default:"true"` @@ -291,6 +293,8 @@ type Kernel struct { SRDLargeCommunities []string `yaml:"-" description:"-"` Statics4 map[string]string `yaml:"-" description:"-"` Statics6 map[string]string `yaml:"-" description:"-"` + KStatics4 map[string]string `yaml:"-" description:"-"` + KStatics6 map[string]string `yaml:"-" description:"-"` } // ProbeResult stores a single probe result @@ -357,6 +361,7 @@ type Config struct { IRRServer string `yaml:"irr-server" description:"Internet routing registry server" default:"rr.ntt.net"` RTRServer string `yaml:"rtr-server" description:"RPKI-to-router server" default:"rtr.rpki.cloudflare.com:8282"` BGPQArgs string `yaml:"bgpq-args" description:"Additional command line arguments to pass to bgpq4" default:""` + BGPQBin string `yaml:"bgpq-binary" description:"Path to bgpq4 binary" default:"bgpq4"` KeepFiltered bool `yaml:"keep-filtered" description:"Should filtered routes be kept in memory?" default:"false"` MergePaths bool `yaml:"merge-paths" description:"Should best and equivalent non-best routes be imported to build ECMP routes?" default:"false"` Source4 string `yaml:"source4" description:"Source IPv4 address"` diff --git a/pkg/embed/templates/global.tmpl b/pkg/embed/templates/global.tmpl index 646045e8..d43d66e8 100644 --- a/pkg/embed/templates/global.tmpl +++ b/pkg/embed/templates/global.tmpl @@ -3,21 +3,32 @@ define ASN = {{ .ASN }}; router id {{ .RouterID }}; -{{ if .Prefixes4 -}} -define LOCALv4 = [ -{{ BirdSet .Prefixes4 }} -]; -{{- end }} +{{ if .Prefixes4 }} + define LOCALv4 = [ + {{ BirdSet .Prefixes4 }} + ]; +{{ end }} + {{ if or .Prefixes4 .Kernel.Statics4 }} -protocol static static4 { - ipv4; - {{- range $i, $prefix := .Prefixes4 }} - route {{ $prefix }} reject; - {{- end }} - {{- range $prefix, $nexthop := MapDeref .Kernel.Statics4 }} - route {{ $prefix }} via {{ $nexthop }}; - {{- end }} -} + protocol static static4 { + ipv4; + {{- range $i, $prefix := .Prefixes4 }} + route {{ $prefix }} reject; + {{- end }} + + {{- range $prefix, $nexthop := MapDeref .Kernel.Statics4 }} + route {{ $prefix }} via {{ $nexthop }}; + {{- end }} + } +{{- end }} + +{{ if .Kernel.KStatics4 }} + protocol static kstatic4 { + ipv4; + {{- range $prefix, $nexthop := MapDeref .Kernel.KStatics4 }} + route {{ $prefix }} via {{ $nexthop }}; + {{- end }} + } {{- end }} {{ if .Prefixes6 -}} @@ -37,6 +48,15 @@ protocol static static6 { } {{- end }} +{{ if .Kernel.KStatics6 }} +protocol static kstatic6 { + ipv6; + {{- range $prefix, $nexthop := MapDeref .Kernel.KStatics6 }} + route {{ $prefix }} via {{ $nexthop }}; + {{- end }} +} +{{- end }} + {{ if .DefaultRoute -}} protocol static default4 { ipv4; @@ -62,7 +82,7 @@ protocol device {}; protocol direct { ipv4; ipv6; } -protocol kernel { +protocol kernel kernel4 { scan time {{ .Kernel.ScanTime }}; {{ if .Kernel.Learn }}learn;{{ end }} {{ if .Kernel.Table }}kernel table {{ .Kernel.Table }};{{ end }} @@ -104,7 +124,7 @@ protocol kernel { {{ if .MergePaths }}merge paths;{{ end }} } -protocol kernel { +protocol kernel kernel6 { scan time {{ .Kernel.ScanTime }}; {{ if .Kernel.Learn }}learn;{{ end }} {{ if .Kernel.Table }}kernel table {{ .Kernel.Table }};{{ end }} @@ -227,7 +247,7 @@ define BLOCKLIST_PREFIXES = [ # Helper Functions function _reject(string reason) { - reject "REJECTED [", reason, "] pfx ", net, " session ", proto, " path ", bgp_path, " pathlen ", bgp_path.len, " origin ", bgp_path.last; + reject "REJECTED [", reason, "] pfx ", net, " session ", proto, " path ", bgp_path, " pathlen ", bgp_path.len, " origin ", bgp_path.last, " nexthop ", bgp_next_hop; } function reject_blocklist() { diff --git a/pkg/irr/irr.go b/pkg/irr/irr.go index 56e2683c..0dbab255 100644 --- a/pkg/irr/irr.go +++ b/pkg/irr/irr.go @@ -8,53 +8,37 @@ import ( "strings" "time" - log "github.com/sirupsen/logrus" - "github.com/natesales/pathvector/pkg/config" + "github.com/natesales/pathvector/pkg/util/log" ) -// withSourceFilter returns the AS set or AS set with the IRR source replaced with the -S SOURCE syntax -// AS34553 -> AS34553 -// RIPE::AS34553 -> -S RIPE AS34553 -func withSourceFilter(asSet string) string { - if strings.Contains(asSet, "::") { - log.Debugf("Using IRRDB source from AS set %s", asSet) - tokens := strings.Split(asSet, "::") - return fmt.Sprintf("-S %s %s", tokens[0], tokens[1]) +func PrefixSet(asSet string, family uint8, irrServer string, queryTimeout uint, bgpqBin, bgpqArgs string) ([]string, error) { + // Run bgpq4 for BIRD format with aggregation enabled + cmdArgs := fmt.Sprintf("-h %s -Ab%d %s", irrServer, family, asSet) + if bgpqArgs != "" { + cmdArgs = bgpqArgs + " " + cmdArgs } - return asSet -} + log.Debugf("Running %s %s", bgpqBin, cmdArgs) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(queryTimeout)) + defer cancel() -// PrefixSet uses bgpq4 to generate a prefix filter and return only the filter lines -func PrefixSet(macro string, family uint8, irrServer string, queryTimeout uint, bgpqArgs string) ([]string, error) { - var prefixes []string + //nolint:golint,gosec + cmd := exec.CommandContext(ctx, bgpqBin, strings.Split(cmdArgs, " ")...) + stdout, err := cmd.Output() + if err != nil { + return nil, err + } - for _, asSet := range strings.Split(macro, " ") { - // Run bgpq4 for BIRD format with aggregation enabled - cmdArgs := fmt.Sprintf("-h %s -Ab%d %s", irrServer, family, withSourceFilter(asSet)) - if bgpqArgs != "" { - cmdArgs = bgpqArgs + " " + cmdArgs - } - log.Debugf("Running bgpq4 %s", cmdArgs) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(queryTimeout)) - defer cancel() - //nolint:golint,gosec - cmd := exec.CommandContext(ctx, "bgpq4", strings.Split(cmdArgs, " ")...) - stdout, err := cmd.Output() - if err != nil { - return nil, err + var prefixes []string + for i, line := range strings.Split(string(stdout), "\n") { + if i == 0 { // Skip first line, as it is the definition line + continue } - - for i, line := range strings.Split(string(stdout), "\n") { - if i == 0 { // Skip first line, as it is the definition line - continue - } - if strings.Contains(line, "];") { // Skip last line and return - break - } - // Trim whitespace and remove the comma, then append to the prefixes slice - prefixes = append(prefixes, strings.TrimSpace(strings.TrimRight(line, ","))) + if strings.Contains(line, "];") { // Skip last line and return + break } + // Trim whitespace and remove the comma, then append to the prefixes slice + prefixes = append(prefixes, strings.TrimSpace(strings.TrimRight(line, ","))) } return prefixes, nil @@ -63,7 +47,7 @@ func PrefixSet(macro string, family uint8, irrServer string, queryTimeout uint, // ASMembers uses bgpq4 to generate an AS member set func ASMembers(asSet string, irrServer string, queryTimeout uint, bgpqArgs string) ([]uint32, error) { // Run bgpq4 for BIRD format with aggregation enabled - cmdArgs := fmt.Sprintf("-h %s -tj %s", irrServer, withSourceFilter(asSet)) + cmdArgs := fmt.Sprintf("-h %s -tj %s", irrServer, asSet) if bgpqArgs != "" { cmdArgs = bgpqArgs + " " + cmdArgs } @@ -86,7 +70,7 @@ func ASMembers(asSet string, irrServer string, queryTimeout uint, bgpqArgs strin } // Update updates a peer's IRR prefix set -func Update(peerData *config.Peer, irrServer string, queryTimeout uint, bgpqArgs string) error { +func Update(peerData *config.Peer, irrServer string, queryTimeout uint, bgpqBin, bgpqArgs string) error { // Check for empty as-set if peerData.ASSet == nil || *peerData.ASSet == "" { return fmt.Errorf("peer has filter-irr enabled and no as-set defined") @@ -96,11 +80,12 @@ func Update(peerData *config.Peer, irrServer string, queryTimeout uint, bgpqArgs var hasNeighbor4, hasNeighbor6 bool if peerData.NeighborIPs != nil { for _, n := range *peerData.NeighborIPs { - if strings.Contains(n, ".") { + switch { + case strings.Contains(n, "."): hasNeighbor4 = true - } else if strings.Contains(n, ":") { + case strings.Contains(n, ":"): hasNeighbor6 = true - } else { + default: log.Fatalf("Invalid neighbor IP %s", n) } } @@ -121,7 +106,7 @@ func Update(peerData *config.Peer, irrServer string, queryTimeout uint, bgpqArgs bgpqArgs6 += "-R 48" } - prefixesFromIRR4, err := PrefixSet(*peerData.ASSet, 4, irrServer, queryTimeout, bgpqArgs4) + prefixesFromIRR4, err := PrefixSet(*peerData.ASSet, 4, irrServer, queryTimeout, bgpqBin, bgpqArgs4) if err != nil { return fmt.Errorf("unable to get IPv4 IRR prefix list from %s: %s", *peerData.ASSet, err) } @@ -134,7 +119,7 @@ func Update(peerData *config.Peer, irrServer string, queryTimeout uint, bgpqArgs log.Warnf("peer has IPv4 session(s) but no IPv4 prefixes") } - prefixesFromIRR6, err := PrefixSet(*peerData.ASSet, 6, irrServer, queryTimeout, bgpqArgs6) + prefixesFromIRR6, err := PrefixSet(*peerData.ASSet, 6, irrServer, queryTimeout, bgpqBin, bgpqArgs6) if err != nil { return fmt.Errorf("unable to get IPv6 IRR prefix list from %s: %s", *peerData.ASSet, err) } diff --git a/pkg/irr/irr_test.go b/pkg/irr/irr_test.go index 8aea60f2..edd0159c 100644 --- a/pkg/irr/irr_test.go +++ b/pkg/irr/irr_test.go @@ -22,10 +22,10 @@ func TestGetIRRPrefixSet(t *testing.T) { {"AS112", 4, []string{"192.31.196.0/24", "192.175.48.0/24"}, false}, {"AS112", 6, []string{"2001:4:112::/48", "2620:4f:8000::/48"}, false}, {"AS112", 9, []string{"2001:4:112::/48", "2620:4f:8000::/48"}, true}, // Invalid address family - {"AS-LROOT", 6, []string{"2001:500:3::/48", "2001:500:8c::/48", "2001:500:9c::/47{47,48}", "2001:500:9e::/47", "2001:500:9f::/48", "2602:800:9004::/47{48,48}", "2620:0:22b0::/48", "2620:0:2ee0::/48"}, false}, + {"AS-FROOT", 4, []string{"192.5.4.0/23{23,24}"}, false}, } for _, tc := range testCases { - out, err := PrefixSet(tc.asSet, tc.family, "rr.ntt.net", irrQueryTimeout, "") + out, err := PrefixSet(tc.asSet, tc.family, "rr.ntt.net", irrQueryTimeout, "bgpq4", "") if err != nil && !tc.shouldError { t.Error(err) } else if err == nil && tc.shouldError { @@ -48,18 +48,20 @@ func TestBuildIRRPrefixSet(t *testing.T) { {"", []string{}, []string{}, true}, // Empty as-set } for _, tc := range testCases { - peer := config.Peer{ASSet: util.Ptr(tc.asSet)} - err := Update(&peer, "rr.ntt.net", irrQueryTimeout, "") - if err != nil && tc.shouldError { - return - } - if err != nil && !tc.shouldError { - t.Error(err) - } else if err == nil && tc.shouldError { - t.Errorf("as-set %s should error but didn't", tc.asSet) - } - assert.Equal(t, tc.prefixSet4, *peer.PrefixSet4) - assert.Equal(t, tc.prefixSet6, *peer.PrefixSet6) + t.Run(tc.asSet, func(t *testing.T) { + peer := config.Peer{ASSet: util.Ptr(tc.asSet)} + err := Update(&peer, "rr.ntt.net", irrQueryTimeout, "bgpq4", "") + if err != nil && tc.shouldError { + return + } + if tc.shouldError { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + } + assert.Equal(t, tc.prefixSet4, *peer.PrefixSet4) + assert.Equal(t, tc.prefixSet6, *peer.PrefixSet6) + }) } } diff --git a/pkg/match/match.go b/pkg/match/match.go index ecb9528d..1f8bb0a4 100644 --- a/pkg/match/match.go +++ b/pkg/match/match.go @@ -4,9 +4,8 @@ import ( "fmt" "strings" - log "github.com/sirupsen/logrus" - "github.com/natesales/pathvector/pkg/peeringdb" + "github.com/natesales/pathvector/pkg/util/log" ) // CommonIXs gets common IXPs from PeeringDB diff --git a/pkg/optimizer/optimizer.go b/pkg/optimizer/optimizer.go index bc9c152a..e2cf8636 100644 --- a/pkg/optimizer/optimizer.go +++ b/pkg/optimizer/optimizer.go @@ -11,11 +11,11 @@ import ( "time" "github.com/go-ping/ping" - log "github.com/sirupsen/logrus" "github.com/natesales/pathvector/pkg/bird" "github.com/natesales/pathvector/pkg/config" "github.com/natesales/pathvector/pkg/util" + "github.com/natesales/pathvector/pkg/util/log" ) // Delimiter is an arbitrary delimiter used to split ASN from peerName @@ -132,8 +132,8 @@ func computeMetrics(o *config.Optimizer, global *config.Config, noConfigure bool // Calculate average latency and packet loss totalProbes := float64(len(o.Db[peer])) - p[peer].PacketLoss = p[peer].PacketLoss / totalProbes - p[peer].Latency = p[peer].Latency / time.Duration(totalProbes) + p[peer].PacketLoss /= totalProbes + p[peer].Latency /= time.Duration(totalProbes) // Check thresholds to apply optimizations var alerts []string @@ -172,11 +172,8 @@ func computeMetrics(o *config.Optimizer, global *config.Config, noConfigure bool modifyPref(peer, global.Peers, o.LocalPrefModifier, - global.CacheDirectory, global.BIRDDirectory, global.BIRDSocket, - global.BIRDBinary, - noConfigure, dryRun, ) } @@ -187,11 +184,8 @@ func modifyPref( peerPair string, peers map[string]*config.Peer, localPrefModifier uint, - cacheDirectory string, birdDirectory string, birdSocket string, - birdBinary string, - noConfigure bool, dryRun bool, ) { peerASN, peerName := parsePeerDelimiter(peerPair) @@ -214,14 +208,13 @@ func modifyPref( if err := os.WriteFile(fileName, []byte(modified), 0644); err != nil { log.Fatal(err) } else { - log.Printf("[Optimizer] Lowered AS%s %s local-pref from %d to %d", peerASN, peerName, currentLocalPref, newLocalPref) + log.Infof("[Optimizer] Lowered AS%s %s local-pref from %d to %d in %s", + peerASN, peerName, currentLocalPref, newLocalPref, fileName, + ) } } - // Run BIRD config validation - bird.Validate(birdBinary, birdDirectory) - if !dryRun { - bird.MoveCacheAndReconfigure(birdDirectory, cacheDirectory, birdSocket, noConfigure) + bird.Configure(birdSocket) } } diff --git a/pkg/optimizer/optimizer_test.go b/pkg/optimizer/optimizer_test.go index dc3bbf60..6e467ded 100644 --- a/pkg/optimizer/optimizer_test.go +++ b/pkg/optimizer/optimizer_test.go @@ -2,6 +2,8 @@ package optimizer import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestOptimizerSameAddressFamily(t *testing.T) { @@ -16,9 +18,8 @@ func TestOptimizerSameAddressFamily(t *testing.T) { {"2001:db8::1", "192.0.2.1", false}, } for _, tc := range testCases { - out := sameAddressFamily(tc.a, tc.b) - if out != tc.same { - t.Errorf("a %s b %s expected same %v got %v", tc.a, tc.b, tc.same, out) - } + t.Run(tc.a+"=="+tc.b, func(t *testing.T) { + assert.Equal(t, tc.same, sameAddressFamily(tc.a, tc.b)) + }) } } diff --git a/pkg/peeringdb/peeringdb.go b/pkg/peeringdb/peeringdb.go index a7ab45e2..8ed5d137 100644 --- a/pkg/peeringdb/peeringdb.go +++ b/pkg/peeringdb/peeringdb.go @@ -10,20 +10,12 @@ import ( "sync" "time" - log "github.com/sirupsen/logrus" - "github.com/natesales/pathvector/pkg/config" + "github.com/natesales/pathvector/pkg/util/log" ) // Endpoint is a public value to allow setting to a cache server -var Endpoint = "" - -func init() { - // Check if running in test - if os.Getenv("PATHVECTOR_TEST") == "1" { - Endpoint = "http://localhost:5000/api" - } -} +var Endpoint = "http://localhost:5001/api" type IxLanResponse struct { Data []IxLanData `json:"data"` @@ -179,10 +171,8 @@ func NeverViaRouteServers(queryTimeout uint, apiKey string) ([]uint32, error) { if apiKey != "" { req.Header.Add("AUTHORIZATION", "Api-Key "+apiKey) - } else { - if os.Getenv("PEERINGDB_API_KEY") != "" { - req.Header.Add("AUTHORIZATION", "Api-Key "+os.Getenv("PEERINGDB_API_KEY")) - } + } else if os.Getenv("PEERINGDB_API_KEY") != "" { + req.Header.Add("AUTHORIZATION", "Api-Key "+os.Getenv("PEERINGDB_API_KEY")) } res, err := httpClient.Do(req) @@ -225,10 +215,8 @@ func IXLANs(asn uint32, peeringDbQueryTimeout uint, apiKey string) ([]IxLanData, if apiKey != "" { req.Header.Add("AUTHORIZATION", "Api-Key "+apiKey) - } else { - if os.Getenv("PEERINGDB_API_KEY") != "" { - req.Header.Add("AUTHORIZATION", "Api-Key "+os.Getenv("PEERINGDB_API_KEY")) - } + } else if os.Getenv("PEERINGDB_API_KEY") != "" { + req.Header.Add("AUTHORIZATION", "Api-Key "+os.Getenv("PEERINGDB_API_KEY")) } res, err := httpClient.Do(req) diff --git a/pkg/peeringdb/peeringdb_test.go b/pkg/peeringdb/peeringdb_test.go index ce50b819..dda74390 100644 --- a/pkg/peeringdb/peeringdb_test.go +++ b/pkg/peeringdb/peeringdb_test.go @@ -31,7 +31,6 @@ func TestPeeringDbQuery(t *testing.T) { if err != nil && !tc.shouldError { t.Error(err) } - if tc.shouldError && err == nil { t.Errorf("asn %d should have errored but didn't", tc.asn) } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 0b4ffbe1..2e9882bf 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -3,10 +3,10 @@ package plugin import ( "fmt" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/natesales/pathvector/pkg/config" + "github.com/natesales/pathvector/pkg/util/log" ) var plugins = make(map[string]Plugin) diff --git a/pkg/process/process.go b/pkg/process/process.go index 34360adb..58df7277 100644 --- a/pkg/process/process.go +++ b/pkg/process/process.go @@ -16,7 +16,6 @@ import ( "github.com/creasty/defaults" "github.com/go-playground/validator/v10" - log "github.com/sirupsen/logrus" "github.com/natesales/pathvector/pkg/bird" "github.com/natesales/pathvector/pkg/block" @@ -27,6 +26,7 @@ import ( "github.com/natesales/pathvector/pkg/plugin" "github.com/natesales/pathvector/pkg/templating" "github.com/natesales/pathvector/pkg/util" + "github.com/natesales/pathvector/pkg/util/log" ) // categorizeCommunity checks if the community is in standard or large form, or an empty string if invalid @@ -126,7 +126,7 @@ func templateReplacements(in string, peer *config.Peer) string { key = "" if !field.IsZero() { val := fmt.Sprintf("%v", field.Elem().Interface()) - log.Tracef("Replacing %s with %s\n", key, val) + log.Tracef("Replacing %s with %s", key, val) in = strings.ReplaceAll(in, key, val) } } @@ -134,7 +134,38 @@ func templateReplacements(in string, peer *config.Peer) string { return in } +// parseStatics categorizes a multiprotocol statics map into IPv4 and IPv6 maps +func parseStatics(all map[string]string, v4 map[string]string, v6 map[string]string) error { + for prefix, nexthop := range all { + // Handle interface suffix + var rawNexthop string + if strings.Contains(nexthop, "%") { + rawNexthop = strings.Split(nexthop, "%")[0] + } else { + rawNexthop = nexthop + } + + pfx, _, err := net.ParseCIDR(prefix) + if err != nil { + return errors.New("Invalid static prefix: " + prefix) + } + if net.ParseIP(rawNexthop) == nil { + return errors.New("Invalid static nexthop: " + rawNexthop) + } + + if pfx.To4() == nil { // If IPv6 + v6[prefix] = nexthop + } else { // If IPv4 + v4[prefix] = nexthop + } + } + + return nil +} + // Load loads a configuration file from a YAML file +// +//gocyclo:ignore func Load(configBlob []byte) (*config.Config, error) { var c config.Config c.Init() @@ -181,8 +212,8 @@ func Load(configBlob []byte) (*config.Config, error) { } for peerName, peerData := range c.Peers { - // Set sanitized peer name peerData.ProtocolName = util.Sanitize(peerName) + peerData.UserSpecifiedName = &peerName // If any peer has NVRS filtering enabled, mark it for querying. if peerData.FilterNeverViaRouteServers != nil { @@ -231,9 +262,10 @@ func Load(configBlob []byte) (*config.Config, error) { fieldName := templateValueType.Field(i).Name fieldValue := peerValue.FieldByName(fieldName) defaultString := templateValueType.Field(i).Tag.Get("default") - if defaultString == "" { + switch { + case defaultString == "": log.Fatalf("Code error: field %s has no default value", fieldName) - } else if defaultString != "-" { + case defaultString != "-": log.Tracef("[%s] (before defaulting, after templating) field %s value %+v", peerName, fieldName, reflect.Indirect(fieldValue)) if fieldValue.IsNil() { elemToSwitch := templateValueType.Field(i).Type.Elem().Kind() @@ -261,13 +293,11 @@ func Load(configBlob []byte) (*config.Config, error) { default: log.Fatalf("Unknown kind %+v for field %s", elemToSwitch, fieldName) } - } else { // Add boolean values to the peer's config - if templateValueType.Field(i).Type.Elem().Kind() == reflect.Bool { - *peerData.BooleanOptions = append(*peerData.BooleanOptions, templateValueType.Field(i).Tag.Get("yaml")) - } + } else if templateValueType.Field(i).Type.Elem().Kind() == reflect.Bool { + *peerData.BooleanOptions = append(*peerData.BooleanOptions, templateValueType.Field(i).Tag.Get("yaml")) } - } else { + default: log.Tracef("[%s] skipping field %s with ignored default (-)", peerName, fieldName) } } @@ -310,17 +340,18 @@ func Load(configBlob []byte) (*config.Config, error) { for _, community := range communities { community = strings.ReplaceAll(community, ":", ",") communityType := categorizeCommunity(community) - if communityType == "standard" { + switch communityType { + case "standard": if _, ok := (*peerData.PrefixStandardCommunities)[prefix]; !ok { (*peerData.PrefixStandardCommunities)[prefix] = []string{} } (*peerData.PrefixStandardCommunities)[prefix] = append((*peerData.PrefixStandardCommunities)[prefix], community) - } else if communityType == "large" { + case "large": if _, ok := (*peerData.PrefixLargeCommunities)[prefix]; !ok { (*peerData.PrefixLargeCommunities)[prefix] = []string{} } (*peerData.PrefixLargeCommunities)[prefix] = append((*peerData.PrefixLargeCommunities)[prefix], community) - } else { + default: return nil, errors.New("Invalid prefix community: " + community) } } @@ -340,11 +371,12 @@ func Load(configBlob []byte) (*config.Config, error) { for community, pref := range *peerData.CommunityPrefs { community = strings.ReplaceAll(community, ":", ",") communityType := categorizeCommunity(community) - if communityType == "standard" { + switch communityType { + case "standard": (*peerData.StandardCommunityPrefs)[community] = pref - } else if communityType == "large" { + case "large": (*peerData.LargeCommunityPrefs)[community] = pref - } else { + default: return nil, errors.New("Invalid community pref: " + community) } } @@ -381,6 +413,8 @@ func Load(configBlob []byte) (*config.Config, error) { // Initialize static maps c.Kernel.Statics4 = map[string]string{} c.Kernel.Statics6 = map[string]string{} + c.Kernel.KStatics4 = map[string]string{} + c.Kernel.KStatics6 = map[string]string{} // Categorize communities var err error @@ -405,29 +439,11 @@ func Load(configBlob []byte) (*config.Config, error) { return nil, fmt.Errorf("invalid local community: %v", err) } - // Parse static routes - for prefix, nexthop := range c.Kernel.Statics { - // Handle interface suffix - var rawNexthop string - if strings.Contains(nexthop, "%") { - rawNexthop = strings.Split(nexthop, "%")[0] - } else { - rawNexthop = nexthop - } - - pfx, _, err := net.ParseCIDR(prefix) - if err != nil { - return nil, errors.New("Invalid static prefix: " + prefix) - } - if net.ParseIP(rawNexthop) == nil { - return nil, errors.New("Invalid static nexthop: " + rawNexthop) - } - - if pfx.To4() == nil { // If IPv6 - c.Kernel.Statics6[prefix] = nexthop - } else { // If IPv4 - c.Kernel.Statics4[prefix] = nexthop - } + if err := parseStatics(c.Kernel.Statics, c.Kernel.Statics4, c.Kernel.Statics6); err != nil { + return nil, fmt.Errorf("parsing statics: %s", err) + } + if err := parseStatics(c.Kernel.KStatics, c.Kernel.KStatics4, c.Kernel.KStatics6); err != nil { + return nil, fmt.Errorf("parsing kstatics: %s", err) } // Parse BFD configs @@ -455,11 +471,12 @@ func Load(configBlob []byte) (*config.Config, error) { } // Validate vrrpInstance - if vrrpInstance.State == "primary" { + switch { + case vrrpInstance.State == "primary": vrrpInstance.State = "MASTER" - } else if vrrpInstance.State == "backup" { + case vrrpInstance.State == "backup": vrrpInstance.State = "BACKUP" - } else { + default: return nil, errors.New("VRRP state must be 'primary' or 'backup', unexpected " + vrrpInstance.State) } } @@ -468,12 +485,12 @@ func Load(configBlob []byte) (*config.Config, error) { if c.RTRServer != "" { rtrServerParts := strings.Split(c.RTRServer, ":") if len(rtrServerParts) != 2 { - log.Fatalf("Invalid rtr-server '%s' format should be host:port", rtrServerParts) + return nil, fmt.Errorf("invalid rtr-server '%s' format should be host:port", rtrServerParts) } c.RTRServerHost = rtrServerParts[0] rtrServerPort, err := strconv.Atoi(rtrServerParts[1]) if err != nil { - log.Fatalf("Invalid RTR server port %s", rtrServerParts[1]) + return nil, fmt.Errorf("invalid RTR server port %s", rtrServerParts[1]) } c.RTRServerPort = rtrServerPort } @@ -547,7 +564,7 @@ func Load(configBlob []byte) (*config.Config, error) { } // peer processes a single peer -func peer(peerName string, peerData *config.Peer, c *config.Config, wg *sync.WaitGroup) { +func peer(peerName string, peerData *config.Peer, c *config.Config, wg *sync.WaitGroup) error { defer wg.Done() log.Debugf("Processing AS%d %s", *peerData.ASN, peerName) @@ -561,14 +578,14 @@ func peer(peerName string, peerData *config.Peer, c *config.Config, wg *sync.Wai // Build IRR prefix sets if *peerData.FilterIRR { - if err := irr.Update(peerData, c.IRRServer, c.IRRQueryTimeout, c.BGPQArgs); err != nil { - log.Fatal(err) + if err := irr.Update(peerData, c.IRRServer, c.IRRQueryTimeout, c.BGPQBin, c.BGPQArgs); err != nil { + return err } } if *peerData.AutoASSetMembers { membersFromIRR, err := irr.ASMembers(*peerData.ASSet, c.IRRServer, c.IRRQueryTimeout, c.BGPQArgs) if err != nil { - log.Fatal(err) + return err } if peerData.ASSetMembers == nil { peerData.ASSetMembers = &membersFromIRR @@ -579,7 +596,7 @@ func peer(peerName string, peerData *config.Peer, c *config.Config, wg *sync.Wai } } if *peerData.FilterASSet && (peerData.ASSetMembers == nil || len(*peerData.ASSetMembers) < 1) { - log.Fatalf("peer has filter-as-set enabled but no members in it's as-set") + return fmt.Errorf("peer has filter-as-set enabled but no members in it's as-set") } util.PrintStructInfo(peerName, peerData) @@ -588,7 +605,7 @@ func peer(peerName string, peerData *config.Peer, c *config.Config, wg *sync.Wai peerFileName := path.Join(c.CacheDirectory, fmt.Sprintf("AS%d_%s.conf", *peerData.ASN, *util.Sanitize(peerName))) peerSpecificFile, err := os.Create(peerFileName) if err != nil { - log.Fatalf("Create peer specific output file: %v", err) + return fmt.Errorf("create peer output file: %v", err) } // Render the template and write to buffer @@ -599,20 +616,21 @@ func peer(peerName string, peerData *config.Peer, c *config.Config, wg *sync.Wai Peer: *peerData, Config: *c, }); err != nil { - log.Fatalf("Execute template: %v", err) + return fmt.Errorf("execute template: %v", err) } // Reformat config and write template to file if _, err := peerSpecificFile.Write([]byte(bird.Reformat(b.String()))); err != nil { - log.Fatalf("Write template to file: %v", err) + return fmt.Errorf("write template to file: %v", err) } log.Debugf("[%s] Wrote config", peerName) + + return nil } -// Run runs the full data generation procedure -func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw bool) { - // Check lockfile +// lock prevents multiple instances of Pathvector from running +func lock(lockFile string) { if lockFile != "" { if _, err := os.Stat(lockFile); err == nil { log.Fatal("Lockfile exists, exiting") @@ -627,6 +645,11 @@ func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw log.Fatalf("Accessing lockfile: %v", err) } } +} + +// Run runs the full data generation procedure +func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw bool) { + lock(lockFile) log.Infof("Starting Pathvector %s", version) startTime := time.Now() @@ -660,7 +683,11 @@ func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw } log.Debug("Finished loading templates") - // Create cache directory + // Delete and (re)create cache directory + log.Debug("Deleting cache directory") + if err := os.RemoveAll(c.CacheDirectory); err != nil { + log.Warnf("Deleting cache directory: %v", err) + } log.Debugf("Making cache directory %s", c.CacheDirectory) if err := os.MkdirAll(c.CacheDirectory, os.FileMode(0755)); err != nil { log.Fatal(err) @@ -675,10 +702,15 @@ func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw log.Debug("Finished creating global config file") // Render the global template and write to buffer - log.Debug("Writing global config file") - if err := templating.Template.ExecuteTemplate(globalFile, "global.tmpl", c); err != nil { + var globalBuffer bytes.Buffer + if err := templating.Template.ExecuteTemplate(&globalBuffer, "global.tmpl", c); err != nil { log.Fatalf("Execute global template: %v", err) } + + log.Debug("Writing global config file") + if _, err := globalFile.Write([]byte(bird.Reformat(globalBuffer.String()))); err != nil { + log.Fatalf("Write global template to file: %v", err) + } log.Debug("Finished writing global config file") // Remove old manual configs @@ -709,12 +741,18 @@ func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw wg := new(sync.WaitGroup) for peerName, peerData := range c.Peers { wg.Add(1) - go peer(peerName, peerData, c, wg) + go func(peerName string, peerData *config.Peer, c *config.Config, wg *sync.WaitGroup) { + if err := peer(peerName, peerData, c, wg); err != nil { + log.Fatal(err) + } + }(peerName, peerData, c, wg) } // end peer loop wg.Wait() // Run BIRD config validation - bird.Validate(c.BIRDBinary, c.CacheDirectory) + if err := bird.Validate(c.BIRDBinary, c.CacheDirectory); err != nil { + log.Fatalf("BIRD config validation: %v", err) + } // Copy config file log.Debug("Copying Pathvector config file to cache directory") @@ -729,7 +767,7 @@ func Run(configFilename, lockFile, version string, noConfigure, dryRun, withdraw if err != nil { log.Fatalf("Marshalling protocol names: %v", err) } - file := path.Join(c.BIRDDirectory, "protocols.json") + file := path.Join(c.CacheDirectory, "protocols.json") log.Debugf("Writing protocol names to %s", file) //nolint:golint,gosec if err := os.WriteFile(file, j, 0644); err != nil { diff --git a/pkg/process/process_test.go b/pkg/process/process_test.go index d8495328..3f3cebd5 100644 --- a/pkg/process/process_test.go +++ b/pkg/process/process_test.go @@ -34,12 +34,10 @@ func TestCategorizeCommunity(t *testing.T) { } for _, tc := range testCases { cType := categorizeCommunity(tc.input) - if cType != "" && tc.shouldError { - t.Errorf("categorizeCommunity should have errored on '%s' but didn't. expected error, got '%s'", tc.input, cType) - } else if cType == "" && !tc.shouldError { - t.Errorf("categorizeCommunity shouldn't have errored on '%s' but did. expected '%s'", tc.input, tc.expectedOutput) - } else if cType != tc.expectedOutput { - t.Errorf("categorizeCommunity %s failed. expected '%v' got '%v'", tc.input, tc.expectedOutput, cType) + if tc.shouldError { + assert.Equal(t, "", cType) + } else { + assert.Equal(t, tc.expectedOutput, cType) } } } @@ -111,22 +109,18 @@ peers: assert.NoError(t, err) assert.Len(t, globalConfig.Peers, 2) - for peerName, peerData := range globalConfig.Peers { - switch peerName { - case "Peer 10": - assert.Equal(t, 65510, util.Deref(peerData.ASN)) - assert.Equal(t, 110, util.Deref(peerData.LocalPref)) - assert.True(t, util.Deref(peerData.SetLocalPref)) - assert.Nil(t, peerData.DefaultLocalPref) - case "Peer 20": - assert.Equal(t, 65520, util.Deref(peerData.ASN)) - assert.Equal(t, 100, util.Deref(peerData.LocalPref)) - assert.False(t, util.Deref(peerData.SetLocalPref)) - assert.Equal(t, 120, util.Deref(peerData.DefaultLocalPref)) - default: - t.Errorf("peer %s unexpected", peerName) - } - } + + peer10 := globalConfig.Peers["Peer 10"] + assert.Equal(t, 65510, util.Deref(peer10.ASN)) + assert.Equal(t, 110, util.Deref(peer10.LocalPref)) + assert.True(t, util.Deref(peer10.SetLocalPref)) + assert.Nil(t, peer10.DefaultLocalPref) + + peer20 := globalConfig.Peers["Peer 20"] + assert.Equal(t, 65520, util.Deref(peer20.ASN)) + assert.Equal(t, 100, util.Deref(peer20.LocalPref)) + assert.False(t, util.Deref(peer20.SetLocalPref)) + assert.Equal(t, 120, util.Deref(peer20.DefaultLocalPref)) } func TestLoadConfigInvalidYAML(t *testing.T) { @@ -186,9 +180,8 @@ kernel: "2001:db8:2::/64" : "2001:db8::1" ` _, err := Load([]byte(configFile)) - if err == nil || !strings.Contains(err.Error(), "Invalid static prefix") { - t.Errorf("expected invalid static prefix error, got %+v", err) - } + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Invalid static prefix") } func TestLoadConfigInvalidVIP(t *testing.T) { @@ -241,52 +234,25 @@ peers: - 192.0.2.4 ` globalConfig, err := Load([]byte(configFile)) - if err != nil { - t.Error(err) - } + assert.Nil(t, err) - for peerName, peerData := range globalConfig.Peers { - if peerName == "Upstream 1" { - if *peerData.ASN != 65510 { - t.Errorf("peer %s expected ASN 65510 got %d", peerName, *peerData.ASN) - } - if *peerData.LocalPref != 90 { - t.Errorf("peer %s expected local-pref 90 got %d", peerName, *peerData.LocalPref) - } - if *peerData.FilterIRR != false { - t.Errorf("peer %s expected filter-irr false got %v", peerName, *peerData.FilterIRR) - } - if *peerData.FilterRPKI != true { - t.Errorf("peer %s expected filter-rpki true got %v", peerName, *peerData.FilterIRR) - } - } else if peerName == "Upstream 2" { - if *peerData.ASN != 65520 { - t.Errorf("peer %s expected ASN 65520 got %d", peerName, *peerData.ASN) - } - if *peerData.LocalPref != 90 { - t.Errorf("peer %s expected local-pref 90 got %d", peerName, *peerData.LocalPref) - } - if *peerData.FilterIRR != true { - t.Errorf("peer %s expected filter-irr true got %v", peerName, *peerData.FilterIRR) - } - if *peerData.FilterRPKI != true { - t.Errorf("peer %s expected filter-rpki true got %v", peerName, *peerData.FilterIRR) - } - } else if peerName == "Upstream 3" { - if *peerData.ASN != 65530 { - t.Errorf("peer %s expected ASN 65530 got %d", peerName, *peerData.ASN) - } - if *peerData.LocalPref != 2 { - t.Errorf("peer %s expected local-pref 2 got %d", peerName, *peerData.LocalPref) - } - if *peerData.FilterIRR != false { - t.Errorf("peer %s expected filter-irr false got %v", peerName, *peerData.FilterIRR) - } - if *peerData.FilterRPKI != true { - t.Errorf("peer %s expected filter-rpki true got %v", peerName, *peerData.FilterIRR) - } - } else { - t.Errorf("") - } - } + assert.Len(t, globalConfig.Peers, 3) + + upstream1 := globalConfig.Peers["Upstream 1"] + assert.Equal(t, 65510, *upstream1.ASN) + assert.Equal(t, 90, *upstream1.LocalPref) + assert.False(t, *upstream1.FilterIRR) + assert.True(t, *upstream1.FilterRPKI) + + upstream2 := globalConfig.Peers["Upstream 2"] + assert.Equal(t, 65520, *upstream2.ASN) + assert.Equal(t, 90, *upstream2.LocalPref) + assert.True(t, *upstream2.FilterIRR) + assert.True(t, *upstream2.FilterRPKI) + + upstream3 := globalConfig.Peers["Upstream 3"] + assert.Equal(t, 65530, *upstream3.ASN) + assert.Equal(t, 2, *upstream3.LocalPref) + assert.False(t, *upstream3.FilterIRR) + assert.True(t, *upstream3.FilterRPKI) } diff --git a/pkg/templating/templating.go b/pkg/templating/templating.go index bd10d1d5..0ac52619 100644 --- a/pkg/templating/templating.go +++ b/pkg/templating/templating.go @@ -4,16 +4,15 @@ import ( "embed" "fmt" "os" + "slices" "strconv" "strings" "sync" "text/template" "time" - log "github.com/sirupsen/logrus" - "github.com/natesales/pathvector/pkg/config" - "github.com/natesales/pathvector/pkg/util" + "github.com/natesales/pathvector/pkg/util/log" ) var ( @@ -89,7 +88,7 @@ var funcMap = template.FuncMap{ if format == "unix" { return strconv.Itoa(int(time.Now().Unix())) } - return time.Now().String() + return time.Now().UTC().Format(time.RFC822) }, "MakeSlice": func(args ...interface{}) []interface{} { @@ -193,21 +192,22 @@ var funcMap = template.FuncMap{ // UniqueProtocolName takes a protocol-safe string and address family and returns a unique protocol name "UniqueProtocolName": func(s, userSuppliedName *string, af string, asn *int, tags *[]string) string { + protocolNameMapLock.Lock() + defer protocolNameMapLock.Unlock() + protoName := fmt.Sprintf("%s_AS%d_v%s", *s, *asn, af) i := 1 for { - if !util.Contains(protocolNames, protoName) { + if !slices.Contains(protocolNames, protoName) { protocolNames = append(protocolNames, protoName) var t []string if tags != nil { t = *tags } - protocolNameMapLock.Lock() protocolNameMap[protoName] = &Protocol{ Name: *userSuppliedName, Tags: t, } - protocolNameMapLock.Unlock() return protoName } protoName = fmt.Sprintf("%s_AS%d_v%s_%d", *s, *asn, af, i) diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go new file mode 100644 index 00000000..2f186994 --- /dev/null +++ b/pkg/util/log/log.go @@ -0,0 +1,114 @@ +package log + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/charmbracelet/log" +) + +type Level int32 + +const ( + TraceLevel = Level(-5) + DebugLevel = Level(log.DebugLevel) + InfoLevel = Level(log.InfoLevel) + WarnLevel = Level(log.WarnLevel) + ErrorLevel = Level(log.ErrorLevel) + FatalLevel = Level(log.FatalLevel) +) + +var ( + logger = log.Default() + writer io.Writer = os.Stdout +) + +func SetLevel(l Level) { + logger.SetLevel(log.Level(l)) +} + +func Capture() *bytes.Buffer { + var buf bytes.Buffer + writer = &buf + tee := io.MultiWriter(writer, os.Stdout) + logger.SetOutput(tee) + return &buf +} + +func ResetCapture() { + logger.SetOutput(os.Stderr) + writer = os.Stdout +} + +// Println prints a line with no formatting or timestamp or anything +func Println(msg ...any) { + _, _ = fmt.Fprintln(writer, msg...) +} + +func Printf(format string, args ...any) { + _, _ = fmt.Fprintf(writer, format, args...) +} + +// Trace logs a trace message. +func Trace(msg interface{}, keyvals ...any) { + logger.Log(log.Level(TraceLevel), msg, keyvals...) +} + +// Debug logs a debug message. +func Debug(msg interface{}, keyvals ...any) { + logger.Log(log.DebugLevel, msg, keyvals...) +} + +// Info logs an info message. +func Info(msg interface{}, keyvals ...any) { + logger.Log(log.InfoLevel, msg, keyvals...) +} + +// Warn logs a warning message. +func Warn(msg interface{}, keyvals ...any) { + logger.Log(log.WarnLevel, msg, keyvals...) +} + +// Error logs an error message. +func Error(msg interface{}, keyvals ...any) { + logger.Log(log.ErrorLevel, msg, keyvals...) +} + +// Fatal logs a fatal message and exit. +func Fatal(msg interface{}, keyvals ...any) { + logger.Log(log.FatalLevel, msg, keyvals...) + os.Exit(1) +} + +// Tracef logs a trace message with formatting. +func Tracef(format string, args ...any) { + logger.Log(log.Level(TraceLevel), fmt.Sprintf(format, args...)) +} + +// Debugf logs a debug message with formatting. +func Debugf(format string, args ...any) { + logger.Log(log.DebugLevel, fmt.Sprintf(format, args...)) +} + +// Infof logs an info message with formatting. +func Infof(format string, args ...any) { + logger.Log(log.InfoLevel, fmt.Sprintf(format, args...)) +} + +// Warnf logs a warning message with formatting. +func Warnf(format string, args ...any) { + logger.Log(log.WarnLevel, fmt.Sprintf(format, args...)) +} + +// Errorf logs an error message with formatting. +func Errorf(format string, args ...any) { + logger.Log(log.ErrorLevel, fmt.Sprintf(format, args...)) +} + +// Fatalf logs a fatal message with formatting and exit. +func Fatalf(format string, args ...any) { + logger.Log(log.FatalLevel, fmt.Sprintf(format, args...)) + os.Exit(1) +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 548c993e..752bb94f 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -6,31 +6,23 @@ import ( "os" "path/filepath" "reflect" + "slices" "strings" "unicode" "github.com/olekukonko/tablewriter" - log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" + + "github.com/natesales/pathvector/pkg/util/log" ) var alphabet = strings.Split("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "") -// Contains runs a linear search on a string array -func Contains(a []string, x string) bool { - for _, n := range a { - if x == n { - return true - } - } - return false -} - // Sanitize limits an input string to only uppercase letters and numbers func Sanitize(input string) *string { output := "" for _, chr := range strings.ReplaceAll(strings.ToUpper(input), " ", "_") { - if Contains(alphabet, string(chr)) || string(chr) == "_" { + if slices.Contains(alphabet, string(chr)) || string(chr) == "_" { output += string(chr) } } @@ -90,10 +82,10 @@ func PrintStructInfo(label string, instance interface{}) { typeOf := s.Type() for i := 0; i < s.NumField(); i++ { attrName := typeOf.Field(i).Name - if !(Contains(excludedFields, attrName)) { + if !(slices.Contains(excludedFields, attrName)) { v := reflect.Indirect(s.Field(i)) if v.IsValid() { - log.Tracef("[%s] field %s = %v\n", label, attrName, v) + log.Tracef("[%s] field %s = %v", label, attrName, v) } } } @@ -101,7 +93,9 @@ func PrintStructInfo(label string, instance interface{}) { // PrintTable prints a table of data func PrintTable(header []string, data [][]string) { - table := tablewriter.NewWriter(os.Stdout) + var buf bytes.Buffer + + table := tablewriter.NewWriter(&buf) table.SetHeader(header) table.SetAutoFormatHeaders(true) table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) @@ -116,6 +110,8 @@ func PrintTable(header []string, data [][]string) { table.SetAutoWrapText(false) table.AppendBulk(data) table.Render() + + log.Println(buf.String()) } // RemoveFileGlob removes files by glob @@ -225,3 +221,8 @@ func YAMLUnmarshalStrict(y []byte, v interface{}) error { decoder.KnownFields(true) return decoder.Decode(v) } + +// IsPrivateASN checks if an ASN is private +func IsPrivateASN(asn uint32) bool { + return (asn >= 64512 && asn <= 65535) || (asn >= 4200000000 && asn <= 4294967294) +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index a022543c..3a71ef75 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -2,6 +2,7 @@ package util import ( "os" + "slices" "testing" "github.com/stretchr/testify/assert" @@ -17,7 +18,7 @@ func TestContains(t *testing.T) { {[]string{"foo", "bar"}, "baz", false}, } for _, tc := range testCases { - if out := Contains(tc.array, tc.element); out != tc.expectedOutput { + if out := slices.Contains(tc.array, tc.element); out != tc.expectedOutput { t.Errorf("array %+v element %s failed. expected '%v' got '%v'", tc.array, tc.element, tc.expectedOutput, out) } } @@ -45,38 +46,30 @@ func TestSanitize(t *testing.T) { func TestMoveFile(t *testing.T) { // Make temporary cache directory - if err := os.Mkdir("test-cache", 0755); err != nil && !os.IsExist(err) { + if err := os.Mkdir("/tmp/test-move", 0755); err != nil && !os.IsExist(err) { t.Error(err) } inputString := "Test File" //nolint:golint,gosec - if err := os.WriteFile("test-cache/source.txt", []byte(inputString), 0644); err != nil { - t.Error(err) - } + assert.Nil(t, os.WriteFile("/tmp/test-move/source.txt", []byte(inputString), 0644)) - if err := MoveFile("test-cache/source.txt", "test-cache/dest.txt"); err != nil { - t.Error(err) - } + assert.Nil(t, MoveFile("/tmp/test-move/source.txt", "/tmp/test-move/dest.txt")) - if _, err := os.Stat("test-cache/dest.txt"); os.IsNotExist(err) { + if _, err := os.Stat("/tmp/test-move/dest.txt"); os.IsNotExist(err) { t.Errorf("file text-cache/dest.txt doesn't exist but should") } - if _, err := os.Stat("test-cache/source.txt"); err == nil { + if _, err := os.Stat("/tmp/test-move/source.txt"); err == nil { t.Errorf("file text-cache/source.txt exists but shouldn't") } - if contents, err := os.ReadFile("test-cache/dest.txt"); err != nil { - if string(contents) != inputString { - t.Errorf("expected %s got %s", inputString, contents) - } - } + contents, err := os.ReadFile("/tmp/test-move/dest.txt") + assert.Nil(t, err) + assert.Equal(t, inputString, string(contents)) - if err := os.Remove("test-cache/dest.txt"); err != nil { - t.Error(err) - } + assert.Nil(t, os.Remove("/tmp/test-move/dest.txt")) } func TestPrintTable(t *testing.T) { @@ -96,3 +89,10 @@ func TestUtilPtrDeref(t *testing.T) { assert.Equal(t, "foo", StrDeref(Ptr("foo"))) assert.Equal(t, "", StrDeref(nil)) } + +func TestUtilIsPrivateASN(t *testing.T) { + assert.True(t, IsPrivateASN(65534)) + assert.True(t, IsPrivateASN(65535)) + assert.True(t, IsPrivateASN(4200000000)) + assert.False(t, IsPrivateASN(112)) +} diff --git a/tests/bird-entrypoint.sh b/tests/bird-entrypoint.sh new file mode 100755 index 00000000..f90aa5ce --- /dev/null +++ b/tests/bird-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ex +apt install -y socat +bird -d & +socat TCP-LISTEN:5002,fork UNIX-CONNECT:/usr/local/var/run/bird.ctl diff --git a/tests/bird-matrix/build-bird-versions.sh b/tests/bird-matrix/build-bird-versions.sh deleted file mode 100755 index 6acf45dc..00000000 --- a/tests/bird-matrix/build-bird-versions.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# Build the last 5 bird versions - -if [ ! -d bird ]; then - git clone https://gitlab.nic.cz/labs/bird.git -fi - -cd bird || exit 1 - -for tag in $(git tag | grep "^v2.0." | sort -V | tail -n 5); do - echo "Building $tag" - git reset --hard HEAD - git checkout "$tag" - autoreconf - ./configure - make - mkdir ../"$tag" - mv bird ../"$tag" - mv birdc ../"$tag" -done - -rm -rf bird diff --git a/tests/bird-matrix/run-tests.sh b/tests/bird-matrix/run-tests.sh deleted file mode 100755 index 520b6487..00000000 --- a/tests/bird-matrix/run-tests.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -#for tag in $(ls | grep "^v2.0." | sort -V); do -# echo "Testing $tag" -#done - -version=$1 -user=$(whoami) - -echo Starting BIRD "$version" -sudo mkdir -p /run/bird -sudo mkdir -p /etc/bird -echo "protocol device {}" | sudo tee /etc/bird/bird.conf >/dev/null -echo Starting bird - -{ - sleep 1 - sudo chown "$user":"$user" /run/bird - sudo chown "$user":"$user" /run/bird/bird.ctl - birdc show status -} & - -sudo "$version"/bird -s /run/bird/bird.ctl -c /etc/bird/bird.conf -d diff --git a/tests/generate-complex.yml b/tests/generate-complex.yml index 2eed6688..f6c8f1f7 100644 --- a/tests/generate-complex.yml +++ b/tests/generate-complex.yml @@ -5,10 +5,12 @@ source6: 2001:db8::1 prefixes: - 192.0.2.0/24 - 2001:db8::/48 -web-ui-file: test-cache/ui.html -cache-directory: test-cache +web-ui-file: /tmp/test-cache/ui.html +cache-directory: /tmp/test-cache blackhole-bogon-asns: true -peeringdb-url: http://localhost:5000/api +peeringdb-url: http://localhost:5001/api +bird-directory: /tmp/bird-conf +bird-socket: tcp://localhost:5002 origin-communities: - 34553:10 @@ -32,6 +34,10 @@ kernel: - 65530:65530:1 statics: "192.0.2.0/24": "203.0.113.1%eth0" + "2001:db8::/48": "2001:db8::1%eth0" + kstatics: + "192.0.2.30/32": "203.0.113.1" + "2001:db8::30/128": "2001:db8::1%eth0" blocklist: [ "AS65530", "192.0.2.0/24" ] blocklist-urls: [ "https://raw.githubusercontent.com/natesales/pathvector/main/tests/blocklist.txt" ] diff --git a/tests/generate-simple.yml b/tests/generate-simple.yml index 14fa2cde..f262e175 100644 --- a/tests/generate-simple.yml +++ b/tests/generate-simple.yml @@ -5,9 +5,11 @@ source6: 2001:db8::1 prefixes: - 192.0.2.0/24 - 2001:db8::/48 -web-ui-file: test-cache/ui.html -cache-directory: test-cache -peeringdb-url: http://localhost:5000/api +web-ui-file: /tmp/test-cache/ui.html +cache-directory: /tmp/test-cache +peeringdb-url: http://localhost:5001/api +bird-directory: /tmp/bird-conf +bird-socket: tcp://localhost:5002 peers: Example: diff --git a/tests/peeringdb/Dockerfile b/tests/peeringdb/Dockerfile new file mode 100644 index 00000000..9dab165d --- /dev/null +++ b/tests/peeringdb/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.12-slim + +WORKDIR /app + +RUN pip install --no-cache-dir flask + +COPY . . + +EXPOSE 5000 + +CMD ["python", "peeringdb-test-api.py"] diff --git a/tests/peeringdb/peeringdb-test-api.py b/tests/peeringdb/peeringdb-test-api.py index 21ec803c..2c5eefac 100644 --- a/tests/peeringdb/peeringdb-test-api.py +++ b/tests/peeringdb/peeringdb-test-api.py @@ -1,10 +1,10 @@ """ -PeeringDB test harness +PeeringDB test API Supported queries: https://peeringdb.com/api/net?info_never_via_route_servers=1 -https://peeringdb.com/api/netixlan?asn=%d https://peeringdb.com/api/net?asn=%d +https://peeringdb.com/api/netixlan?asn=%d """ import json @@ -13,14 +13,17 @@ app = Flask(__name__) -nvrs = json.loads(open("tests/peeringdb/nvrs.json").read()) -nets = json.loads(open("tests/peeringdb/net.json").read()) -netixlans = json.loads(open("tests/peeringdb/netixlan.json").read()) +nvrs = json.loads(open("nvrs.json").read()) +nets = json.loads(open("net.json").read()) +netixlans = json.loads(open("netixlan.json").read()) def response(d): return {"data": [d], "meta": {}} +@app.route("/") +def index(): + return "PeeringDB test API" @app.route("/api/net", methods=["GET"]) def net(): @@ -44,4 +47,4 @@ def netixlan(): if __name__ == "__main__": - app.run("0.0.0.0", port=5000) + app.run("0.0.0.0", port=5001) diff --git a/tests/probe-simple.yml b/tests/probe-simple.yml index beb148e5..200660dc 100644 --- a/tests/probe-simple.yml +++ b/tests/probe-simple.yml @@ -5,8 +5,10 @@ source6: 2001:db8::1 prefixes: - 192.0.2.0/24 - 2001:db8::/48 -cache-directory: test-cache -peeringdb-url: http://localhost:5000/api +cache-directory: /tmp/test-cache +peeringdb-url: http://localhost:5001/api +bird-directory: /tmp/bird-conf +bird-socket: tcp://localhost:5002 optimizer: probe-udp: true diff --git a/tests/suite/Justfile b/tests/suite/Justfile new file mode 100644 index 00000000..f25ed6fb --- /dev/null +++ b/tests/suite/Justfile @@ -0,0 +1,9 @@ +down: + docker-compose down + sudo rm -rf *-run + +up: + BIRD_VERSION=2.14 docker-compose up -d + +birdc container: + sudo socat - UNIX-CONNECT:./{{ container }}-run/bird.ctl diff --git a/tests/suite/README.md b/tests/suite/README.md new file mode 100644 index 00000000..eed481f7 --- /dev/null +++ b/tests/suite/README.md @@ -0,0 +1,20 @@ +# Matrix Test Suite + +## Session establishment test + +- Run BIRD on both router containers +- Namespaced routing tables, control sockets, and cache directories + +### Assertions +- Session established +- Route received +- Route installed in kernel +- Ping + +## Complex config generation test + +- Generate with `generate-complex` on different versions of BIRD + +## Test Suite + +Go test flag to use container test target diff --git a/tests/suite/alpha.conf b/tests/suite/alpha.conf new file mode 100644 index 00000000..415f0779 --- /dev/null +++ b/tests/suite/alpha.conf @@ -0,0 +1,19 @@ +router id 172.16.97.2; + +protocol device {} +protocol direct { ipv4; ipv6; } + +protocol static { + ipv4; + route 172.16.98.0/24 reject; +} + +protocol bgp bravo { + local 172.16.97.2 as 65002; + neighbor 172.16.97.3 as 65003; + + ipv4 { + import all; + export where source = RTS_STATIC; + }; +} diff --git a/tests/suite/bravo.conf b/tests/suite/bravo.conf new file mode 100644 index 00000000..fc51d412 --- /dev/null +++ b/tests/suite/bravo.conf @@ -0,0 +1,4 @@ +router id 172.16.97.3; + +protocol device {} +protocol direct { ipv4; ipv6; } diff --git a/tests/suite/docker-compose.yml b/tests/suite/docker-compose.yml new file mode 100644 index 00000000..ddbce748 --- /dev/null +++ b/tests/suite/docker-compose.yml @@ -0,0 +1,28 @@ +version: "3.8" +services: + alpha: + image: pierky/bird:${BIRD_VERSION} + container_name: alpha + volumes: + - ./alpha.conf:/etc/bird/bird.conf + - ./alpha-run/:/usr/local/var/run/ + networks: + default: + ipv4_address: 172.16.97.2 + + bravo: + image: pierky/bird:${BIRD_VERSION} + container_name: bravo + volumes: + - ./bravo.conf:/etc/bird/bird.conf + - ./bravo-run/:/usr/local/var/run/ + networks: + default: + ipv4_address: 172.16.97.3 + +networks: + default: + ipam: + driver: default + config: + - subnet: 172.16.97.0/24 diff --git a/tests/suite/pathvector.yml b/tests/suite/pathvector.yml new file mode 100644 index 00000000..491c7a51 --- /dev/null +++ b/tests/suite/pathvector.yml @@ -0,0 +1,10 @@ +asn: 65003 +router-id: 172.16.97.3 +prefixes: + - 172.16.99.0/24 + +peers: + alpha: + asn: 65002 + peers: + - 172.16.97.2