From 774595e86065a9e08ade52936aae1b0165abd00b Mon Sep 17 00:00:00 2001 From: Zeping Bai Date: Sun, 14 Jul 2024 15:43:07 +0800 Subject: [PATCH 1/3] feat: introduce next generation ADC (#138) Co-authored-by: Navendu Pottekkat --- .dockerignore | 32 - .editorconfig | 13 + .eslintignore | 1 + .eslintrc.json | 42 + .gitattributes | 1 - .github/dependabot.yml | 5 +- .github/workflows/cli_test.yaml | 78 - .github/workflows/e2e.yaml | 60 + .github/workflows/golangci-lint.yml | 54 - .github/workflows/mtls_test.yaml | 78 - .github/workflows/release.yaml | 140 +- .github/workflows/unit.yaml | 27 + .github/workflows/unit_test.yaml | 61 - .gitignore | 58 +- .goreleaser.yaml | 26 - .npmrc | 2 + .prettierignore | 4 + .prettierrc | 20 + .vscode/extensions.json | 8 + .vscode/settings.json | 3 + CONTRIBUTING.md | 75 - Dockerfile | 22 - Makefile | 38 +- README.md | 122 +- apisix.yaml | 23 - apps/cli-e2e/.eslintrc.json | 18 + apps/cli-e2e/README.md | 5 + apps/cli-e2e/jest.config.ts | 18 + apps/cli-e2e/project.json | 26 + apps/cli-e2e/src/support/global-setup.ts | 3 + apps/cli-e2e/src/support/global-teardown.ts | 3 + apps/cli-e2e/tsconfig.json | 13 + apps/cli-e2e/tsconfig.spec.json | 9 + apps/cli/.eslintrc.json | 18 + apps/cli/jest.config.ts | 11 + apps/cli/node-sea.json | 5 + apps/cli/project.json | 53 + apps/cli/scripts/download-node.ts | 141 + apps/cli/src/assets/.gitkeep | 0 apps/cli/src/command/convert.command.ts | 96 + apps/cli/src/command/dev.command.ts | 11 + apps/cli/src/command/diff.command.ts | 199 + apps/cli/src/command/dump.command.ts | 112 + apps/cli/src/command/helper.ts | 205 + apps/cli/src/command/index.ts | 39 + apps/cli/src/command/lint.command.ts | 72 + apps/cli/src/command/ping.command.ts | 22 + apps/cli/src/command/sync.command.ts | 64 + apps/cli/src/command/typing.d.ts | 29 + apps/cli/src/command/utils.spec.ts | 252 + apps/cli/src/command/utils.ts | 283 + apps/cli/src/differ/differv3.ts | 668 + apps/cli/src/differ/specs/basic.spec.ts | 858 ++ apps/cli/src/differ/specs/usecase.spec.ts | 262 + apps/cli/src/linter/index.ts | 7 + apps/cli/src/linter/schema.ts | 287 + apps/cli/src/main.ts | 7 + apps/cli/src/utils/listr.ts | 113 + apps/cli/tsconfig.app.json | 23 + apps/cli/tsconfig.json | 21 + apps/cli/tsconfig.spec.json | 14 + apps/cli/webpack.config.js | 23 + cmd/configure.go | 217 - cmd/diff.go | 27 - cmd/dump.go | 173 - cmd/openapi2apisix.go | 105 - cmd/ping.go | 45 - cmd/root.go | 108 - cmd/sync.go | 190 - cmd/utils.go | 14 - cmd/validate.go | 141 - cmd/version.go | 25 - go.mod | 87 - go.sum | 695 - install.sh | 89 - internal/pkg/db/memdb.go | 261 - internal/pkg/db/memdb_test.go | 99 - internal/pkg/differ/differ.go | 695 - internal/pkg/differ/differ_test.go | 346 - internal/pkg/openapi2apisix/openapi2apisix.go | 266 - .../pkg/openapi2apisix/openapi2apisix_test.go | 382 - .../testdata/Postman-API101.yaml | 152 - .../openapi2apisix/testdata/operationId.yaml | 82 - .../pkg/openapi2apisix/testdata/tags.json | 61 - .../pkg/openapi2apisix/testdata/tags.yaml | 55 - .../openapi2apisix/testdata/urlVariables.yaml | 86 - internal/pkg/openapi2apisix/types.go | 61 - internal/pkg/openapi2apisix/types_test.go | 84 - internal/pkg/validator/validator.go | 126 - jest.config.ts | 5 + jest.preset.js | 3 + libs/backend-api7/.eslintrc.json | 18 + libs/backend-api7/README.md | 8 + .../e2e/assets/api7-dashboard.crt | 18 + .../e2e/assets/certs/generate-ssl.sh | 9 + .../e2e/assets/certs/test-ssl1.cer | 17 + .../e2e/assets/certs/test-ssl1.csr | 15 + .../e2e/assets/certs/test-ssl1.key | 28 + .../e2e/assets/certs/test-ssl2.cer | 17 + .../e2e/assets/certs/test-ssl2.csr | 15 + .../e2e/assets/certs/test-ssl2.key | 28 + .../e2e/assets/testdata/mixed-1-clean.json | 72 + .../e2e/assets/testdata/mixed-1.json | 307 + .../e2e/default-value.e2e-spec.ts | 75 + libs/backend-api7/e2e/ping.e2e-spec.ts | 30 + libs/backend-api7/e2e/support/global-setup.ts | 73 + .../e2e/support/global-teardown.ts | 3 + libs/backend-api7/e2e/support/utils.ts | 94 + .../e2e/sync-and-dump-1.e2e-spec.ts | 516 + .../e2e/sync-and-dump-2.e2e-spec.ts | 209 + libs/backend-api7/jest.config.e2e.ts | 19 + libs/backend-api7/jest.config.ts | 11 + libs/backend-api7/project.json | 34 + libs/backend-api7/src/fetcher.ts | 172 + libs/backend-api7/src/index.ts | 296 + libs/backend-api7/src/operator.ts | 263 + libs/backend-api7/src/transformer.ts | 214 + libs/backend-api7/src/typing.ts | 121 + libs/backend-api7/src/utils.ts | 37 + libs/backend-api7/tsconfig.json | 17 + libs/backend-api7/tsconfig.lib.json | 19 + libs/backend-api7/tsconfig.spec.json | 18 + libs/backend-apisix/.eslintrc.json | 18 + libs/backend-apisix/README.md | 52 + .../e2e/assets/apisix_conf/http.yaml | 22 + .../e2e/assets/apisix_conf/mtls.yaml | 27 + .../e2e/assets/apisix_conf/mtls/ca.cer | 17 + .../e2e/assets/apisix_conf/mtls/ca.csr | 15 + .../e2e/assets/apisix_conf/mtls/ca.key | 28 + .../e2e/assets/apisix_conf/mtls/client.cer | 17 + .../e2e/assets/apisix_conf/mtls/client.csr | 15 + .../e2e/assets/apisix_conf/mtls/client.key | 28 + .../assets/apisix_conf/mtls/generate-mtls.sh | 20 +- .../e2e/assets/apisix_conf/mtls/server.cer | 17 + .../e2e/assets/apisix_conf/mtls/server.csr | 15 + .../e2e/assets/apisix_conf/mtls/server.key | 28 + .../e2e/assets/docker-compose.yaml | 49 + libs/backend-apisix/e2e/assets/test-ssl.cer | 17 + libs/backend-apisix/e2e/assets/test-ssl.csr | 15 + libs/backend-apisix/e2e/assets/test-ssl.key | 28 + .../e2e/assets/testdata/mixed-1-clean.json | 72 + .../e2e/assets/testdata/mixed-1.json | 307 + libs/backend-apisix/e2e/ping.e2e-spec.ts | 62 + libs/backend-apisix/e2e/support/constants.ts | 2 + .../e2e/support/global-setup.ts | 3 + .../e2e/support/global-teardown.ts | 3 + libs/backend-apisix/e2e/support/utils.ts | 83 + .../e2e/sync-and-dump-1.e2e-spec.ts | 424 + libs/backend-apisix/jest.config.e2e.ts | 19 + libs/backend-apisix/jest.config.ts | 11 + libs/backend-apisix/project.json | 34 + libs/backend-apisix/src/fetcher.ts | 188 + libs/backend-apisix/src/index.ts | 86 + libs/backend-apisix/src/operator.ts | 107 + libs/backend-apisix/src/transformer.ts | 359 + libs/backend-apisix/src/typing.ts | 178 + libs/backend-apisix/src/utils.ts | 43 + libs/backend-apisix/tsconfig.json | 17 + libs/backend-apisix/tsconfig.lib.json | 11 + libs/backend-apisix/tsconfig.spec.json | 18 + libs/converter-openapi/.eslintrc.json | 18 + libs/converter-openapi/README.md | 306 + libs/converter-openapi/jest.config.ts | 12 + libs/converter-openapi/project.json | 16 + libs/converter-openapi/src/extension.ts | 24 + libs/converter-openapi/src/index.ts | 154 + libs/converter-openapi/src/parser.ts | 90 + libs/converter-openapi/src/schema.ts | 71 + .../test/assets/basic-1.yaml | 47 + .../test/assets/basic-2.yaml | 61 + .../test/assets/basic-3.yaml | 54 + .../test/assets/basic-4.yaml | 53 + .../test/assets/basic-5.yaml | 65 + .../test/assets/basic-6.yaml | 51 + .../test/assets/basic-7.yaml | 24 + .../test/assets/extension-1.yaml | 17 + .../test/assets/extension-10.yaml | 41 + .../test/assets/extension-11.yaml | 91 + .../test/assets/extension-12.yaml | 36 + .../test/assets/extension-2.yaml | 17 + .../test/assets/extension-3.yaml | 20 + .../test/assets/extension-4.yaml | 18 + .../test/assets/extension-5.yaml | 33 + .../test/assets/extension-6.yaml | 41 + .../test/assets/extension-7.yaml | 33 + .../test/assets/extension-8.yaml | 33 + .../test/assets/extension-9.yaml | 33 + libs/converter-openapi/test/basic.spec.ts | 422 + libs/converter-openapi/test/extension.spec.ts | 632 + libs/converter-openapi/test/utils.ts | 13 + libs/converter-openapi/tsconfig.json | 16 + libs/converter-openapi/tsconfig.lib.json | 22 + libs/converter-openapi/tsconfig.spec.json | 22 + libs/sdk/.eslintrc.json | 25 + libs/sdk/README.md | 11 + libs/sdk/jest.config.ts | 11 + libs/sdk/package.json | 4 + libs/sdk/project.json | 31 + libs/sdk/src/backend/index.ts | 25 + libs/sdk/src/converter/index.ts | 9 + libs/sdk/src/core/differ.ts | 36 + libs/sdk/src/core/index.ts | 248 + libs/sdk/src/core/resource.ts | 12 + libs/sdk/src/index.ts | 4 + libs/sdk/src/utils.spec.ts | 21 + libs/sdk/src/utils.ts | 45 + libs/sdk/tsconfig.json | 16 + libs/sdk/tsconfig.lib.json | 20 + libs/sdk/tsconfig.spec.json | 14 + main.go | 7 - nx.json | 55 + package.json | 71 + pkg/api/apisix/apisix.go | 72 - pkg/api/apisix/client.go | 284 - pkg/api/apisix/cluster.go | 192 - pkg/api/apisix/consumer.go | 26 - pkg/api/apisix/consumer_group.go | 26 - pkg/api/apisix/global_rule.go | 26 - pkg/api/apisix/plugin_config.go | 26 - pkg/api/apisix/plugin_metadata.go | 26 - pkg/api/apisix/resource.go | 86 - pkg/api/apisix/resource_client.go | 131 - pkg/api/apisix/route.go | 26 - pkg/api/apisix/service.go | 26 - pkg/api/apisix/ssl.go | 26 - pkg/api/apisix/stream_route.go | 39 - pkg/api/apisix/types/data/README.md | 52 - .../types/data/plugin_default_values.json | 713 -- pkg/api/apisix/types/data/plugin_schemas.lua | 226 - pkg/api/apisix/types/plugin_schema.go | 230 - pkg/api/apisix/types/plugin_schema_test.go | 412 - pkg/api/apisix/types/resource_schema.go | 173 - pkg/api/apisix/types/resource_schema_test.go | 131 - pkg/api/apisix/types/types.go | 679 - pkg/api/apisix/types/types_test.go | 37 - pkg/api/apisix/upstream.go | 26 - pkg/common/file.go | 200 - pkg/common/file_test.go | 42 - pkg/common/utils.go | 22 - pkg/config/types.go | 14 - pkg/data/events.go | 183 - pkg/data/events_test.go | 80 - pnpm-lock.yaml | 10616 ++++++++++++++++ test/cli/cli_test.go | 22 - test/cli/suites-basic/diff.go | 24 - test/cli/suites-basic/dump.go | 302 - test/cli/suites-basic/ping.go | 19 - test/cli/suites-basic/sdk.go | 132 - test/cli/suites-basic/sync.go | 281 - .../suites-basic/testdata/test-partial.yaml | 31 - .../testdata/test-with-common-labels.yaml | 38 - .../testdata/test-with-labels.yaml | 49 - test/cli/suites-basic/testdata/test.yaml | 35 - test/cli/suites-basic/testdata/test2.yaml | 19 - test/cli/suites-basic/validate.go | 19 - test/cli/suites-consumer-group/diff.go | 22 - test/cli/suites-consumer-group/dump.go | 50 - test/cli/suites-consumer-group/sdk.go | 126 - test/cli/suites-consumer-group/sync.go | 119 - .../suites-consumer-group/testdata/test.yaml | 18 - test/cli/suites-consumer-group/validate.go | 19 - test/cli/suites-consumer/diff.go | 21 - test/cli/suites-consumer/dump.go | 44 - test/cli/suites-consumer/sdk.go | 107 - test/cli/suites-consumer/sync.go | 108 - test/cli/suites-consumer/testdata/test.yaml | 14 - test/cli/suites-consumer/validate.go | 19 - test/cli/suites-global-rule/diff.go | 21 - test/cli/suites-global-rule/dump.go | 39 - test/cli/suites-global-rule/sdk.go | 125 - test/cli/suites-global-rule/sync.go | 100 - .../cli/suites-global-rule/testdata/test.yaml | 14 - test/cli/suites-global-rule/validate.go | 19 - test/cli/suites-plugin-config/diff.go | 21 - test/cli/suites-plugin-config/dump.go | 40 - test/cli/suites-plugin-config/sdk.go | 126 - test/cli/suites-plugin-config/sync.go | 101 - .../suites-plugin-config/testdata/test.yaml | 13 - test/cli/suites-plugin-config/validate.go | 19 - test/cli/suites-plugin-metadata/diff.go | 21 - test/cli/suites-plugin-metadata/dump.go | 33 - test/cli/suites-plugin-metadata/sdk.go | 119 - .../suites-plugin-metadata/testdata/test.yaml | 6 - test/cli/suites-plugin-metadata/validate.go | 20 - test/cli/suites-stream-route/diff.go | 22 - test/cli/suites-stream-route/dump.go | 48 - test/cli/suites-stream-route/sdk.go | 128 - test/cli/suites-stream-route/sync.go | 39 - .../suites-stream-route/testdata/test.yaml | 14 - test/cli/suites-stream-route/validate.go | 19 - test/cli/suites-usecase/sync-delete.go | 99 - .../testdata/sync-delete-init.yaml | 20 - .../testdata/sync-delete-updated.yaml | 20 - test/config/ci_config.go | 20 - test/mtls/certs/README.md | 25 - test/mtls/certs/ca.ca | 45 - test/mtls/certs/ca.cert | 17 - test/mtls/certs/ca.key | 28 - test/mtls/certs/client.cert | 17 - test/mtls/certs/client.key | 28 - test/mtls/certs/server.cert | 17 - test/mtls/certs/server.key | 28 - test/mtls/mtls_test.go | 15 - test/mtls/suites-basic/diff.go | 24 - test/mtls/suites-basic/dump.go | 85 - test/mtls/suites-basic/ping.go | 19 - test/mtls/suites-basic/sdk.go | 132 - test/mtls/suites-basic/sync.go | 155 - test/mtls/suites-basic/testdata/test.yaml | 35 - test/mtls/suites-basic/testdata/test2.yaml | 19 - test/mtls/suites-basic/validate.go | 19 - test/scaffold/scaffold.go | 484 - tsconfig.base.json | 39 + utils/goimports-reviser.sh | 11 - utils/quickstart-mtls.sh | 185 - utils/quickstart.sh | 184 - 316 files changed, 23499 insertions(+), 14006 deletions(-) delete mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.json delete mode 100644 .gitattributes delete mode 100644 .github/workflows/cli_test.yaml create mode 100644 .github/workflows/e2e.yaml delete mode 100644 .github/workflows/golangci-lint.yml delete mode 100644 .github/workflows/mtls_test.yaml create mode 100644 .github/workflows/unit.yaml delete mode 100644 .github/workflows/unit_test.yaml delete mode 100644 .goreleaser.yaml create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json delete mode 100644 CONTRIBUTING.md delete mode 100644 Dockerfile delete mode 100644 apisix.yaml create mode 100644 apps/cli-e2e/.eslintrc.json create mode 100644 apps/cli-e2e/README.md create mode 100644 apps/cli-e2e/jest.config.ts create mode 100644 apps/cli-e2e/project.json create mode 100644 apps/cli-e2e/src/support/global-setup.ts create mode 100644 apps/cli-e2e/src/support/global-teardown.ts create mode 100644 apps/cli-e2e/tsconfig.json create mode 100644 apps/cli-e2e/tsconfig.spec.json create mode 100644 apps/cli/.eslintrc.json create mode 100644 apps/cli/jest.config.ts create mode 100644 apps/cli/node-sea.json create mode 100644 apps/cli/project.json create mode 100644 apps/cli/scripts/download-node.ts create mode 100644 apps/cli/src/assets/.gitkeep create mode 100644 apps/cli/src/command/convert.command.ts create mode 100644 apps/cli/src/command/dev.command.ts create mode 100644 apps/cli/src/command/diff.command.ts create mode 100644 apps/cli/src/command/dump.command.ts create mode 100644 apps/cli/src/command/helper.ts create mode 100644 apps/cli/src/command/index.ts create mode 100644 apps/cli/src/command/lint.command.ts create mode 100644 apps/cli/src/command/ping.command.ts create mode 100644 apps/cli/src/command/sync.command.ts create mode 100644 apps/cli/src/command/typing.d.ts create mode 100644 apps/cli/src/command/utils.spec.ts create mode 100644 apps/cli/src/command/utils.ts create mode 100644 apps/cli/src/differ/differv3.ts create mode 100644 apps/cli/src/differ/specs/basic.spec.ts create mode 100644 apps/cli/src/differ/specs/usecase.spec.ts create mode 100644 apps/cli/src/linter/index.ts create mode 100644 apps/cli/src/linter/schema.ts create mode 100644 apps/cli/src/main.ts create mode 100644 apps/cli/src/utils/listr.ts create mode 100644 apps/cli/tsconfig.app.json create mode 100644 apps/cli/tsconfig.json create mode 100644 apps/cli/tsconfig.spec.json create mode 100644 apps/cli/webpack.config.js delete mode 100644 cmd/configure.go delete mode 100644 cmd/diff.go delete mode 100644 cmd/dump.go delete mode 100644 cmd/openapi2apisix.go delete mode 100644 cmd/ping.go delete mode 100644 cmd/root.go delete mode 100644 cmd/sync.go delete mode 100644 cmd/utils.go delete mode 100644 cmd/validate.go delete mode 100644 cmd/version.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100755 install.sh delete mode 100644 internal/pkg/db/memdb.go delete mode 100644 internal/pkg/db/memdb_test.go delete mode 100644 internal/pkg/differ/differ.go delete mode 100644 internal/pkg/differ/differ_test.go delete mode 100644 internal/pkg/openapi2apisix/openapi2apisix.go delete mode 100644 internal/pkg/openapi2apisix/openapi2apisix_test.go delete mode 100644 internal/pkg/openapi2apisix/testdata/Postman-API101.yaml delete mode 100644 internal/pkg/openapi2apisix/testdata/operationId.yaml delete mode 100644 internal/pkg/openapi2apisix/testdata/tags.json delete mode 100644 internal/pkg/openapi2apisix/testdata/tags.yaml delete mode 100755 internal/pkg/openapi2apisix/testdata/urlVariables.yaml delete mode 100644 internal/pkg/openapi2apisix/types.go delete mode 100644 internal/pkg/openapi2apisix/types_test.go delete mode 100644 internal/pkg/validator/validator.go create mode 100644 jest.config.ts create mode 100644 jest.preset.js create mode 100644 libs/backend-api7/.eslintrc.json create mode 100644 libs/backend-api7/README.md create mode 100644 libs/backend-api7/e2e/assets/api7-dashboard.crt create mode 100755 libs/backend-api7/e2e/assets/certs/generate-ssl.sh create mode 100644 libs/backend-api7/e2e/assets/certs/test-ssl1.cer create mode 100644 libs/backend-api7/e2e/assets/certs/test-ssl1.csr create mode 100644 libs/backend-api7/e2e/assets/certs/test-ssl1.key create mode 100644 libs/backend-api7/e2e/assets/certs/test-ssl2.cer create mode 100644 libs/backend-api7/e2e/assets/certs/test-ssl2.csr create mode 100644 libs/backend-api7/e2e/assets/certs/test-ssl2.key create mode 100644 libs/backend-api7/e2e/assets/testdata/mixed-1-clean.json create mode 100644 libs/backend-api7/e2e/assets/testdata/mixed-1.json create mode 100644 libs/backend-api7/e2e/default-value.e2e-spec.ts create mode 100644 libs/backend-api7/e2e/ping.e2e-spec.ts create mode 100644 libs/backend-api7/e2e/support/global-setup.ts create mode 100644 libs/backend-api7/e2e/support/global-teardown.ts create mode 100644 libs/backend-api7/e2e/support/utils.ts create mode 100644 libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts create mode 100644 libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts create mode 100644 libs/backend-api7/jest.config.e2e.ts create mode 100644 libs/backend-api7/jest.config.ts create mode 100644 libs/backend-api7/project.json create mode 100644 libs/backend-api7/src/fetcher.ts create mode 100644 libs/backend-api7/src/index.ts create mode 100644 libs/backend-api7/src/operator.ts create mode 100644 libs/backend-api7/src/transformer.ts create mode 100644 libs/backend-api7/src/typing.ts create mode 100644 libs/backend-api7/src/utils.ts create mode 100644 libs/backend-api7/tsconfig.json create mode 100644 libs/backend-api7/tsconfig.lib.json create mode 100644 libs/backend-api7/tsconfig.spec.json create mode 100644 libs/backend-apisix/.eslintrc.json create mode 100644 libs/backend-apisix/README.md create mode 100644 libs/backend-apisix/e2e/assets/apisix_conf/http.yaml create mode 100644 libs/backend-apisix/e2e/assets/apisix_conf/mtls.yaml create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.cer create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.csr create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.key create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.cer create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.csr create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.key rename test/mtls/certs/renew.sh => libs/backend-apisix/e2e/assets/apisix_conf/mtls/generate-mtls.sh (58%) create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.cer create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.csr create mode 100755 libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.key create mode 100644 libs/backend-apisix/e2e/assets/docker-compose.yaml create mode 100644 libs/backend-apisix/e2e/assets/test-ssl.cer create mode 100644 libs/backend-apisix/e2e/assets/test-ssl.csr create mode 100644 libs/backend-apisix/e2e/assets/test-ssl.key create mode 100644 libs/backend-apisix/e2e/assets/testdata/mixed-1-clean.json create mode 100644 libs/backend-apisix/e2e/assets/testdata/mixed-1.json create mode 100644 libs/backend-apisix/e2e/ping.e2e-spec.ts create mode 100644 libs/backend-apisix/e2e/support/constants.ts create mode 100644 libs/backend-apisix/e2e/support/global-setup.ts create mode 100644 libs/backend-apisix/e2e/support/global-teardown.ts create mode 100644 libs/backend-apisix/e2e/support/utils.ts create mode 100644 libs/backend-apisix/e2e/sync-and-dump-1.e2e-spec.ts create mode 100644 libs/backend-apisix/jest.config.e2e.ts create mode 100644 libs/backend-apisix/jest.config.ts create mode 100644 libs/backend-apisix/project.json create mode 100644 libs/backend-apisix/src/fetcher.ts create mode 100644 libs/backend-apisix/src/index.ts create mode 100644 libs/backend-apisix/src/operator.ts create mode 100644 libs/backend-apisix/src/transformer.ts create mode 100644 libs/backend-apisix/src/typing.ts create mode 100644 libs/backend-apisix/src/utils.ts create mode 100644 libs/backend-apisix/tsconfig.json create mode 100644 libs/backend-apisix/tsconfig.lib.json create mode 100644 libs/backend-apisix/tsconfig.spec.json create mode 100644 libs/converter-openapi/.eslintrc.json create mode 100644 libs/converter-openapi/README.md create mode 100644 libs/converter-openapi/jest.config.ts create mode 100644 libs/converter-openapi/project.json create mode 100644 libs/converter-openapi/src/extension.ts create mode 100644 libs/converter-openapi/src/index.ts create mode 100644 libs/converter-openapi/src/parser.ts create mode 100644 libs/converter-openapi/src/schema.ts create mode 100644 libs/converter-openapi/test/assets/basic-1.yaml create mode 100644 libs/converter-openapi/test/assets/basic-2.yaml create mode 100644 libs/converter-openapi/test/assets/basic-3.yaml create mode 100644 libs/converter-openapi/test/assets/basic-4.yaml create mode 100644 libs/converter-openapi/test/assets/basic-5.yaml create mode 100644 libs/converter-openapi/test/assets/basic-6.yaml create mode 100644 libs/converter-openapi/test/assets/basic-7.yaml create mode 100644 libs/converter-openapi/test/assets/extension-1.yaml create mode 100644 libs/converter-openapi/test/assets/extension-10.yaml create mode 100644 libs/converter-openapi/test/assets/extension-11.yaml create mode 100644 libs/converter-openapi/test/assets/extension-12.yaml create mode 100644 libs/converter-openapi/test/assets/extension-2.yaml create mode 100644 libs/converter-openapi/test/assets/extension-3.yaml create mode 100644 libs/converter-openapi/test/assets/extension-4.yaml create mode 100644 libs/converter-openapi/test/assets/extension-5.yaml create mode 100644 libs/converter-openapi/test/assets/extension-6.yaml create mode 100644 libs/converter-openapi/test/assets/extension-7.yaml create mode 100644 libs/converter-openapi/test/assets/extension-8.yaml create mode 100644 libs/converter-openapi/test/assets/extension-9.yaml create mode 100644 libs/converter-openapi/test/basic.spec.ts create mode 100644 libs/converter-openapi/test/extension.spec.ts create mode 100644 libs/converter-openapi/test/utils.ts create mode 100644 libs/converter-openapi/tsconfig.json create mode 100644 libs/converter-openapi/tsconfig.lib.json create mode 100644 libs/converter-openapi/tsconfig.spec.json create mode 100644 libs/sdk/.eslintrc.json create mode 100644 libs/sdk/README.md create mode 100644 libs/sdk/jest.config.ts create mode 100644 libs/sdk/package.json create mode 100644 libs/sdk/project.json create mode 100644 libs/sdk/src/backend/index.ts create mode 100644 libs/sdk/src/converter/index.ts create mode 100644 libs/sdk/src/core/differ.ts create mode 100644 libs/sdk/src/core/index.ts create mode 100644 libs/sdk/src/core/resource.ts create mode 100644 libs/sdk/src/index.ts create mode 100644 libs/sdk/src/utils.spec.ts create mode 100644 libs/sdk/src/utils.ts create mode 100644 libs/sdk/tsconfig.json create mode 100644 libs/sdk/tsconfig.lib.json create mode 100644 libs/sdk/tsconfig.spec.json delete mode 100644 main.go create mode 100644 nx.json create mode 100644 package.json delete mode 100644 pkg/api/apisix/apisix.go delete mode 100644 pkg/api/apisix/client.go delete mode 100644 pkg/api/apisix/cluster.go delete mode 100644 pkg/api/apisix/consumer.go delete mode 100644 pkg/api/apisix/consumer_group.go delete mode 100644 pkg/api/apisix/global_rule.go delete mode 100644 pkg/api/apisix/plugin_config.go delete mode 100644 pkg/api/apisix/plugin_metadata.go delete mode 100644 pkg/api/apisix/resource.go delete mode 100644 pkg/api/apisix/resource_client.go delete mode 100644 pkg/api/apisix/route.go delete mode 100644 pkg/api/apisix/service.go delete mode 100644 pkg/api/apisix/ssl.go delete mode 100644 pkg/api/apisix/stream_route.go delete mode 100644 pkg/api/apisix/types/data/README.md delete mode 100644 pkg/api/apisix/types/data/plugin_default_values.json delete mode 100644 pkg/api/apisix/types/data/plugin_schemas.lua delete mode 100644 pkg/api/apisix/types/plugin_schema.go delete mode 100644 pkg/api/apisix/types/plugin_schema_test.go delete mode 100644 pkg/api/apisix/types/resource_schema.go delete mode 100644 pkg/api/apisix/types/resource_schema_test.go delete mode 100644 pkg/api/apisix/types/types.go delete mode 100644 pkg/api/apisix/types/types_test.go delete mode 100644 pkg/api/apisix/upstream.go delete mode 100644 pkg/common/file.go delete mode 100644 pkg/common/file_test.go delete mode 100644 pkg/common/utils.go delete mode 100644 pkg/config/types.go delete mode 100644 pkg/data/events.go delete mode 100644 pkg/data/events_test.go create mode 100644 pnpm-lock.yaml delete mode 100644 test/cli/cli_test.go delete mode 100644 test/cli/suites-basic/diff.go delete mode 100644 test/cli/suites-basic/dump.go delete mode 100644 test/cli/suites-basic/ping.go delete mode 100644 test/cli/suites-basic/sdk.go delete mode 100644 test/cli/suites-basic/sync.go delete mode 100644 test/cli/suites-basic/testdata/test-partial.yaml delete mode 100644 test/cli/suites-basic/testdata/test-with-common-labels.yaml delete mode 100644 test/cli/suites-basic/testdata/test-with-labels.yaml delete mode 100644 test/cli/suites-basic/testdata/test.yaml delete mode 100644 test/cli/suites-basic/testdata/test2.yaml delete mode 100644 test/cli/suites-basic/validate.go delete mode 100644 test/cli/suites-consumer-group/diff.go delete mode 100644 test/cli/suites-consumer-group/dump.go delete mode 100644 test/cli/suites-consumer-group/sdk.go delete mode 100644 test/cli/suites-consumer-group/sync.go delete mode 100644 test/cli/suites-consumer-group/testdata/test.yaml delete mode 100644 test/cli/suites-consumer-group/validate.go delete mode 100644 test/cli/suites-consumer/diff.go delete mode 100644 test/cli/suites-consumer/dump.go delete mode 100644 test/cli/suites-consumer/sdk.go delete mode 100644 test/cli/suites-consumer/sync.go delete mode 100644 test/cli/suites-consumer/testdata/test.yaml delete mode 100644 test/cli/suites-consumer/validate.go delete mode 100644 test/cli/suites-global-rule/diff.go delete mode 100644 test/cli/suites-global-rule/dump.go delete mode 100644 test/cli/suites-global-rule/sdk.go delete mode 100644 test/cli/suites-global-rule/sync.go delete mode 100644 test/cli/suites-global-rule/testdata/test.yaml delete mode 100644 test/cli/suites-global-rule/validate.go delete mode 100644 test/cli/suites-plugin-config/diff.go delete mode 100644 test/cli/suites-plugin-config/dump.go delete mode 100644 test/cli/suites-plugin-config/sdk.go delete mode 100644 test/cli/suites-plugin-config/sync.go delete mode 100644 test/cli/suites-plugin-config/testdata/test.yaml delete mode 100644 test/cli/suites-plugin-config/validate.go delete mode 100644 test/cli/suites-plugin-metadata/diff.go delete mode 100644 test/cli/suites-plugin-metadata/dump.go delete mode 100644 test/cli/suites-plugin-metadata/sdk.go delete mode 100644 test/cli/suites-plugin-metadata/testdata/test.yaml delete mode 100644 test/cli/suites-plugin-metadata/validate.go delete mode 100644 test/cli/suites-stream-route/diff.go delete mode 100644 test/cli/suites-stream-route/dump.go delete mode 100644 test/cli/suites-stream-route/sdk.go delete mode 100644 test/cli/suites-stream-route/sync.go delete mode 100644 test/cli/suites-stream-route/testdata/test.yaml delete mode 100644 test/cli/suites-stream-route/validate.go delete mode 100644 test/cli/suites-usecase/sync-delete.go delete mode 100644 test/cli/suites-usecase/testdata/sync-delete-init.yaml delete mode 100644 test/cli/suites-usecase/testdata/sync-delete-updated.yaml delete mode 100644 test/config/ci_config.go delete mode 100644 test/mtls/certs/README.md delete mode 100644 test/mtls/certs/ca.ca delete mode 100644 test/mtls/certs/ca.cert delete mode 100644 test/mtls/certs/ca.key delete mode 100644 test/mtls/certs/client.cert delete mode 100644 test/mtls/certs/client.key delete mode 100644 test/mtls/certs/server.cert delete mode 100644 test/mtls/certs/server.key delete mode 100644 test/mtls/mtls_test.go delete mode 100644 test/mtls/suites-basic/diff.go delete mode 100644 test/mtls/suites-basic/dump.go delete mode 100644 test/mtls/suites-basic/ping.go delete mode 100644 test/mtls/suites-basic/sdk.go delete mode 100644 test/mtls/suites-basic/sync.go delete mode 100644 test/mtls/suites-basic/testdata/test.yaml delete mode 100644 test/mtls/suites-basic/testdata/test2.yaml delete mode 100644 test/mtls/suites-basic/validate.go delete mode 100644 test/scaffold/scaffold.go create mode 100644 tsconfig.base.json delete mode 100755 utils/goimports-reviser.sh delete mode 100755 utils/quickstart-mtls.sh delete mode 100644 utils/quickstart.sh diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 3aae5392..00000000 --- a/.dockerignore +++ /dev/null @@ -1,32 +0,0 @@ -# Include any files or directories that you don't want to be copied to your -# container here (e.g., local build artifacts, temporary files, etc.). -# -# For more help, visit the .dockerignore file reference guide at -# https://docs.docker.com/engine/reference/builder/#dockerignore-file - -**/.DS_Store -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/bin -**/charts -**/docker-compose* -**/compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..6e87a003 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +node_modules diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..0be733b7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,42 @@ +{ + "root": true, + "ignorePatterns": ["**/*"], + "plugins": ["@nx"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@nx/enforce-module-boundaries": [ + "error", + { + "enforceBuildableLibDependency": true, + "allow": [], + "depConstraints": [ + { + "sourceTag": "*", + "onlyDependOnLibsWithTags": ["*"] + } + ] + } + ] + } + }, + { + "files": ["*.ts", "*.tsx"], + "extends": ["plugin:@nx/typescript"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "extends": ["plugin:@nx/javascript"], + "rules": {} + }, + { + "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], + "env": { + "jest": true + }, + "rules": {} + } + ] +} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index a0717e4b..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.go text eol=lf \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bd8a3cf3..90a67fe4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,6 @@ ---- version: 2 updates: - - package-ecosystem: "gomod" + - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" @@ -10,6 +9,6 @@ updates: - package-ecosystem: "github-actions" directory: "/.github" schedule: - interval: "weekly" + interval: "weekly" labels: - "area/dependencies" diff --git a/.github/workflows/cli_test.yaml b/.github/workflows/cli_test.yaml deleted file mode 100644 index a495c502..00000000 --- a/.github/workflows/cli_test.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: Run CLI E2E Test Suites - -on: - push: - branches: - - main - - release/** - pull_request: - branches: - - main - - release/** -permissions: read-all -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true -jobs: - changes: - runs-on: ubuntu-latest - timeout-minutes: 5 - outputs: - src: ${{ steps.filter.outputs.src }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - uses: dorny/paths-filter@v2 - id: filter - with: - token: ${{ secrets.GITHUB_TOKEN }} - filters: | - src: - - '*.go' - - '**/*.go' - - 'go.mod' - - 'go.sum' - - 'Makefile' - - '.github/**' - - 'internal/**' - run-test: - needs: changes - if: | - (needs.changes.outputs.src == 'true') - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Setup Go Environment - uses: actions/setup-go@v3 - with: - go-version: '1.20.3' - - - name: Install ginkgo - run: | - go install github.com/onsi/ginkgo/v2/ginkgo - sudo cp ~/go/bin/ginkgo /usr/local/bin - - - name: Build ADC - working-directory: ./ - run: | - make build - - - name: Deploy Apache APISIX - run: | - sh utils/quickstart.sh - docker run --name httpbin --network apisix-quickstart-net --hostname httpbin -d kennethreitz/httpbin - - - name: Run E2E Test Suites - working-directory: ./ - run: | - export PATH=$PWD/bin:$PATH - make test diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 00000000..7a21adc0 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,60 @@ +name: E2E Test +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + apisix: + runs-on: ubuntu-latest + env: + BACKEND_APISIX_VERSION: 3.9.1-debian + steps: + - uses: actions/checkout@v4 + + # Setup backend environment + - name: Setup Apache APISIX + working-directory: ./libs/backend-apisix/e2e/assets + run: docker compose up -d; sleep 10 + + # Build and test ADC CLI + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - uses: pnpm/action-setup@v2 + with: + version: latest + - name: Install dependencies + run: pnpm install + + # Run E2E tests + - name: Run E2E tests + run: npx nx run backend-apisix:e2e + + api7: + runs-on: ubuntu-latest + if: contains(github.event.pull_request.labels.*.name, 'test/api7') + strategy: + matrix: + version: [3.2.13.8, 3.2.14.0] + env: + BACKEND_API7_DOWNLOAD_URL: https://run.api7.ai/api7-ee/api7-ee-v${{ matrix.version }}.tar.gz + BACKEND_API7_LICENSE: ${{ secrets.BACKEND_API7_LICENSE }} + steps: + - uses: actions/checkout@v4 + + # Build and test ADC CLI + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - uses: pnpm/action-setup@v2 + with: + version: latest + - name: Install dependencies + run: pnpm install + + # Run API7 E2E tests + - name: Run E2E tests + run: npx nx run backend-api7:e2e diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml deleted file mode 100644 index 6d0479e6..00000000 --- a/.github/workflows/golangci-lint.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: golangci-lint -on: - push: - branches: - - main - pull_request: - -permissions: - contents: read - # Optional: allow read access to pull request. Use with `only-new-issues` option. - pull-requests: read - -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 - with: - go-version: '1.21' - cache: false - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - # Require: The version of golangci-lint to use. - # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. - # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. - version: v1.54 - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # - # Note: By default, the `.golangci.yml` file should be at the root of the repository. - # The location of the configuration file can be changed by using `--config=` - # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - only-new-issues: true - - # Optional: if set to true, then all caching functionality will be completely disabled, - # takes precedence over all other caching options. - # skip-cache: true - - # Optional: if set to true, then the action won't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. - # skip-build-cache: true - - # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. - # install-mode: "goinstall" \ No newline at end of file diff --git a/.github/workflows/mtls_test.yaml b/.github/workflows/mtls_test.yaml deleted file mode 100644 index d50db108..00000000 --- a/.github/workflows/mtls_test.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: Run CLI mTLS E2E Test Suites - -on: - push: - branches: - - main - - release/** - pull_request: - branches: - - main - - release/** -permissions: read-all -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true -jobs: - changes: - runs-on: ubuntu-latest - timeout-minutes: 5 - outputs: - src: ${{ steps.filter.outputs.src }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - uses: dorny/paths-filter@v2 - id: filter - with: - token: ${{ secrets.GITHUB_TOKEN }} - filters: | - src: - - '*.go' - - '**/*.go' - - 'go.mod' - - 'go.sum' - - 'Makefile' - - '.github/**' - - 'internal/**' - run-test: - needs: changes - if: | - (needs.changes.outputs.src == 'true') - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Setup Go Environment - uses: actions/setup-go@v3 - with: - go-version: '1.20.3' - - - name: Install ginkgo - run: | - go install github.com/onsi/ginkgo/v2/ginkgo - sudo cp ~/go/bin/ginkgo /usr/local/bin - - - name: Build ADC - working-directory: ./ - run: | - make build - - - name: Deploy Apache APISIX (mTLS) - run: | - sh utils/quickstart-mtls.sh - docker run --name httpbin --network apisix-quickstart-net --hostname httpbin -d kennethreitz/httpbin - - - name: Run E2E mTLS Test Suites - working-directory: ./ - run: | - export PATH=$PWD/bin:$PATH - make test-mtls diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 637cfb14..09f20f9f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,63 +1,117 @@ -name: release - +name: Release on: push: tags: - - 'v*.*' - -permissions: - contents: write - + - 'v*.*.*' jobs: - goreleaser: + adc: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 + - uses: actions/checkout@v4 + - name: Setup release flow + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV - - name: Set up Go - uses: actions/setup-go@v4 + # Build and test ADC CLI + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - uses: pnpm/action-setup@v2 with: - go-version: '1.20' + version: latest + - name: Build ADC + run: | + pnpm install + NODE_ENV=production npx nx build cli + node --experimental-sea-config apps/cli/node-sea.json + npx ts-node apps/cli/scripts/download-node.ts + npx postject ./node-binary/linux-amd64 NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 + npx postject ./node-binary/linux-arm64 NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 + npx postject ./node-binary/win-x64.exe NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 + npx postject ./node-binary/win-arm64.exe NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 + + - name: Compress ADC + run: | + mkdir -p release + mv ./node-binary/linux-amd64 ./adc + tar -czf release/adc_${{ env.RELEASE_VERSION }}_linux_amd64.tar.gz ./adc && rm -rf ./adc + mv ./node-binary/linux-arm64 ./adc + tar -czf release/adc_${{ env.RELEASE_VERSION }}_linux_arm64.tar.gz ./adc && rm -rf ./adc + mv ./node-binary/win-x64.exe ./adc.exe + zip -r release/adc_${{ env.RELEASE_VERSION }}_windows_amd64.zip ./adc.exe && rm -rf ./adc.exe + mv ./node-binary/win-arm64.exe ./adc.exe + zip -r release/adc_${{ env.RELEASE_VERSION }}_windows_arm64.zip ./adc.exe && rm -rf ./adc.exe - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v4 + - name: Upload ADC to GitHub release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') with: - distribution: goreleaser - version: v1.20.0 - args: release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + files: | + release/adc_${{ env.RELEASE_VERSION }}_linux_amd64.tar.gz + release/adc_${{ env.RELEASE_VERSION }}_linux_arm64.tar.gz + release/adc_${{ env.RELEASE_VERSION }}_windows_amd64.zip + release/adc_${{ env.RELEASE_VERSION }}_windows_arm64.zip - - name: Upload assets - uses: actions/upload-artifact@v3 + - name: Configure AWS S3 credentials + uses: aws-actions/configure-aws-credentials@v1 with: - name: dist - path: dist/* + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-2 - docker: - runs-on: ubuntu-latest + - name: Upload ADC to AWS S3 + run: | + aws s3 sync ./release s3://api7ai-docs-resources/adc/release + + adc-macos: + runs-on: macos-14 steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Setup release flow + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV + + # Build and test ADC CLI + - uses: actions/setup-node@v4 with: - fetch-depth: 0 + node-version: 'lts/*' + - uses: pnpm/action-setup@v2 + with: + version: latest + - name: Build ADC + run: | + pnpm install + NODE_ENV=production npx nx build cli + node --experimental-sea-config apps/cli/node-sea.json + npx ts-node apps/cli/scripts/download-node.ts + codesign --remove-signature ./node-binary/darwin-arm64 + codesign --remove-signature ./node-binary/darwin-x64 + npx postject ./node-binary/darwin-arm64 NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --macho-segment-name NODE_SEA + npx postject ./node-binary/darwin-x64 NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --macho-segment-name NODE_SEA + codesign --sign - ./node-binary/darwin-arm64 + codesign --sign - ./node-binary/darwin-x64 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Compress ADC + run: | + mkdir release + mv ./node-binary/darwin-arm64 ./adc + tar -czf release/adc_${{ env.RELEASE_VERSION }}_darwin_arm64.tar.gz ./adc && rm -rf ./adc + mv ./node-binary/darwin-x64 ./adc + tar -czf release/adc_${{ env.RELEASE_VERSION }}_darwin_amd64.tar.gz ./adc && rm -rf ./adc - - name: Login to Docker Hub - uses: docker/login-action@v3 + - name: Upload ADC to GitHub release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + files: | + release/adc_${{ env.RELEASE_VERSION }}_darwin_arm64.tar.gz + release/adc_${{ env.RELEASE_VERSION }}_darwin_amd64.tar.gz - - name: Build and push - uses: docker/build-push-action@v5 + - name: Configure AWS S3 credentials + uses: aws-actions/configure-aws-credentials@v1 with: - push: true - tags: api7/adc:${{ github.ref_name }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-2 + + - name: Upload ADC to AWS S3 + run: | + aws s3 sync ./release s3://api7ai-docs-resources/adc/release diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml new file mode 100644 index 00000000..61f2b711 --- /dev/null +++ b/.github/workflows/unit.yaml @@ -0,0 +1,27 @@ +name: Unit Test +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - uses: pnpm/action-setup@v2 + with: + version: latest + - name: Install dependencies + run: pnpm install + + # Run unit tests + - name: Run CLI unit tests + run: npx nx run cli:test + - name: Run OpenAPI Converter unit tests + run: NODE_OPTIONS=--experimental-vm-modules npx nx run converter-openapi:test diff --git a/.github/workflows/unit_test.yaml b/.github/workflows/unit_test.yaml deleted file mode 100644 index 52d8f02a..00000000 --- a/.github/workflows/unit_test.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: Run Unit Test Suites - -on: - push: - branches: - - main - - release/** - pull_request: - branches: - - main - - release/** -permissions: read-all -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true -jobs: - changes: - runs-on: ubuntu-latest - timeout-minutes: 5 - outputs: - src: ${{ steps.filter.outputs.src }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - uses: dorny/paths-filter@v2 - id: filter - with: - token: ${{ secrets.GITHUB_TOKEN }} - filters: | - src: - - '*.go' - - '**/*.go' - - 'go.mod' - - 'go.sum' - - 'Makefile' - - '.github/**' - - 'internal/**' - run-test: - needs: changes - if: | - (needs.changes.outputs.src == 'true') - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Setup Go Environment - uses: actions/setup-go@v3 - with: - go-version: '1.20.3' - - name: Run Unit Test Suites - working-directory: ./ - run: | - make unit-test diff --git a/.gitignore b/.gitignore index 2300aa98..4b9a2e32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,43 @@ -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib +# See http://help.github.com/ignore-files/ for more about ignoring files. -# Test binary, built with `go test -c` -*.test +# compiled output +dist +tmp +/out-tsc -# Output of the go coverage tool, specifically when used with LiteIDE -*.out +# dependencies +node_modules -# Dependency directories (remove the comment below to include it) -# vendor/ +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace -# Go workspace file -go.work -.idea -bin +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json -dist/ +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +.nx/cache +.env +api7-ee diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index 125126c3..00000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,26 +0,0 @@ -builds: - - env: - - CGO_ENABLED=0 - goos: - - linux - - windows - - darwin - goarch: - - amd64 - - arm64 - ignore: - - goos: windows - goarch: arm64 - ldflags: - - -X github.com/api7/adc/cmd.VERSION={{ .Tag }} -X github.com/api7/adc/cmd.GitRevision={{ .Commit }} -archives: - - format: tar.gz -checksum: - name_template: 'checksums.txt' -snapshot: - name_template: "{{ .Tag }}" -changelog: - sort: asc - filters: - exclude: - - '^test:' diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..19be10eb --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +strict-peer-dependencies=false +auto-install-peers=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..d155fdbd --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +# Add files here to ignore them from prettier formatting +/dist +/coverage +/.nx/cache \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..a586b02b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,20 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "tabWidth": 2, + "semi": true, + "plugins": [ + "@trivago/prettier-plugin-sort-imports" + ], + "importOrder": [ + "^[./]" + ], + "importOrderParserPlugins": [ + "typescript", + "decorators-legacy" + ], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "endOfLine": "lf", + "printWidth": 80 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..64553b17 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "nrwl.angular-console", + "esbenp.prettier-vscode", + "firsttris.vscode-jest-runner", + "dbaeumer.vscode-eslint" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..ad92582b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index ea9fcb54..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,75 +0,0 @@ -# Contributing to ADC - -All kinds of contributions are welcome! Whether it is: - -- Reporting a bug -- Proposing a new feature -- Improving documentation -- Submitting a fix - -## Reporting Bugs and Suggesting New Features - -We use [GitHub issues](https://github.com/api7/adc/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) to track bugs and feature requests. Please make sure your requests are as detailed as possible to ensure minimum turnaround time. - -## Proposing Changes - -Pull requests are the best way to propose changes to ADC. Please follow these steps while submitting a pull request: - -1. Fork the repo and create your branch from main. -2. If you've added code that should be tested, add tests. -3. If you've changed behavior, update the [documentation](./README.md). -4. Open that pull request! -5. Ensure the test suites pass in CI. - -See [GitHub Flow](https://guides.github.com/introduction/flow/index.html). - -## Building ADC Locally - -To make changes to the code, you need to build ADC locally. - -First, you need to install [Go](https://go.dev/) in your development environment. ADC uses Go v1.20. - -Then fork and clone the repository, navigate to it in your machine and run: - -```shell -make build -``` - -## Connecting to APISIX - -ADC works with [Apache APISIX](https://docs.api7.ai/apisix). To deploy APISIX in Docker, run: - -```shell -curl -sL https://run.api7.ai/apisix/quickstart | sh -``` - -Once APISIX is deployed and ready, ADC can be configured to connect to it by running: - -```shell -./bin/adc configure -``` - -You can add the APISIX endpoint address, which is by default `http://127.0.0.1:9180`, when prompted. - -## Testing ADC - -Now to test a particular command, for example `ping`, you can run: - -```shell -./bin/adc ping -``` - -You can also run the Go code directly without building the binary: - -```shell -go run main.go ping -``` - -To run unit tests and e2e test suites: - -```shell -make unit-test -make test -``` - -ADC uses the [Cobra](https://github.com/spf13/cobra) library. diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 882a9f33..00000000 --- a/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG GO_VERSION=1.20 -ARG VERSION=dev -FROM golang:${GO_VERSION} AS build -WORKDIR /src - -RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,source=go.sum,target=go.sum \ - --mount=type=bind,source=go.mod,target=go.mod \ - go mod download -x - -RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,target=. \ - CGO_ENABLED=0 go build -o /bin/adc -ldflags "-X github.com/api7/adc/cmd.VERSION=$VERSION -X github.com/api7/adc/cmd.GitRevision=$VERSION" . - -FROM alpine:3.18 AS final - -# Copy the executable from the "build" stage. -COPY --from=build --chown=appuser:appuser /bin/adc /bin/ - -CMD /bin/adc diff --git a/Makefile b/Makefile index 1d32d296..fced8890 100644 --- a/Makefile +++ b/Makefile @@ -1,34 +1,10 @@ -#/bin/bash -PWD ?= $(shell pwd) -export PATH := $(PWD)/bin:$(PATH) - -VERSION ?= dev -GITSHA = $(shell git rev-parse --short=7 HEAD) -LDFLAGS = "-X github.com/api7/adc/cmd.VERSION=$(VERSION) -X github.com/api7/adc/cmd.GitRevision=$(GITSHA)" - -.DEFAULT_GOAL := help - -.PHONY: help +default: help help: ## Display this help @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) +.PHONY: help -.PHONY: build -build: ## Build adc - @echo "Building adc..." - @CGO_ENABLED=0 go build -o bin/adc -ldflags $(LDFLAGS) main.go - -.PHONY: test -test: ## Run cli test - @cd test/cli && ginkgo -r --nodes=1 - -.PHONY: test-mtls -test-mtls: ## Run cli test for mtls - @cd test/mtls && ginkgo -r --nodes=1 - -.PHONY: unit-test -unit-test: ## Run unit test - @go test -v $$(go list ./... | grep -v /test/) - -.PHONY: fmt -fmt: ## Format all go codes - ./utils/goimports-reviser.sh +run-api7: + @docker login hkccr.ccs.tencentyun.com -u 100033089146 -p "{e>rw2[#EDAD" ## read only account + @docker compose -f ./e2e/api7/docker-compose.yaml up -d + @./e2e/api7/init.sh +.PHONY: run-api7 diff --git a/README.md b/README.md index e73da9de..829a9cc3 100644 --- a/README.md +++ b/README.md @@ -1,118 +1,120 @@ -# APISIX Declarative CLI (ADC) +# API Declarative CLI (ADC) -ADC is a command line utility to interface with APISIX's API. +ADC is a command line utility that interfaces with API7 Enterprise and Apache APISIX Admin APIs. -It is built using the [Cobra](https://github.com/spf13/cobra) library. +## Supported Backends -## Installation +The following backend types are supported in ADC: -ADC can be installed using the `go install` command: +1. [API7 Enterprise](libs/backend-api7/README.md) +2. [Apache APISIX](libs/backend-apisix/README.md) -``` -go install github.com/api7/adc@latest -``` +## Supported Converters -This will install the `adc` binary to your `$GOPATH/bin` directory. +The following converters are supported to convert API specifications to ADC configuration: + +1. [OpenAPI Spec 3](libs/converter-openapi/README.md) + +## Installation -You can also download the appropriate binary from the [releases page](https://github.com/api7/adc/releases): +You can download the appropriate binary from the [releases page](https://github.com/api7/adc/releases): ```bash -wget https://github.com/api7/adc/releases/download/v0.1.0/adc_0.1.0_linux_amd64.tar.gz -tar -zxvf adc_0.1.0_linux_amd64.tar.gz +wget https://github.com/api7/adc/releases/download/v0.10.0/adc_0.10.0_linux_amd64.tar.gz +tar -zxvf adc_0.10.0_linux_amd64.tar.gz mv adc /usr/local/bin/adc ``` -> [!IMPORTANT] -> Make sure that these directories are in your `$PATH` environment variable. +Pre-built binaries for `amd64` and `arm64` on Linux, Windows, and macOS are available now. -## Usage +## Configure ADC -To view a list of all available commands, run: +You can configure ADC through environment variables or command line flags. Run `adc help [command]` to see the available configuration options for a command. -```shell -adc --help -``` +ADC supports dotenv, so you can store and use your environment variables in a `.env` file. The examples below show how to configure ADC for both API7 Enterprise and Apache APISIX backends. -To learn how to use a particular subcommand, for example, `ping` run: +### Example API7 Enterprise Configuration -```shell -adc ping --help +```bash +ADC_BACKEND=api7ee +ADC_SERVER=https://localhost:7443 +ADC_TOKEN= ``` -### adc configure +### Example Apache APISIX Configuration -```shell -adc configure +```bash +ADC_SERVER=http://localhost:9180 +ADC_TOKEN= ``` -Configures ADC with APISIX's server address and token. Running this command will prompt you for an APISIX server address and API token and saves them to a configuration file. +## Usage + +This section highlights some of the common ADC commands. -By default, ADC creates a configuration file at `$HOME/apisix.yaml` and this can be changed manually. +### Check Connectivity -### adc ping +The `ping` command verifies the configuration by trying to connect to the configured backend: -```shell +```bash adc ping ``` -Pings the configured APISIX instance to verify connectivity. +### Dump Configuration in ADC Format -### adc validate +The `dump` command fetches the current configuration of the backend and saves it in the ADC configuration file format: -```shell -adc validate -f apisix.yaml +```bash +adc dump -o adc.yaml ``` -Validates the provided APISIX configuration file. +### Show the Difference between Local and Remote Configuration -### adc sync +The `diff` command compares the configuration in the specified ADC configuration file with the current configuration of the backend: -```shell -adc sync +```bash +adc diff -f adc.yaml ``` -Syncs the local configuration present in the `$HOME/apisix.yaml` file (or specified configuration file) to the connected APISIX instance. +### Synchronize Local Configuration -### adc dump +The `sync` command synchronizes the configuration in the specified ADC configuration file with the backend: -```shell -adc dump --output apisix.yaml +```bash +adc sync -f adc.yaml ``` -Dumps the configuration of the connected APISIX instance to the specified configuration file. +### Convert API Specifications -### adc diff +The `convert` command converts API specifications to ADC configuration. Currently, it supports converting an OpenAPI 3 specification to ADC configuration. -```shell -adc diff +```bash +adc convert openapi -f openapi.yaml ``` -Shows the differences in configuration between the connected APISIX instance and the local configuration file. +### Verify ADC Configuration -### adc openapi2apisix +The `lint` command verifies the provided ADC configuration file locally. -```shell -adc openapi2apisix -o apisix.yaml -f openAPI.yaml +```bash +adc lint -f adc.yaml ``` -Converts the configuration in OpenAPI format (`openAPI.yaml`) to APISIX configuration (`apisix.yaml`). +## Development -### adc version +To build ADC from source, [install Nx](https://nx.dev/getting-started/installation) and run: -```shell -adc version +```bash +pnpm install +nx build cli ``` -Prints the version of ADC. See [Installation](#installation) for details on installing the latest version. - -### adc completion +To use the binary, run: -```shell -adc completion +```bash +node dist/apps/cli/main.js -h ``` -Generates autocompletion scripts for the specified shell. - ## License This project is licensed under the [Apache 2.0 License](LICENSE). diff --git a/apisix.yaml b/apisix.yaml deleted file mode 100644 index 4cf0241b..00000000 --- a/apisix.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: "Example configuration" -version: "1.0.0" -services: - - name: httpbin-service - hosts: - - api7.ai - upstream: - name: httpbin - nodes: - - host: httpbin.org - port: 80 - weight: 1 -routes: - - name: httpbin-route-anything - service_id: httpbin-service - uri: "/anything" - methods: - - GET - - name: httpbin-route-ip - service_id: httpbin-service - uri: "/ip" - methods: - - GET diff --git a/apps/cli-e2e/.eslintrc.json b/apps/cli-e2e/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/apps/cli-e2e/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/cli-e2e/README.md b/apps/cli-e2e/README.md new file mode 100644 index 00000000..b66ae09c --- /dev/null +++ b/apps/cli-e2e/README.md @@ -0,0 +1,5 @@ +# E2E Test for CLI + +**Currently we don't do E2E testing on the cli, but rather unit testing and E2E testing in each module separately.** + +**So this package is used as a placeholder in the project.** diff --git a/apps/cli-e2e/jest.config.ts b/apps/cli-e2e/jest.config.ts new file mode 100644 index 00000000..c7474ce8 --- /dev/null +++ b/apps/cli-e2e/jest.config.ts @@ -0,0 +1,18 @@ +/* eslint-disable */ +export default { + displayName: 'cli-e2e', + preset: '../../jest.preset.js', + globalSetup: '/src/support/global-setup.ts', + globalTeardown: '/src/support/global-teardown.ts', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/cli-e2e', +}; diff --git a/apps/cli-e2e/project.json b/apps/cli-e2e/project.json new file mode 100644 index 00000000..f99b2058 --- /dev/null +++ b/apps/cli-e2e/project.json @@ -0,0 +1,26 @@ +{ + "name": "cli-e2e", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "implicitDependencies": [ + "cli" + ], + "projectType": "application", + "targets": { + "e2e": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{e2eProjectRoot}" + ], + "options": { + "jestConfig": "apps/cli-e2e/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ] + } + }, +} \ No newline at end of file diff --git a/apps/cli-e2e/src/support/global-setup.ts b/apps/cli-e2e/src/support/global-setup.ts new file mode 100644 index 00000000..bab0a073 --- /dev/null +++ b/apps/cli-e2e/src/support/global-setup.ts @@ -0,0 +1,3 @@ +export default () => { + /* */ +}; diff --git a/apps/cli-e2e/src/support/global-teardown.ts b/apps/cli-e2e/src/support/global-teardown.ts new file mode 100644 index 00000000..bab0a073 --- /dev/null +++ b/apps/cli-e2e/src/support/global-teardown.ts @@ -0,0 +1,3 @@ +export default () => { + /* */ +}; diff --git a/apps/cli-e2e/tsconfig.json b/apps/cli-e2e/tsconfig.json new file mode 100644 index 00000000..ed633e1d --- /dev/null +++ b/apps/cli-e2e/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/apps/cli-e2e/tsconfig.spec.json b/apps/cli-e2e/tsconfig.spec.json new file mode 100644 index 00000000..d7f9cf20 --- /dev/null +++ b/apps/cli-e2e/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.ts"] +} diff --git a/apps/cli/.eslintrc.json b/apps/cli/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/apps/cli/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/cli/jest.config.ts b/apps/cli/jest.config.ts new file mode 100644 index 00000000..334b2644 --- /dev/null +++ b/apps/cli/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'cli', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/cli', +}; diff --git a/apps/cli/node-sea.json b/apps/cli/node-sea.json new file mode 100644 index 00000000..15c2535e --- /dev/null +++ b/apps/cli/node-sea.json @@ -0,0 +1,5 @@ +{ + "main": "dist/apps/cli/main.js", + "output": "sea-prep.blob", + "disableExperimentalSEAWarning": true +} diff --git a/apps/cli/project.json b/apps/cli/project.json new file mode 100644 index 00000000..2ad524cd --- /dev/null +++ b/apps/cli/project.json @@ -0,0 +1,53 @@ +{ + "name": "cli", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/cli/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nx/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "target": "node", + "compiler": "tsc", + "outputPath": "dist/apps/cli", + "main": "apps/cli/src/main.ts", + "tsConfig": "apps/cli/tsconfig.app.json", + "assets": ["apps/cli/src/assets"], + "webpackConfig": "apps/cli/webpack.config.js" + }, + "configurations": { + "development": {}, + "production": {} + } + }, + "serve": { + "executor": "@nx/js:node", + "defaultConfiguration": "development", + "options": { + "buildTarget": "cli:build" + }, + "configurations": { + "development": { + "buildTarget": "cli:build:development" + }, + "production": { + "buildTarget": "cli:build:production" + } + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/cli/jest.config.ts" + } + } + }, + "tags": [] +} diff --git a/apps/cli/scripts/download-node.ts b/apps/cli/scripts/download-node.ts new file mode 100644 index 00000000..1c654193 --- /dev/null +++ b/apps/cli/scripts/download-node.ts @@ -0,0 +1,141 @@ +import { execSync } from 'child_process'; +import { cpSync, existsSync, mkdirSync, rmSync, unlinkSync } from 'fs'; +import { Listr } from 'listr2'; + +const version = process.env.NODE_VERSION ?? '20.12.2'; + +const tasks = new Listr([ + { + title: 'Download NodeJS multi-arch binary', + task: (_, task): Listr => + task.newListr([ + { + title: 'Download Linux AMD64', + task: async (): Promise => { + const file = 'linux-x64.tar.gz'; + if (!existsSync(`./${file}`)) + execSync( + `wget -q -O ${file} https://nodejs.org/dist/v${version}/node-v${version}-linux-x64.tar.gz`, + ); + }, + }, + { + title: 'Download Linux ARM64', + task: async (): Promise => { + const file = 'linux-arm64.tar.gz'; + if (!existsSync(`./${file}`)) + execSync( + `wget -q -O ${file} https://nodejs.org/dist/v${version}/node-v${version}-linux-arm64.tar.gz`, + ); + }, + }, + { + title: 'Download Windows AMD64', + task: async (): Promise => { + const file = 'win-x64.zip'; + if (!existsSync(`./${file}`)) + execSync( + `wget -q -O ${file} https://nodejs.org/dist/v${version}/node-v${version}-win-x64.zip`, + ); + }, + }, + { + title: 'Download Windows ARM64', + task: async (): Promise => { + const file = 'win-arm64.zip'; + if (!existsSync(`./${file}`)) + execSync( + `wget -q -O ${file} https://nodejs.org/dist/v${version}/node-v${version}-win-arm64.zip`, + ); + }, + }, + { + title: 'Download macOS ARM64', + task: async (): Promise => { + const file = 'darwin-arm64.tar.gz'; + if (!existsSync(`./${file}`)) + execSync( + `wget -q -O ${file} https://nodejs.org/dist/v${version}/node-v${version}-darwin-arm64.tar.gz`, + ); + }, + }, + { + title: 'Download macOS AMD64', + task: async (): Promise => { + const file = 'darwin-x64.tar.gz'; + if (!existsSync(`./${file}`)) + execSync( + `wget -q -O ${file} https://nodejs.org/dist/v${version}/node-v${version}-darwin-x64.tar.gz`, + ); + }, + }, + ]), + }, + { + title: 'Extract NodeJS multi-arch binary', + task: () => { + execSync('tar -xvzf linux-x64.tar.gz'); + execSync('tar -xvzf linux-arm64.tar.gz'); + execSync('unzip win-x64.zip'); + execSync('unzip win-arm64.zip'); + execSync('tar -xvzf darwin-arm64.tar.gz'); + execSync('tar -xvzf darwin-x64.tar.gz'); + }, + }, + { + title: 'Copy all NodeJS binary', + task: () => { + mkdirSync('./node-binary'); + cpSync( + `./node-v${version}-linux-x64/bin/node`, + './node-binary/linux-amd64', + ); + cpSync( + `./node-v${version}-linux-arm64/bin/node`, + './node-binary/linux-arm64', + ); + cpSync( + `./node-v${version}-win-x64/node.exe`, + './node-binary/win-x64.exe', + ); + cpSync( + `./node-v${version}-win-arm64/node.exe`, + './node-binary/win-arm64.exe', + ); + cpSync( + `./node-v${version}-darwin-arm64/bin/node`, + './node-binary/darwin-arm64', + ); + cpSync( + `./node-v${version}-darwin-x64/bin/node`, + './node-binary/darwin-x64', + ); + }, + }, + { + title: 'Clean temporary files', + task: () => { + const opts = { recursive: true, force: true }; + rmSync(`./node-v${version}-linux-x64`, opts); + rmSync(`./node-v${version}-linux-arm64`, opts); + rmSync(`./node-v${version}-win-x64`, opts); + rmSync(`./node-v${version}-win-arm64`, opts); + rmSync(`./node-v${version}-darwin-arm64`, opts); + rmSync(`./node-v${version}-darwin-x64`, opts); + unlinkSync('./linux-x64.tar.gz'); + unlinkSync('./linux-arm64.tar.gz'); + unlinkSync('./win-x64.zip'); + unlinkSync('./win-arm64.zip'); + unlinkSync('./darwin-arm64.tar.gz'); + unlinkSync('./darwin-x64.tar.gz'); + }, + }, +]); + +(async function () { + try { + await tasks.run(); + } catch (e) { + console.error(e); + } +})(); diff --git a/apps/cli/src/assets/.gitkeep b/apps/cli/src/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apps/cli/src/command/convert.command.ts b/apps/cli/src/command/convert.command.ts new file mode 100644 index 00000000..65482cca --- /dev/null +++ b/apps/cli/src/command/convert.command.ts @@ -0,0 +1,96 @@ +import { OpenAPIConverter } from '@api7/adc-converter-openapi'; +import OpenAPIParser from '@readme/openapi-parser'; +import { Listr } from 'listr2'; +import { existsSync, writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { OpenAPIV3 } from 'openapi-types'; +import { stringify } from 'yaml'; + +import { SignaleRenderer } from '../utils/listr'; +import { TaskContext } from './diff.command'; +import { BaseCommand } from './helper'; + +interface ConvertOptions { + file: string; + output: string; + verbose: number; +} + +class BaseConvertCommand extends BaseCommand { + constructor(name: string) { + super(name); + this.option( + '-f, --file ', + 'OpenAPI configuration file path', + ).option('-o, --output ', 'output file path'); + } +} + +const OpenAPICommand = new BaseConvertCommand('openapi') + .description('Converting OpenAPI to ADC Configuration') + .action(async () => { + const opts = OpenAPICommand.optsWithGlobals(); + + const tasks = new Listr< + TaskContext & { oas?: OpenAPIV3.Document }, + typeof SignaleRenderer + >( + [ + { + title: 'Load OpenAPI document', + task: async (ctx) => { + const filePath = opts.file; + + // check existance + if (!existsSync(filePath)) { + const error = new Error( + `File "${resolve(filePath)}" does not exist`, + ); + error.stack = ''; + throw error; + } + + try { + ctx.oas = (await OpenAPIParser.dereference( + filePath, + )) as OpenAPIV3.Document; + } catch (error) { + error.message = error.message.replace('\n', ''); + error.stack = ''; + throw error; + } + }, + }, + { + title: 'Convert OpenAPI document', + task: (ctx) => new OpenAPIConverter().toADC(ctx.oas), + }, + { + title: 'Write converted OpenAPI file', + task: (ctx, task) => { + const yamlStr = stringify(ctx.local, {}); + if (!opts.output) opts.output = './adc.yaml'; + writeFileSync(opts.output, yamlStr); + task.title = `Converted OpenAPI file to "${resolve( + opts.output, + )}" successfully`; + }, + }, + ], + { + renderer: SignaleRenderer, + rendererOptions: { verbose: opts.verbose }, + ctx: { local: {} }, + }, + ); + + try { + await tasks.run(); + } catch (err) { + /* ignore */ + } + }); + +export const ConvertCommand = new BaseCommand('convert') + .description('Convert other API spec to ADC configurations') + .addCommand(OpenAPICommand); diff --git a/apps/cli/src/command/dev.command.ts b/apps/cli/src/command/dev.command.ts new file mode 100644 index 00000000..3665b9aa --- /dev/null +++ b/apps/cli/src/command/dev.command.ts @@ -0,0 +1,11 @@ +import { Command } from 'commander'; + +import { BackendCommand } from './helper'; +import { BackendOptions } from './typing'; + +export const DevCommand = new BackendCommand( + 'dev', + 'Only for dev', +).handle(async (opts) => { + console.log(opts); +}); diff --git a/apps/cli/src/command/diff.command.ts b/apps/cli/src/command/diff.command.ts new file mode 100644 index 00000000..98c97ff4 --- /dev/null +++ b/apps/cli/src/command/diff.command.ts @@ -0,0 +1,199 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Listr, ListrTask } from 'listr2'; +import { existsSync } from 'node:fs'; +import { readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import YAML from 'yaml'; + +import { DifferV3 } from '../differ/differv3'; +import { SignaleRenderer } from '../utils/listr'; +import { LoadRemoteConfigurationTask } from './dump.command'; +import { BackendCommand } from './helper'; +import { BackendOptions } from './typing'; +import { + fillLabels, + filterResourceType, + loadBackend, + mergeKVConfigurations, + toConfiguration, + toKVConfiguration, +} from './utils'; + +type DiffOptions = BackendOptions & { + file: Array; +}; + +export interface TaskContext { + local?: ADCSDK.Configuration; + remote?: ADCSDK.Configuration; + diff?: ADCSDK.Event[]; + defaultValue?: ADCSDK.DefaultValue; +} + +export const LoadLocalConfigurationTask = ( + files: Array, + labelSelector?: BackendOptions['labelSelector'], + includeResourceType?: Array, + excludeResourceType?: Array, +): ListrTask<{ + local: ADCSDK.Configuration; +}> => ({ + title: `Load local configuration`, + task: async (ctx, task) => { + if (!files || files.length <= 0) { + task.output = + 'No configuration file input\nPlease specify the declarative configuration file to use with -f or --file'; + throw new Error(); + } + + interface LoadLocalContext { + configurations: Record; + } + const subCtx: LoadLocalContext = { + configurations: {}, + }; + return task.newListr( + [ + // load yaml files + ...files.map((filePath): ListrTask => { + return { + title: `Load ${path.resolve(filePath)}`, + task: async (subCtx) => { + if (!existsSync(filePath)) + throw new Error( + `File ${path.resolve(filePath)} does not exist`, + ); + const fileContent = + (await readFile(filePath, { encoding: 'utf-8' })) ?? ''; + + subCtx.configurations[filePath] = YAML.parse(fileContent) ?? {}; + }, + }; + }), + // merge yaml files + { + title: 'Merge local configurations', + task: async (subCtx) => { + const localKVConfiguration = mergeKVConfigurations( + Object.fromEntries( + Object.entries(subCtx.configurations).map( + ([filePath, configuration]) => [ + filePath, + toKVConfiguration(configuration, filePath), + ], + ), + ), + ); + ctx.local = toConfiguration(localKVConfiguration); + }, + }, + { + title: 'Filter configuration resource type', + enabled: () => + includeResourceType?.length > 0 || excludeResourceType?.length > 0, + task: () => { + ctx.local = filterResourceType( + ctx.local, + includeResourceType, + excludeResourceType, + ); + }, + }, + { + title: 'Filter configuration', + enabled: !!labelSelector && Object.keys(labelSelector).length > 0, + task: async () => { + // Merge label selectors from CLI inputs to each resource + fillLabels(ctx.local, labelSelector); + }, + }, + ], + { + ctx: subCtx, + }, + ); + }, +}); + +export const DiffResourceTask = ( + printSummary = false, + persistentSummary = false, +): ListrTask => ({ + title: 'Diff configuration', + task: async (ctx, task) => { + ctx.diff = DifferV3.diff(ctx.local, ctx.remote, ctx.defaultValue); + + if (printSummary) { + task.output = ''; + let [created, updated, deleted] = [0, 0, 0]; + ctx.diff.forEach((event) => { + switch (event.type) { + case ADCSDK.EventType.CREATE: + task.output += `create ${event.resourceType}: "${event.resourceName}"\n`; + created++; + break; + case ADCSDK.EventType.DELETE: + task.output += `delete ${event.resourceType}: "${event.resourceName}"\n`; + deleted++; + break; + case ADCSDK.EventType.UPDATE: + task.output += `update ${event.resourceType}: "${event.resourceName}"\n`; + updated++; + break; + } + }); + task.output += `Summary: ${created} will be created, ${updated} will be updated, ${deleted} will be deleted`; + } + }, +}); + +export const DiffCommand = new BackendCommand( + 'diff', + 'Show the difference between local and backend configurations', +) + .option( + '-f, --file ', + 'The files you want to synchronize, can be set more than one.', + (filePath, files: Array = []) => files.concat(filePath), + ) + .addExample('adc diff -f service-a.yaml -f service-b.yaml') + .handle(async (opts) => { + const backend = loadBackend(opts.backend, opts); + + const tasks = new Listr( + [ + LoadLocalConfigurationTask( + opts.file, + opts.labelSelector, + opts.includeResourceType, + opts.excludeResourceType, + ), + LoadRemoteConfigurationTask({ + backend, + labelSelector: opts.labelSelector, + includeResourceType: opts.includeResourceType, + excludeResourceType: opts.excludeResourceType, + }), + DiffResourceTask(true, true), + { + title: 'Write detail diff result to file', + task: async (ctx, task) => { + await writeFile('./diff.yaml', YAML.stringify(ctx.diff), {}); + task.output = 'Detail diff result has been wrote to diff.yaml'; + }, + }, + ], + { + renderer: SignaleRenderer, + rendererOptions: { verbose: opts.verbose }, + ctx: { remote: {}, local: {}, diff: [], defaultValue: {} }, + }, + ); + + try { + await tasks.run(); + } catch (err) { + //console.log(chalk.red(`Failed to diff resources, ${err}`)); + process.exit(1); + } + }); diff --git a/apps/cli/src/command/dump.command.ts b/apps/cli/src/command/dump.command.ts new file mode 100644 index 00000000..2d997342 --- /dev/null +++ b/apps/cli/src/command/dump.command.ts @@ -0,0 +1,112 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Listr, ListrTask } from 'listr2'; +import { writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { stringify } from 'yaml'; + +import { SignaleRenderer } from '../utils/listr'; +import { TaskContext } from './diff.command'; +import { BackendCommand } from './helper'; +import type { BackendOptions } from './typing'; +import { + filterConfiguration, + filterResourceType, + loadBackend, + recursiveRemoveMetadataField, +} from './utils'; + +type DumpOptions = BackendOptions & { + output: string; +}; + +export interface LoadRemoteConfigurationTaskOptions { + backend: ADCSDK.Backend; + labelSelector?: BackendOptions['labelSelector']; + includeResourceType?: Array; + excludeResourceType?: Array; +} +export const LoadRemoteConfigurationTask = ({ + backend, + labelSelector, + includeResourceType, + excludeResourceType, +}: LoadRemoteConfigurationTaskOptions): ListrTask => ({ + title: 'Load remote configuration', + task: async (ctx, task) => { + return task.newListr([ + { + title: 'Fetch all configuration', + task: async () => await backend.dump(), + }, + { + title: 'Filter configuration resource type', + enabled: () => + includeResourceType?.length > 0 || excludeResourceType?.length > 0, + task: () => { + ctx.remote = filterResourceType( + ctx.remote, + includeResourceType, + excludeResourceType, + ); + }, + }, + { + title: 'Filter remote configuration', + enabled: !!labelSelector, + task: (ctx) => { + [ctx.remote] = filterConfiguration(ctx.remote, labelSelector); + }, + }, + ]); + }, +}); + +export const DumpCommand = new BackendCommand( + 'dump', + 'Dump configurations from the backend', +) + .option( + '-o, --output ', + 'Specify the file path where data is dumped from the backend', + 'adc.yaml', + ) + .addExample('adc dump') + .addExample('adc dump -o other-name.yaml') + .handle(async (opts) => { + const backend = loadBackend(opts.backend, opts); + const tasks = new Listr( + [ + LoadRemoteConfigurationTask({ + backend, + labelSelector: opts.labelSelector, + includeResourceType: opts.includeResourceType, + excludeResourceType: opts.excludeResourceType, + }), + { + // Remove output resource metadata fields + task: (ctx) => recursiveRemoveMetadataField(ctx.remote), + }, + { + title: 'Write to dump file', + task: async (ctx, task) => { + await writeFile(opts.output, stringify(ctx.remote), {}); + task.output = `Dump backend configuration to ${path.resolve( + opts.output, + )} successfully!`; + }, + }, + ], + { + renderer: SignaleRenderer, + rendererOptions: { verbose: opts.verbose }, + ctx: { remote: {}, local: {}, diff: [], defaultValue: {} }, + }, + ); + + try { + await tasks.run(); + } catch (err) { + //console.log(chalk.red(`Failed to dump backend configuration from backend, ${err}`)); + process.exit(1); + } + }); diff --git a/apps/cli/src/command/helper.ts b/apps/cli/src/command/helper.ts new file mode 100644 index 00000000..3b1f8c12 --- /dev/null +++ b/apps/cli/src/command/helper.ts @@ -0,0 +1,205 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import chalk from 'chalk'; +import { Command, InvalidArgumentError, Option } from 'commander'; +import { has } from 'lodash'; +import { existsSync } from 'node:fs'; +import { resolve } from 'node:path'; +import parseDuration from 'parse-duration'; +import qs from 'qs'; + +export class BaseCommand extends Command { + private exmaples: Array = []; + + constructor(name: string, description?: string) { + super(name); + + if (description) this.description(description); + + // Add global flag - verbose + this.addOption( + new Option( + '--verbose ', + 'Override verbose logging levels, it supports 0: no logs, 1: basic logs, 2: debug logs', + ) + .argParser((val) => { + const int = parseInt(val); + if (!Number.isInteger(int)) return 1; + if (int >= 0 && int <= 2) return int; + return int < 0 ? 0 : 2; + }) + .default(1), + ); + } + + public addExample(exmaple: string) { + if (this.exmaples.length === 0) + this.addHelpText('after', () => { + return `\nExample:\n\n${this.exmaples + .map((example) => ` $ ${example}`) + .join('\n')}\n`; + }); + + this.exmaples.push(exmaple); + return this; + } +} + +export class BackendCommand extends BaseCommand { + constructor(name: string, description?: string) { + super(name, description); + + this.addBackendOptions(); + } + + public handle(cb: (opts: OPTS, command: Command) => void | Promise) { + this.action(async (_, command: Command) => { + const opts = command.opts(); + + if ( + (has(opts, 'tlsClientCertFile') && !has(opts, 'tlsClientKeyFile')) || + (has(opts, 'tlsClientKeyFile') && !has(opts, 'tlsClientCertFile')) + ) { + console.log( + chalk.red( + 'TLS client certificate and key must be provided at the same time', + ), + ); + return; + } + + await cb(opts, command); + }); + return this; + } + + private addBackendOptions() { + const processCertificateFile = (value: string, err: string) => { + const path = resolve(value); + if (!existsSync(path)) throw new Error(err); + return path; + }; + + const parseResourceTypeFilter = ( + cv: ADCSDK.ResourceType, + pv: Array = [], + ) => { + const resourceTypes = Object.values(ADCSDK.ResourceType); + if (!resourceTypes.includes(cv)) + throw new InvalidArgumentError( + `Allowed choices are ${resourceTypes.join(', ')}.`, + ); + return pv.concat(cv); + }; + + this.addOption( + new Option('--backend ', 'Type of backend to connect') + .env('ADC_BACKEND') + .choices(['apisix', 'api7ee']) + .default('apisix'), + ) + .addOption( + new Option( + '--server ', + 'HTTP address of backend. This value can also be set using the environment variable ADC_SERVER environment variable', + ) + .env('ADC_SERVER') + .default('http://localhost:9180'), + ) + .addOption( + new Option('--token ', 'Token used to access the backend').env( + 'ADC_TOKEN', + ), + ) + .addOption( + new Option( + '--gateway-group ', + 'Gateway group used to specify the gateway group to operate [API7EE backend only]', + ) + .env('ADC_GATEWAY_GROUP') + .default('default'), + ) + .addOption( + new Option( + '--label-selector ', + 'Filter for resource labels (e.g., labelKey=labelValue)', + ).argParser((val, previous: Record = {}) => + Object.assign(previous, qs.parse(val, { delimiter: ',' })), + ), + ) + .addOption( + new Option( + '--include-resource-type ', + 'Filter for resource types, contains only the specified type', + ) + .conflicts('excludeResourceType') + .choices(Object.values(ADCSDK.ResourceType)) + .argParser(parseResourceTypeFilter), + ) + .addOption( + new Option( + '--exclude-resource-type ', + 'Filter for resource types, not contains only the specified type', + ) + .conflicts('includeResourceType') + .choices(Object.values(ADCSDK.ResourceType)) + .argParser(parseResourceTypeFilter), + ) + .addOption( + new Option( + '--timeout ', + 'Set a request timeout for the client to connect with Backend Admin API (in duration, e.g., 10s, 1h10m)', + ) + .default(10000, '10s') + .argParser((value) => { + return parseDuration(value) ?? 10000; + }), + ) + .addOption( + new Option( + '--ca-cert-file ', + 'Path to CA certificate for verifying the Backend Admin API', + ) + .env('ADC_CA_CERT_FILE') + .argParser((value) => + processCertificateFile( + value, + 'The specified CA certificate file does not exist', + ), + ), + ) + .addOption( + new Option( + '--tls-client-cert-file ', + 'Path to Mutual TLS client certificate for verifying the Backend Admin API', + ) + .env('ADC_TLS_CLIENT_CERT_FILE') + .argParser((value) => + processCertificateFile( + value, + 'The specified Mutual TLS client certificate file does not exist', + ), + ), + ) + .addOption( + new Option( + '--tls-client-key-file ', + 'Path to Mutual TLS client key for verifying the Backend Admin API', + ) + .env('ADC_TLS_CLIENT_KEY_FILE') + .argParser((value) => + processCertificateFile( + value, + 'The specified Mutual TLS client key file does not exist', + ), + ), + ) + .addOption( + new Option( + '--tls-skip-verify', + `Disable verification of Backend Admin API TLS certificate`, + ) + .env('ADC_TLS_SKIP_VERIFY') + .default(false), + ); + } +} diff --git a/apps/cli/src/command/index.ts b/apps/cli/src/command/index.ts new file mode 100644 index 00000000..840953e1 --- /dev/null +++ b/apps/cli/src/command/index.ts @@ -0,0 +1,39 @@ +import { Command } from 'commander'; +import dotenv from 'dotenv'; + +import { ConvertCommand } from './convert.command'; +import { DevCommand } from './dev.command'; +import { DiffCommand } from './diff.command'; +import { DumpCommand } from './dump.command'; +import { LintCommand } from './lint.command'; +import { PingCommand } from './ping.command'; +import { SyncCommand } from './sync.command'; +import { configurePluralize } from './utils'; + +export const setupCommands = (): Command => { + const program = new Command('adc'); + + program + .configureHelp({ showGlobalOptions: true }) + .passThroughOptions() + .version('0.11.1'); + + program + .addCommand(PingCommand) + .addCommand(DumpCommand) + .addCommand(DiffCommand) + .addCommand(SyncCommand) + .addCommand(ConvertCommand) + .addCommand(LintCommand); + //.addCommand(ValidateCommand) + + if (process.env.NODE_ENV === 'development') program.addCommand(DevCommand); + + // initialize dotenv + dotenv.config(); + + // initialize pluralize + configurePluralize(); + + return program; +}; diff --git a/apps/cli/src/command/lint.command.ts b/apps/cli/src/command/lint.command.ts new file mode 100644 index 00000000..5249478e --- /dev/null +++ b/apps/cli/src/command/lint.command.ts @@ -0,0 +1,72 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Listr, ListrTask } from 'listr2'; +import pluralize from 'pluralize'; +import { ZodError } from 'zod'; + +import { check } from '../linter'; +import { LoadLocalConfigurationTask } from './diff.command'; +import { BaseCommand } from './helper'; + +export const LintTask = (): ListrTask<{ local: ADCSDK.Configuration }> => ({ + title: 'Lint configuration', + task: (ctx) => { + const result = check(ctx.local); + + if (!result.success) { + let err = + 'Lint configuration\nThe following errors were found in configuration:\n'; + let pathException = false; + + if ('error' in result) { + (result.error as ZodError).errors.forEach((error, idx) => { + if (error.path.length < 2) { + // special case: not enough information is available to indicate the location of the error + pathException = true; + err += `#${idx + 1} raw error: ${JSON.stringify(error)}\n`; + return; + } + + // normal case + const resourceType = pluralize.singular(error.path[0] as string); + const resourceName = ctx.local[error.path[0]][error.path[1]].name; + err += `#${idx + 1} ${ + error.message + } at ${resourceType}: "${resourceName}", field: "${( + error.path.slice(2, error.path.length) ?? [] + ).join('.')}"\n`; + }); + } + + if (pathException) + err += + 'NOTE: There are some unsummarizable errors in the lint results that are presented as "raw error". You can report such unexpected cases.'; + + throw new Error(err); + } + }, +}); + +export const LintCommand = new BaseCommand('lint') + .description( + 'Check provided configuration files, local execution only, ensuring inputs meet ADC requirements', + ) + .option( + '-f, --file ', + 'The files you want to synchronize, can be set more than one.', + (filePath, files: Array = []) => files.concat(filePath), + ) + .addExample('adc lint -f service-a.yaml -f service-b.yaml') + .action(async () => { + const opts = LintCommand.optsWithGlobals(); + + const tasks = new Listr([ + LoadLocalConfigurationTask(opts.file, {}), + LintTask(), + ]); + + try { + await tasks.run(); + } catch (err) { + process.exit(1); + } + }); diff --git a/apps/cli/src/command/ping.command.ts b/apps/cli/src/command/ping.command.ts new file mode 100644 index 00000000..f747f3ed --- /dev/null +++ b/apps/cli/src/command/ping.command.ts @@ -0,0 +1,22 @@ +import chalk from 'chalk'; + +import { BackendCommand } from './helper'; +import type { BackendOptions } from './typing'; +import { loadBackend } from './utils'; + +type PingOptions = BackendOptions; + +export const PingCommand = new BackendCommand( + 'ping', + 'Verify connectivity with backend', +).handle(async (opts) => { + const backend = loadBackend(opts.backend, opts); + + try { + await backend.ping(); + console.log(chalk.green('Connected to backend successfully!')); + } catch (err) { + console.log(chalk.red(`Unable to connect to the backend, ${err}`)); + process.exit(1); + } +}); diff --git a/apps/cli/src/command/sync.command.ts b/apps/cli/src/command/sync.command.ts new file mode 100644 index 00000000..fe002276 --- /dev/null +++ b/apps/cli/src/command/sync.command.ts @@ -0,0 +1,64 @@ +import { Listr } from 'listr2'; + +import { SignaleRenderer } from '../utils/listr'; +import { + DiffResourceTask, + LoadLocalConfigurationTask, + TaskContext, +} from './diff.command'; +import { LoadRemoteConfigurationTask } from './dump.command'; +import { BackendCommand } from './helper'; +import { BackendOptions } from './typing'; +import { loadBackend } from './utils'; + +type SyncOption = BackendOptions & { + file: Array; +}; + +export const SyncCommand = new BackendCommand( + 'sync', + 'Sync local configurations to backend', +) + .option( + '-f, --file ', + 'The files you want to synchronize, can be set more than one.', + (filePath, files: Array = []) => files.concat(filePath), + ) + .addExample('adc sync -f service-a.yaml -f service-b.yaml') + .handle(async (opts) => { + const backend = loadBackend(opts.backend, opts); + + const tasks = new Listr( + [ + LoadLocalConfigurationTask( + opts.file, + opts.labelSelector, + opts.includeResourceType, + opts.excludeResourceType, + ), + LoadRemoteConfigurationTask({ + backend, + labelSelector: opts.labelSelector, + includeResourceType: opts.includeResourceType, + excludeResourceType: opts.excludeResourceType, + }), + DiffResourceTask(false, false), + { + title: 'Sync configuration', + task: async () => await backend.sync(), + exitOnError: true, + }, + ], + { + renderer: SignaleRenderer, + rendererOptions: { verbose: opts.verbose }, + ctx: { remote: {}, local: {}, diff: [], defaultValue: {} }, + }, + ); + + try { + await tasks.run(); + } catch (err) { + process.exit(1); + } + }); diff --git a/apps/cli/src/command/typing.d.ts b/apps/cli/src/command/typing.d.ts new file mode 100644 index 00000000..652337df --- /dev/null +++ b/apps/cli/src/command/typing.d.ts @@ -0,0 +1,29 @@ +import * as ADCSDK from '@api7/adc-sdk'; + +export interface GlobalOptions { + verbose: number; +} + +export type BackendOptions = { + backend: string; + server: string; + token: string; + gatewayGroup: string; + + labelSelector?: Record; + includeResourceType?: Array; + excludeResourceType?: Array; +} & GlobalOptions; + +export interface KVConfiguration { + routes?: Record; + services?: Record; + upstreams?: Record; + ssls?: Record; + global_rules?: Record; + plugin_configs?: Record; + plugin_metadata?: Record; + consumers?: Record; + consumer_groups?: Record; + stream_routes?: Record; +} diff --git a/apps/cli/src/command/utils.spec.ts b/apps/cli/src/command/utils.spec.ts new file mode 100644 index 00000000..5a8a172a --- /dev/null +++ b/apps/cli/src/command/utils.spec.ts @@ -0,0 +1,252 @@ +import * as ADCSDK from '@api7/adc-sdk'; + +import { fillLabels, recursiveRemoveMetadataField } from './utils'; + +describe('CLI utils', () => { + it('should fill label selector for local resources', () => { + expect( + fillLabels( + { + services: [ + { + name: 'Test Service', + routes: [ + { + name: 'Test Nested Route', + uris: ['/test-nested'], + }, + ], + stream_routes: [ + { + name: 'Test Nested Stream Route', + }, + ], + }, + ], + ssls: [ + { + snis: ['test.com'], + certificates: [], + }, + ], + consumers: [ + { + username: 'alice', + }, + ], + routes: [ + { + name: 'Test Route', + uris: ['/test'], + }, + ], + stream_routes: [ + { + name: 'Test Stream Route', + }, + ], + }, + { test1: 'test1', test2: 'test2' }, + ), + ).toEqual({ + consumers: [ + { labels: { test1: 'test1', test2: 'test2' }, username: 'alice' }, + ], + routes: [ + { + labels: { test1: 'test1', test2: 'test2' }, + name: 'Test Route', + uris: ['/test'], + }, + ], + services: [ + { + labels: { test1: 'test1', test2: 'test2' }, + name: 'Test Service', + routes: [ + { + labels: { test1: 'test1', test2: 'test2' }, + name: 'Test Nested Route', + uris: ['/test-nested'], + }, + ], + stream_routes: [ + { + labels: { test1: 'test1', test2: 'test2' }, + name: 'Test Nested Stream Route', + }, + ], + }, + ], + ssls: [ + { + certificates: [], + labels: { test1: 'test1', test2: 'test2' }, + snis: ['test.com'], + }, + ], + stream_routes: [ + { + labels: { test1: 'test1', test2: 'test2' }, + name: 'Test Stream Route', + }, + ], + }); + }); + + it('should fill label selector for local resources, append', () => { + expect( + fillLabels( + { + services: [ + { + name: 'Test Service', + labels: { test: 'test' }, + routes: [ + { + name: 'Test Nested Route', + labels: { test: 'test' }, + uris: ['/test-nested'], + }, + ], + stream_routes: [ + { + name: 'Test Nested Stream Route', + labels: { test: 'test' }, + }, + ], + }, + ], + ssls: [ + { + snis: ['test.com'], + labels: { test: 'test' }, + certificates: [], + }, + ], + consumers: [ + { + username: 'alice', + labels: { test: 'test' }, + }, + ], + routes: [ + { + name: 'Test Route', + labels: { test: 'test' }, + uris: ['/test'], + }, + ], + stream_routes: [ + { + name: 'Test Stream Route', + labels: { test: 'test' }, + }, + ], + }, + { test1: 'test1', test2: 'test2' }, + ), + ).toEqual({ + consumers: [ + { + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + username: 'alice', + }, + ], + + routes: [ + { + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + name: 'Test Route', + uris: ['/test'], + }, + ], + services: [ + { + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + name: 'Test Service', + routes: [ + { + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + name: 'Test Nested Route', + uris: ['/test-nested'], + }, + ], + stream_routes: [ + { + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + name: 'Test Nested Stream Route', + }, + ], + }, + ], + ssls: [ + { + certificates: [], + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + snis: ['test.com'], + }, + ], + stream_routes: [ + { + labels: { test: 'test', test1: 'test1', test2: 'test2' }, + name: 'Test Stream Route', + }, + ], + }); + }); + + it('should remove metadata from dump result', () => { + const config: ADCSDK.Configuration = { + services: [ + { + name: 'TestService1', + metadata: { id: 'test_service1' }, + routes: [ + { + name: 'TestRoute', + uris: ['/test'], + metadata: { id: 'test_route' }, + }, + ], + }, + { + name: 'TestService2', + metadata: { id: 'test_service2' }, + stream_routes: [ + { + name: 'TestStreamRoute', + metadata: { id: 'test_stream_route' }, + }, + ], + }, + ], + ssls: [ + { + snis: ['test'], + certificates: [], + metadata: { id: 'test_ssl' }, + }, + ], + }; + recursiveRemoveMetadataField(config); + expect(config).toEqual({ + services: [ + { + name: 'TestService1', + routes: [ + { + name: 'TestRoute', + uris: ['/test'], + }, + ], + }, + { + name: 'TestService2', + stream_routes: [{ name: 'TestStreamRoute' }], + }, + ], + ssls: [{ certificates: [], snis: ['test'] }], + }); + }); +}); diff --git a/apps/cli/src/command/utils.ts b/apps/cli/src/command/utils.ts new file mode 100644 index 00000000..7907c962 --- /dev/null +++ b/apps/cli/src/command/utils.ts @@ -0,0 +1,283 @@ +import { BackendAPI7 } from '@api7/adc-backend-api7'; +import { BackendAPISIX } from '@api7/adc-backend-apisix'; +import * as ADCSDK from '@api7/adc-sdk'; +import chalk from 'chalk'; +import path from 'node:path'; +import pluralize from 'pluralize'; + +import { KVConfiguration } from './typing'; + +export const loadBackend = ( + type: string, + opts: ADCSDK.BackendOptions, +): ADCSDK.Backend => { + switch (type) { + case 'api7ee': + return new BackendAPI7(opts); + case 'apisix': + default: + console.log(chalk.red(`Apache APISIX backend is experimental!`)); + return new BackendAPISIX(opts); + } +}; + +// convert the configuration to a key-value pairs keyed by name/username/snis +export const toKVConfiguration = ( + configuration: ADCSDK.Configuration, + filePath: string, +): KVConfiguration => + Object.fromEntries( + Object.entries(configuration).map(([resourceType, resources]) => { + if (['global_rules', 'plugin_metadata'].includes(resourceType)) { + return [resourceType, resources]; + } else if (resourceType === 'ssls') { + return [ + resourceType, + Object.fromEntries( + configuration.ssls.map((item) => [item.snis.join(','), item]), + ), + ]; + } else if (resourceType === 'consumers') { + return [ + resourceType, + Object.fromEntries( + configuration.consumers.map((item) => [item.username, item]), + ), + ]; + } else if ( + [ + 'routes', + 'services', + 'plugin_configs', + 'consumer_groups', + 'plugin_metadata', + 'stream_routes', + 'upstreams', + ].includes(resourceType) // ensure that you don't convert unexpected keys + ) { + return [ + resourceType, + Object.fromEntries(resources.map((item) => [item.name, item])), + ]; + } else { + throw new Error( + `Configuration file "${path.resolve( + filePath, + )}" contains an unknown key "${resourceType}"`, + ); + } + }), + ); + +// convert key-value pairs to a array configuration +export const toConfiguration = ( + configuration: KVConfiguration, +): ADCSDK.Configuration => + Object.fromEntries( + Object.entries(configuration).map(([resourceType, resources]) => { + if (['global_rules', 'plugin_metadata'].includes(resourceType)) + return [resourceType, resources]; + return [resourceType, Object.values(resources)]; + }), + ); + +/** + * Merge KVConfiguration and send warnings when there is the same resource name + * @param configurations Key-value pairs for file name and KVConfiguration + * @throws Will throw an error if a resource name is duplicated + */ +export const mergeKVConfigurations = ( + configurations: Record, +) => { + const configurationArr = Object.entries(configurations); // [string, KVConfiguration] + const baseConfiguration = configurationArr.shift()[1]; + configurationArr.forEach( + ([fileName, configuration]: [string, KVConfiguration]) => { + Object.entries(configuration).forEach( + ([resourceType, resources]: [string, Record]) => { + Object.keys(resources).forEach((keyName: string) => { + if (!baseConfiguration[resourceType]) + baseConfiguration[resourceType] = {}; + if (baseConfiguration[resourceType][keyName]) { + throw new Error( + `Duplicate ${pluralize.singular( + resourceType, + )} "${keyName}" was found in ${path.resolve(fileName)}`, + ); + } else { + baseConfiguration[resourceType][keyName] = resources[keyName]; + } + }); + }, + ); + }, + ); + return baseConfiguration; +}; + +export const mergeConfigurations = ( + ...fileContents: Array +) => { + const result: ADCSDK.Configuration = { + services: [], + ssls: [], + consumers: [], + global_rules: {}, + plugin_metadata: {}, + + routes: [], + stream_routes: [], + /* consumer_groups: [], + plugin_configs: [], + upstreams: [], */ + }; + + fileContents.forEach((config) => { + config.services && result.services.push(...config.services); + config.ssls && result.ssls.push(...config.ssls); + config.consumers && result.consumers.push(...config.consumers); + config.global_rules && + Object.keys(config.global_rules).forEach((globalRuleName: string) => { + result.global_rules[globalRuleName] = + config.global_rules[globalRuleName]; + }); + config.plugin_metadata && + Object.keys(config.plugin_metadata).forEach( + (pluginMetadataName: string) => { + result.plugin_metadata[pluginMetadataName] = + config.plugin_metadata[pluginMetadataName]; + }, + ); + + config.routes && result.routes.push(...config.routes); + config.stream_routes && result.stream_routes.push(...config.stream_routes); + /* config.consumer_groups && + result.consumer_groups.push(...config.consumer_groups); + config.plugin_configs && + result.plugin_configs.push(...config.plugin_configs); + config.upstreams && result.upstreams.push(...config.upstreams); */ + }); + + return result; +}; + +export const filterConfiguration = ( + configuration: ADCSDK.Configuration, + rules: Record, +): [ADCSDK.Configuration, ADCSDK.Configuration] => { + const removed: ADCSDK.Configuration = {}; + Object.keys(configuration).forEach((resourceType) => { + if (resourceType === 'plugin_metadata' || resourceType === 'global_rules') + return; + const result = labelFilter(configuration[resourceType], rules); + configuration[resourceType] = result.filtered; + removed[resourceType] = result.removed; + }); + + return [configuration, removed]; +}; + +const labelFilter = ( + resources: Array = [], + rules: Record = {}, +) => { + const filtered = resources.filter((resource) => { + return Object.entries(rules).every( + ([key, value]) => + resource?.labels && + resource?.labels[key] && + resource?.labels[key] === value, + ); + }); + + return { + filtered, + removed: resources.filter((resource) => !filtered.includes(resource)), + }; +}; + +export const fillLabels = ( + configuration: ADCSDK.Configuration, + rules: Record, +) => { + const assignSelector = (labels: object) => Object.assign(labels ?? {}, rules); + + for (const resourceType in configuration) { + if (['global_rules', 'plugin_metadata'].includes(resourceType)) continue; + + (configuration[resourceType] as Array).forEach( + (resource) => { + resource.labels = assignSelector(resource.labels as object); + + // Process the nested resources + if (resourceType === 'services') { + const sub = resource as ADCSDK.Service; + sub?.routes?.forEach((item) => { + item.labels = assignSelector(item.labels); + }); + sub?.stream_routes?.forEach((item) => { + item.labels = assignSelector(item.labels); + }); + } else if (resourceType === 'consumer_groups') { + const sub = resource as ADCSDK.ConsumerGroup; + sub?.consumers?.forEach((item) => { + item.labels = assignSelector(item.labels); + }); + } + }, + ); + } + + return configuration; +}; + +export const filterResourceType = ( + config: ADCSDK.Configuration, + includes: Array, + excludes: Array, +) => { + const key = (item) => + item !== ADCSDK.ResourceType.PLUGIN_METADATA ? item.slice(0, -1) : item; + return Object.fromEntries( + Object.entries(config).filter(([resourceType]) => { + return includes + ? includes.includes(key(resourceType)) + : !excludes.includes(key(resourceType)); + }), + ); +}; + +export const recursiveRemoveMetadataField = (c: ADCSDK.Configuration) => { + const removeMetadata = (obj: object) => { + if ('metadata' in obj) delete obj.metadata; + }; + Object.entries(c).forEach(([key, value]) => { + if (['global_rules', 'plugin_metadata', 'consumers'].includes(key)) return; + if (Array.isArray(value)) + value.forEach((item) => { + removeMetadata(item); + if (key === 'services') { + if ('routes' in item && Array.isArray(item.routes)) + item.routes.forEach((r) => removeMetadata(r)); + if ('stream_routes' in item && Array.isArray(item.stream_routes)) + item.stream_routes.forEach((r) => removeMetadata(r)); + } else if (key === 'consumer_groups') { + if ('consumers' in item && Array.isArray(item.consumers)) + item.consumers.forEach((c) => removeMetadata(c)); + } + }); + }); +}; + +export const configurePluralize = () => { + pluralize.addIrregularRule('route', 'routes'); + pluralize.addIrregularRule('service', 'services'); + pluralize.addIrregularRule('upstream', 'upstreams'); + pluralize.addIrregularRule('ssl', 'ssls'); + pluralize.addIrregularRule('global_rule', 'global_rules'); + pluralize.addIrregularRule('plugin_config', 'plugin_configs'); + pluralize.addIrregularRule('plugin_metadata', 'plugin_metadata'); + pluralize.addIrregularRule('consumer', 'consumers'); + pluralize.addIrregularRule('consumer_group', 'consumer_groups'); + pluralize.addIrregularRule('stream_route', 'stream_routes'); +}; diff --git a/apps/cli/src/differ/differv3.ts b/apps/cli/src/differ/differv3.ts new file mode 100644 index 00000000..815b23e9 --- /dev/null +++ b/apps/cli/src/differ/differv3.ts @@ -0,0 +1,668 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { randomUUID } from 'crypto'; +import { diff as objectDiff } from 'deep-diff'; +import { cloneDeep, isEmpty, isEqual, isNil, unset } from 'lodash'; +import winston from 'winston'; + +const order = { + [`${ADCSDK.ResourceType.ROUTE}.${ADCSDK.EventType.DELETE}`]: 0, + [`${ADCSDK.ResourceType.STREAM_ROUTE}.${ADCSDK.EventType.DELETE}`]: 1, + [`${ADCSDK.ResourceType.SERVICE}.${ADCSDK.EventType.DELETE}`]: 2, + [`${ADCSDK.ResourceType.UPSTREAM}.${ADCSDK.EventType.DELETE}`]: 3, + [`${ADCSDK.ResourceType.PLUGIN_CONFIG}.${ADCSDK.EventType.DELETE}`]: 6, + [`${ADCSDK.ResourceType.CONSUMER}.${ADCSDK.EventType.DELETE}`]: 4, + [`${ADCSDK.ResourceType.CONSUMER_GROUP}.${ADCSDK.EventType.DELETE}`]: 5, + + [`${ADCSDK.ResourceType.ROUTE}.${ADCSDK.EventType.UPDATE}`]: 7, + [`${ADCSDK.ResourceType.STREAM_ROUTE}.${ADCSDK.EventType.UPDATE}`]: 8, + [`${ADCSDK.ResourceType.SERVICE}.${ADCSDK.EventType.UPDATE}`]: 9, + [`${ADCSDK.ResourceType.UPSTREAM}.${ADCSDK.EventType.UPDATE}`]: 10, + [`${ADCSDK.ResourceType.PLUGIN_CONFIG}.${ADCSDK.EventType.UPDATE}`]: 11, + [`${ADCSDK.ResourceType.CONSUMER_GROUP}.${ADCSDK.EventType.UPDATE}`]: 12, + [`${ADCSDK.ResourceType.CONSUMER}.${ADCSDK.EventType.UPDATE}`]: 13, + + [`${ADCSDK.ResourceType.SSL}.${ADCSDK.EventType.CREATE}`]: 14, // SSL may be referenced by upstream mTLS, so it needs to be created in advance + [`${ADCSDK.ResourceType.UPSTREAM}.${ADCSDK.EventType.CREATE}`]: 15, + [`${ADCSDK.ResourceType.SERVICE}.${ADCSDK.EventType.CREATE}`]: 16, + [`${ADCSDK.ResourceType.PLUGIN_CONFIG}.${ADCSDK.EventType.CREATE}`]: 17, + [`${ADCSDK.ResourceType.ROUTE}.${ADCSDK.EventType.CREATE}`]: 18, + [`${ADCSDK.ResourceType.STREAM_ROUTE}.${ADCSDK.EventType.CREATE}`]: 19, + [`${ADCSDK.ResourceType.CONSUMER_GROUP}.${ADCSDK.EventType.CREATE}`]: 20, + [`${ADCSDK.ResourceType.CONSUMER}.${ADCSDK.EventType.CREATE}`]: 21, + + [`${ADCSDK.ResourceType.SSL}.${ADCSDK.EventType.DELETE}`]: 22, + [`${ADCSDK.ResourceType.SSL}.${ADCSDK.EventType.UPDATE}`]: 23, + [`${ADCSDK.ResourceType.GLOBAL_RULE}.${ADCSDK.EventType.DELETE}`]: 24, + [`${ADCSDK.ResourceType.GLOBAL_RULE}.${ADCSDK.EventType.CREATE}`]: 25, + [`${ADCSDK.ResourceType.GLOBAL_RULE}.${ADCSDK.EventType.UPDATE}`]: 26, + [`${ADCSDK.ResourceType.PLUGIN_METADATA}.${ADCSDK.EventType.DELETE}`]: 27, + [`${ADCSDK.ResourceType.PLUGIN_METADATA}.${ADCSDK.EventType.CREATE}`]: 28, + [`${ADCSDK.ResourceType.PLUGIN_METADATA}.${ADCSDK.EventType.UPDATE}`]: 29, +}; + +export class DifferV3 { + private defaultValue: ADCSDK.DefaultValue; + private transactionId: string; + + private logger = winston.createLogger({ + level: process?.env?.ADC_DIFFER_DEBUG === '1' ? 'debug' : 'info', + format: winston.format.json(), + transports: [new winston.transports.Console()], + }); + + public static diff( + local: ADCSDK.Configuration, + remote: ADCSDK.Configuration, + defaultValue?: ADCSDK.DefaultValue, + parentName?: string, + ): Array { + const differ = new DifferV3(); + differ.defaultValue = defaultValue; + differ.transactionId = randomUUID(); + + differ.logger.debug({ + message: 'enter differ', + local, + remote, + defaultValue, + transactionId: differ.transactionId, + }); + + const generateResourceName = (name: string) => + parentName ? `${parentName}.${name}` : name; + const result = [ + ...differ.diffResource( + ADCSDK.ResourceType.SERVICE, + local?.services?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + remote?.services?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.SSL, + local?.ssls?.map((res) => [ + res.snis.join(','), + ADCSDK.utils.generateId(res.snis.join(',')), + res, + ]) ?? [], + remote?.ssls?.map((res) => [ + res.snis.join(','), + ADCSDK.utils.generateId(res.snis.join(',')), + res, + ]) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.CONSUMER, + local?.consumers?.map((res) => [res.username, res.username, res]) ?? [], + remote?.consumers?.map((res) => [res.username, res.username, res]) ?? + [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.GLOBAL_RULE, + Object.entries(local?.global_rules ?? {}).map( + ([pluginName, pluginConfig]) => [ + pluginName, + pluginName, + pluginConfig, + ], + ) ?? [], + Object.entries(remote?.global_rules ?? {}).map( + ([pluginName, pluginConfig]) => [ + pluginName, + pluginName, + pluginConfig, + ], + ) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.PLUGIN_METADATA, + Object.entries(local?.plugin_metadata ?? {}).map( + ([pluginName, pluginConfig]) => [ + pluginName, + pluginName, + pluginConfig, + ], + ) ?? [], + Object.entries(remote?.plugin_metadata ?? {}).map( + ([pluginName, pluginConfig]) => [ + pluginName, + pluginName, + pluginConfig, + ], + ) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.ROUTE, + local?.routes?.map((res) => [ + res.name, + ADCSDK.utils.generateId(generateResourceName(res.name)), + res, + ]) ?? [], + remote?.routes?.map((res) => [ + res.name, + ADCSDK.utils.generateId(generateResourceName(res.name)), + res, + ]) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.STREAM_ROUTE, + local?.stream_routes?.map((res) => [ + res.name, + ADCSDK.utils.generateId(generateResourceName(res.name)), + res, + ]) ?? [], + remote?.stream_routes?.map((res) => [ + res.name, + ADCSDK.utils.generateId(generateResourceName(res.name)), + res, + ]) ?? [], + ), + /* ...differ.diffResource( + ADCSDK.ResourceType.UPSTREAM, + local?.upstreams?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + remote?.upstreams?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.CONSUMER_GROUP, + local?.consumer_groups?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + remote?.consumer_groups?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + ), + ...differ.diffResource( + ADCSDK.ResourceType.PLUGIN_CONFIG, + local?.plugin_configs?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + remote?.plugin_configs?.map((res) => [ + res.name, + ADCSDK.utils.generateId(res.name), + res, + ]) ?? [], + ), */ + ]; + + const unwrapedEvents: Array = []; + + // unwrap sub events of a main event + result.forEach((event) => { + if (event.type !== ADCSDK.EventType.ONLY_SUB_EVENTS) + unwrapedEvents.push(event); + unwrapedEvents.push(...event.subEvents); + unset(event, 'subEvents'); + }); + + // sort by resource base rules + return unwrapedEvents.sort((a, b) => { + return ( + order[`${a.resourceType}.${a.type}`] - + order[`${b.resourceType}.${b.type}`] + ); + }); + } + + private diffResource( + resourceType: ADCSDK.ResourceType.SERVICE, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Service]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Service]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.SSL, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.SSL]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.SSL]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.CONSUMER, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Consumer]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Consumer]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.GLOBAL_RULE, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.GlobalRule]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.GlobalRule]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.PLUGIN_METADATA, + local: Array< + [ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.PluginMetadata] + >, + remote: Array< + [ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.PluginMetadata] + >, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.ROUTE, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Route]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Route]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.STREAM_ROUTE, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.StreamRoute]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.StreamRoute]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.UPSTREAM, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Upstream]>, + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.Upstream]>, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.CONSUMER_GROUP, + local: Array< + [ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.ConsumerGroup] + >, + remote: Array< + [ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.ConsumerGroup] + >, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType.PLUGIN_CONFIG, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.PluginConfig]>, + remote: Array< + [ADCSDK.ResourceName, ADCSDK.ResourceId, ADCSDK.PluginConfig] + >, + ): Array; + private diffResource( + resourceType: ADCSDK.ResourceType, + local: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, T]> = [], + remote: Array<[ADCSDK.ResourceName, ADCSDK.ResourceId, T]> = [], + ): Array { + const result: Array = []; + + const localIdMap: Record = Object.fromEntries( + local.map((item) => [item[1], item[2]]), + ); + + const checkedRemoteId: Array = []; + remote.forEach(([remoteName, remoteId, remoteItem]) => { + const remoteMetadata = cloneDeep( + (remoteItem as { metadata: ADCSDK.ResourceMetadata })?.metadata, + ); + unset(remoteItem, 'metadata'); + const eventResourceId = remoteMetadata?.id ?? remoteId; + + // Asserts that the remote resource should exist locally, and that + // non-existence means that the user deleted that resource. + const localItem = localIdMap[eventResourceId]; + + // Exists remotely but not locally: resource deleted by user + if (!localItem) { + return result.push({ + resourceType, + type: ADCSDK.EventType.DELETE, + resourceId: eventResourceId, + resourceName: remoteName, + oldValue: remoteItem, + + // Special handling of resources containing nested resources + subEvents: DifferV3.diff( + {}, + resourceType === ADCSDK.ResourceType.SERVICE + ? { + routes: (remoteItem as ADCSDK.Service).routes, + stream_routes: (remoteItem as ADCSDK.Service).stream_routes, + } + : resourceType === ADCSDK.ResourceType.CONSUMER_GROUP + ? { + consumers: (remoteItem as ADCSDK.ConsumerGroup).consumers, + } + : {}, + this.defaultValue, + resourceType === ADCSDK.ResourceType.SERVICE + ? remoteName + : undefined, + ).map(this.postprocessSubEvent(remoteName, eventResourceId)), + }); + } + + // Record the remote IDs that have been checked. It will be used + // to identify locally added resources. + checkedRemoteId.push(eventResourceId); + + const originalLocalItem = cloneDeep(localItem); + + // For special handling of SSL resources, since neither APISIX nor + // API7 outputs certificate private keys in plaintext, the local + // and remote key fields should be removed, and the check item should + // be a combination of "snis + certificates", not including the key. + // It is almost impossible to have the same public key value when the + // private key is different, so the combination of SNI and certificate + // public key checks the combination to find out the update of the + // certificate in the local configuration. + if (resourceType === ADCSDK.ResourceType.SSL) { + (localItem as ADCSDK.SSL).certificates.forEach((cert) => + unset(cert, 'key'), + ); + (remoteItem as ADCSDK.SSL).certificates.forEach((cert) => + unset(cert, 'key'), + ); + } + + // Merges local resources into a table of default values for this type. + // The Admin API merges the default values specified in the schema into + // the data, so default values not contained in the local data will + // necessarily make it considered a data modification, and these + // discrepancies will trigger update API calls, potentially resulting + // in unintended behavior. + // As such, each backend implementation needs to provide its table of + // default values, which merges the local resources to the defaults, + // just as the Admin API does. + const defaultValue = this.defaultValue?.core?.[resourceType] ?? {}; + const mergedLocalItem = this.mergeDefault( + localItem, + cloneDeep(defaultValue), + ); + + // Special handling of resources containing nested resources: routes, consumer_groups + const subEvents: Array = []; + if ( + resourceType === ADCSDK.ResourceType.SERVICE || + resourceType === ADCSDK.ResourceType.CONSUMER_GROUP + ) { + subEvents.push( + ...DifferV3.diff( + resourceType === ADCSDK.ResourceType.SERVICE + ? { + routes: (localItem as ADCSDK.Service).routes, + stream_routes: (localItem as ADCSDK.Service).stream_routes, + } + : resourceType === ADCSDK.ResourceType.CONSUMER_GROUP + ? { + consumers: (localItem as ADCSDK.ConsumerGroup).consumers, + } + : {}, + resourceType === ADCSDK.ResourceType.SERVICE + ? { + routes: (remoteItem as ADCSDK.Service).routes, + stream_routes: (remoteItem as ADCSDK.Service).stream_routes, + } + : resourceType === ADCSDK.ResourceType.CONSUMER_GROUP + ? { + consumers: (remoteItem as ADCSDK.ConsumerGroup).consumers, + } + : {}, + this.defaultValue, + resourceType === ADCSDK.ResourceType.SERVICE + ? remoteName + : undefined, + ).map(this.postprocessSubEvent(remoteName, remoteId)), + ); + + this.logger.debug({ + message: 'sub resource diff', + subEvents, + + transactionId: this.transactionId, + }); + + // Remove nested resources to indeed compare the main resource itself. + (resourceType === ADCSDK.ResourceType.SERVICE + ? ['routes', 'stream_routes'] + : ['consumers'] + ).map((key) => { + unset(mergedLocalItem, key); + unset(remoteItem, key); + }); + } + + let outputLocalItem: ADCSDK.Resource = cloneDeep(originalLocalItem); + let outputRemoteItem: ADCSDK.Resource = cloneDeep(remoteItem); + + // If the resource may contain plugin configurations, perform a + // diff check on each plugin + let pluginChanged = false; + if ( + resourceType === ADCSDK.ResourceType.SERVICE || + resourceType === ADCSDK.ResourceType.CONSUMER || + resourceType === ADCSDK.ResourceType.ROUTE || + resourceType === ADCSDK.ResourceType.STREAM_ROUTE || + resourceType === ADCSDK.ResourceType.CONSUMER_GROUP || + resourceType === ADCSDK.ResourceType.PLUGIN_CONFIG + ) { + let mergedLocalPlugins: ADCSDK.Plugins = {}; + [pluginChanged, mergedLocalPlugins] = this.diffPlugins( + 'plugins' in mergedLocalItem + ? (cloneDeep(mergedLocalItem.plugins) as ADCSDK.Plugins) + : {}, + 'plugins' in remoteItem ? (remoteItem.plugins as ADCSDK.Plugins) : {}, + ); + + if (!isEmpty(mergedLocalPlugins)) + //@ts-expect-error it has been asserted that the resource type can contain the plugins field + mergedLocalItem.plugins = mergedLocalPlugins; + + // The cache is used to output the old and new values of the diff report. + // They are characterized by the fact that they do not contain subresources + // such as route; however, they should be the current resource's containing + // the plugins field. + outputLocalItem = cloneDeep(mergedLocalItem); + outputRemoteItem = cloneDeep(remoteItem); + + // Since plugins will be checked separately, differences in the plugins + // should not be considered when checking for current resource changes. + if (!pluginChanged) { + unset(mergedLocalItem, 'plugins'); + unset(remoteItem, 'plugins'); + } + } + + // Checking other resource properties, lhs is old(remote), rhs is new(local) + // The resources checked are exclusively the current resource itself, without + // plugins and sub-resources. + const diff = objectDiff(cloneDeep(remoteItem), mergedLocalItem); + this.logger.debug({ + message: 'main resource diff', + diff, + local: outputLocalItem, + remote: outputRemoteItem, + realRemote: remoteItem, + realLocal: mergedLocalItem, + originalLocal: localItem, + transactionId: this.transactionId, + }); + + // If there are changes to the plugins or changes to other properties + // of the resource, an update event is added. + if ( + pluginChanged || + (diff && diff.length !== 0) || + subEvents.length > 0 + ) { + // If there are only sub-events and no modifications to the resource + // itself, only the ONLY_SUB_EVENTS event is emitted, and it will be + // discarded during event aggregation and sorting. + const onlySubEvents = + subEvents.length > 0 && + !pluginChanged && + (!diff || diff.length === 0); + + return result.push({ + resourceType, + type: onlySubEvents + ? ADCSDK.EventType.ONLY_SUB_EVENTS + : ADCSDK.EventType.UPDATE, + resourceId: remoteId, + resourceName: remoteName, + oldValue: outputRemoteItem, + + // Merged defaults should not be included to prevent bothering the user + newValue: outputLocalItem, + + // Even if only the plugin part has changed, the difference is recorded + // on the whole resource. + diff, + + // Attach sub resources update events + subEvents, + }); + } + }); + + // Exists locally but not remotely: resource created by user + local.forEach(([localName, localId, localItem]) => { + if (checkedRemoteId.includes(localId)) return; + return result.push({ + resourceType, + type: ADCSDK.EventType.CREATE, + resourceId: localId, + resourceName: localName, + newValue: localItem, + + // Special handling of resources containing nested resources + subEvents: DifferV3.diff( + resourceType === ADCSDK.ResourceType.SERVICE + ? { + routes: (localItem as ADCSDK.Service).routes, + stream_routes: (localItem as ADCSDK.Service).stream_routes, + } + : resourceType === ADCSDK.ResourceType.CONSUMER_GROUP + ? { + consumers: (localItem as ADCSDK.ConsumerGroup).consumers, + } + : {}, + {}, + this.defaultValue, + resourceType === ADCSDK.ResourceType.SERVICE ? localName : undefined, + ).map(this.postprocessSubEvent(localName, localId)), + }); + }); + + return result; + } + + // Check for differences on multiple plugins and return if any differences. + private diffPlugins( + local: ADCSDK.Plugins, + remote: ADCSDK.Plugins, + ): [boolean, ADCSDK.Plugins] { + this.logger.debug({ + message: 'diff plugins', + local, + remote, + emptyLocal: isEmpty(local), + emptyRemote: isEmpty(remote), + transactionId: this.transactionId, + }); + if (isEmpty(local) && isEmpty(remote)) return [false, local]; + + // Pre-merge the plugin's default configuration into the local configuration + local = Object.fromEntries( + Object.entries(local).map(([pluginName, config]) => { + const defaultValue = this.defaultValue?.plugins?.[pluginName] ?? {}; + return [pluginName, this.mergeDefault(config, cloneDeep(defaultValue))]; + }), + ) as ADCSDK.Plugins; + const checker = (left: ADCSDK.Plugins, right: ADCSDK.Plugins) => { + for (const leftPluginName in left) { + const leftPlugin = left[leftPluginName]; + const rightPlugin = right[leftPluginName]; + + // A plugin may be deleted, its configuration should be undefined. + // When either plugin is configured as undefined, check if the two sides + // are equal, and if they are not, it means that the plugin has been + // added or removed. + if ( + (leftPlugin === undefined || rightPlugin === undefined) && + !isEqual(leftPlugin, rightPlugin) + ) + return true; + + const pluginDiff = objectDiff( + cloneDeep(leftPlugin), + cloneDeep(rightPlugin), + ); + if (pluginDiff && pluginDiff.length !== 0) return true; + } + return false; + }; + + // Check for changes in the local plugin vs. remote and + // remote plugin vs. local, respectively. + return [checker(local, remote) || checker(remote, local), local]; + } + + private postprocessSubEvent( + parentName: string, + parentId: string, + ): (event: ADCSDK.Event) => ADCSDK.Event { + return (event) => { + // If the Differ-generated subevent resource ID does not + // match the resource name hash, the subevent resource ID + // is maintained. + // This may be due to the fact that the remote resource's + // ID is server-generated. + const subEventResourceId = + ADCSDK.utils.generateId(event.resourceName) === event.resourceId + ? ADCSDK.utils.generateId(`${parentName}.${event.resourceName}`) + : event.resourceId; + + return { + ...event, + parentId, + resourceId: subEventResourceId, + }; + }; + } + + private mergeDefault(resource: ADCSDK.Resource, defaults: object): object { + const defaultsClone = cloneDeep(defaults); + const resourceClone = cloneDeep(resource); + const isObjectButNotArray = (val: unknown) => + typeof val === 'object' && !Array.isArray(val); + + Object.entries(defaultsClone).forEach(([key, value]) => { + // If a specific key does not exist in the resource + if (isNil(resourceClone[key])) { + // If the default value to be merged is an object and not an array, + // it should not be merged. + if (isObjectButNotArray(value) || Array.isArray(value)) return; // include any array value + resourceClone[key] = value; + } else { + // If the default value and the value in the resource are both + // objects, they need to be deeply merged, recursively it + if ( + isObjectButNotArray(value) && + isObjectButNotArray(resourceClone[key]) + ) + resourceClone[key] = this.mergeDefault(resourceClone[key], value); + + // If the default value is an array then the default value of the + // array item (index 0) is merged to each element of the array. + if ( + Array.isArray(value) && + value[0] && + Array.isArray(resourceClone[key]) + ) + resourceClone[key] = (resourceClone[key] as Array).map( + (item) => this.mergeDefault(item, value[0]), + ); + + // Otherwise, ignore the default value and do not replace the + // value in the resource + } + }); + + return resourceClone; + } +} diff --git a/apps/cli/src/differ/specs/basic.spec.ts b/apps/cli/src/differ/specs/basic.spec.ts new file mode 100644 index 00000000..4ec8dbbd --- /dev/null +++ b/apps/cli/src/differ/specs/basic.spec.ts @@ -0,0 +1,858 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { unset } from 'lodash'; + +import { DifferV3 } from '../differv3'; + +describe('Differ V3 - basic', () => { + it('should output empty when input is empty', () => { + expect(DifferV3.diff({}, {})).toEqual([]); + }); + + it('should create resource', () => { + const consumerName = 'alice'; + expect( + DifferV3.diff( + { + consumers: [ + { + username: consumerName, + plugins: {}, + }, + ], + }, + {}, + ), + ).toEqual([ + { + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.CREATE, + resourceId: consumerName, + resourceName: consumerName, + newValue: { username: consumerName, plugins: {} }, + }, + ] as Array); + }); + + it('should update resource', () => { + const consumerName = 'alice'; + const consumerKey = 'alice-key'; + expect( + DifferV3.diff( + { + consumers: [ + { + username: consumerName, + plugins: { + 'key-auth': { key: consumerKey }, + }, + }, + ], + }, + { + consumers: [ + { + username: consumerName, + plugins: {}, + }, + ], + }, + ), + ).toEqual([ + { + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.UPDATE, + resourceId: consumerName, + resourceName: consumerName, + oldValue: { username: consumerName, plugins: {} }, + newValue: { + username: consumerName, + plugins: { + 'key-auth': { key: consumerKey }, + }, + }, + diff: [ + { + kind: 'N', + rhs: { key: consumerKey }, + path: ['plugins', 'key-auth'], + }, + ], + }, + ] as Array); + }); + + it('should delete resource', () => { + const consumerName = 'alice'; + expect( + DifferV3.diff( + {}, + { + consumers: [ + { + username: consumerName, + plugins: {}, + }, + ], + }, + ), + ).toEqual([ + { + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.DELETE, + resourceId: consumerName, + resourceName: consumerName, + oldValue: { username: consumerName, plugins: {} }, + }, + ] as Array); + }); + + it('should be sorted by event type', () => { + const createConsumer = 'createConsumer'; + const updatedConsumer = 'updatedConsumer'; + const deletedConsumer = 'deletedConsumer'; + expect( + DifferV3.diff( + { + consumers: [ + { + username: createConsumer, + plugins: {}, + }, + { + username: updatedConsumer, + plugins: { + 'key-auth': {}, + }, + }, + ], + }, + { + consumers: [ + { + username: updatedConsumer, + plugins: {}, + }, + { + username: deletedConsumer, + plugins: {}, + }, + ], + }, + ), + ).toEqual([ + // DELETE > UPDATE > CREATE + { + oldValue: { plugins: {}, username: deletedConsumer }, + resourceId: deletedConsumer, + resourceName: deletedConsumer, + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.DELETE, + }, + { + diff: [{ kind: 'N', path: ['plugins', 'key-auth'], rhs: {} }], + newValue: { plugins: { 'key-auth': {} }, username: updatedConsumer }, + oldValue: { plugins: {}, username: updatedConsumer }, + resourceId: updatedConsumer, + resourceName: updatedConsumer, + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.UPDATE, + }, + { + newValue: { plugins: {}, username: createConsumer }, + resourceId: createConsumer, + resourceName: createConsumer, + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.CREATE, + }, + ]); + }); + + it('should adapt to default values added on the backend, core', () => { + const consumerName = 'alice'; + expect( + DifferV3.diff( + { + consumers: [{ username: consumerName, plugins: {} }], + }, + { + consumers: [{ username: consumerName, description: '', plugins: {} }], + }, + { + core: { + [ADCSDK.ResourceType.CONSUMER]: { description: '' }, + }, + }, + ), + ).toEqual([]); + }); + + it('should adapt to default values added on the backend, plugin', () => { + const consumerName = 'alice'; + expect( + DifferV3.diff( + { + consumers: [ + { username: consumerName, plugins: { 'key-auth': { key: 'key' } } }, + ], + }, + { + consumers: [ + { + username: consumerName, + plugins: { 'key-auth': { key: 'key', added: 'added' } }, + }, + ], + }, + { + plugins: { + 'key-auth': { added: 'added' }, + }, + }, + ), + ).toEqual([]); + }); + + it('should update resource, add plugin', () => { + const consumerName = 'alice'; + const consumerKey = 'alice-key'; + expect( + DifferV3.diff( + { + consumers: [ + { + username: consumerName, + plugins: { + 'key-auth': { key: consumerKey }, + }, + }, + ], + }, + { + consumers: [ + { + username: consumerName, + plugins: {}, + }, + ], + }, + ), + ).toEqual([ + { + diff: [ + { + kind: 'N', + path: ['plugins', 'key-auth'], + rhs: { key: consumerKey }, + }, + ], + newValue: { + plugins: { 'key-auth': { key: consumerKey } }, + username: consumerName, + }, + oldValue: { plugins: {}, username: consumerName }, + resourceId: consumerName, + resourceName: consumerName, + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.UPDATE, + }, + ] as Array); + }); + + it('should update resource, update plugin with default value', () => { + const consumerName = 'alice'; + const oldKey = 'old-key'; + const newKey = 'new-key'; + expect( + DifferV3.diff( + { + consumers: [ + { + username: consumerName, + plugins: { + 'key-auth': { key: newKey }, + }, + }, + ], + }, + { + consumers: [ + { + username: consumerName, + plugins: { + 'key-auth': { key: oldKey, added: 'added' }, + }, + }, + ], + }, + { + plugins: { + 'key-auth': { added: 'added' }, + }, + }, + ), + ).toEqual([ + { + diff: [ + { + kind: 'E', + lhs: oldKey, + path: ['plugins', 'key-auth', 'key'], + rhs: newKey, + }, + ], + newValue: { + plugins: { 'key-auth': { key: newKey, added: 'added' } }, + username: consumerName, + }, + oldValue: { + plugins: { 'key-auth': { added: 'added', key: oldKey } }, + username: consumerName, + }, + resourceId: consumerName, + resourceName: consumerName, + resourceType: ADCSDK.ResourceType.CONSUMER, + type: ADCSDK.EventType.UPDATE, + }, + ] as Array); + }); + + it('should generate hashed resource id', () => { + const sslName = 'demo-sni1,demo-sni2'; + const ssl = { + snis: ['demo-sni1', 'demo-sni2'], + certificates: [{ certificate: 'cert', key: 'key' }], + }; + expect( + DifferV3.diff( + { + ssls: [ssl], + }, + {}, + ), + ).toEqual([ + { + newValue: ssl, + resourceId: ADCSDK.utils.generateId(sslName), + resourceName: sslName, + resourceType: 'ssl', + type: 'create', + }, + ] as Array); + }); + + it('should update service nested route', () => { + const serviceName = 'Test Service'; + const routeName = 'Test Route'; + const oldService: ADCSDK.Service = { + name: serviceName, + routes: [ + { + name: routeName, + uris: ['/test'], + plugins: { + test: { + testKey: 'oldValue', + }, + }, + }, + ], + }; + + const newService = structuredClone(oldService); + newService.routes[0].plugins.test.testKey = 'newValue'; + + expect( + DifferV3.diff( + { + services: [structuredClone(newService)], + }, + { + services: [structuredClone(oldService)], + }, + ), + ).toEqual([ + { + diff: [ + { + kind: 'E', + lhs: 'oldValue', + path: ['plugins', 'test', 'testKey'], + rhs: 'newValue', + }, + ], + newValue: newService.routes[0], + oldValue: oldService.routes[0], + parentId: ADCSDK.utils.generateId(serviceName), + resourceId: ADCSDK.utils.generateId(`${serviceName}.${routeName}`), + resourceName: routeName, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.UPDATE, + }, + ]); + }); + + it('should update service and its nested route', () => { + const serviceName = 'Test Service'; + const routeName = 'Test Route'; + const oldService: ADCSDK.Service = { + name: serviceName, + plugins: { + test: { + testKey: 'serviceOldValue', + }, + }, + routes: [ + { + name: routeName, + uris: ['/test'], + plugins: { + test: { + testKey: 'oldValue', + }, + }, + }, + ], + }; + + const newService = structuredClone(oldService); + newService.path_prefix = '/test'; + newService.plugins.test.testKey = 'serviceNewValue'; + newService.routes[0].plugins.test.testKey = 'newValue'; + + expect( + DifferV3.diff( + { + services: [structuredClone(newService)], + }, + { + services: [structuredClone(oldService)], + }, + ), + ).toEqual([ + { + diff: [ + { + kind: 'E', + lhs: 'oldValue', + path: ['plugins', 'test', 'testKey'], + rhs: 'newValue', + }, + ], + newValue: newService.routes[0], + oldValue: oldService.routes[0], + parentId: ADCSDK.utils.generateId(serviceName), + resourceId: ADCSDK.utils.generateId(`${serviceName}.${routeName}`), + resourceName: routeName, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.UPDATE, + }, + { + diff: [ + { + kind: 'E', + lhs: 'serviceOldValue', + path: ['plugins', 'test', 'testKey'], + rhs: 'serviceNewValue', + }, + { + kind: 'N', + path: ['path_prefix'], + rhs: '/test', + }, + ], + newValue: { + name: serviceName, + path_prefix: '/test', + plugins: { test: { testKey: 'serviceNewValue' } }, + }, + oldValue: { + name: serviceName, + plugins: { test: { testKey: 'serviceOldValue' } }, + }, + resourceId: ADCSDK.utils.generateId(serviceName), + resourceName: serviceName, + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.UPDATE, + }, + ]); + }); + + it('should keep plugins when plugins not be changed', () => { + const serviceName = 'Test Service'; + const oldService: ADCSDK.Service = { + name: serviceName, + plugins: { + test: { + testKey: 'testValue', + added: 'added', + }, + }, + }; + + const newService = structuredClone(oldService); + newService.path_prefix = '/test'; + const origNewService = structuredClone(newService); + + // ensure local resource does not include default added field + unset(newService, 'plugins.test.added'); + + expect( + DifferV3.diff( + { + services: [structuredClone(newService)], + }, + { + services: [structuredClone(oldService)], + }, + { + plugins: { + test: { + added: 'added', + }, + }, + }, + ), + ).toEqual([ + { + diff: [ + { + kind: 'N', + path: ['path_prefix'], + rhs: '/test', + }, + ], + newValue: origNewService, + oldValue: oldService, + resourceId: ADCSDK.utils.generateId(serviceName), + resourceName: serviceName, + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.UPDATE, + }, + ]); + }); + + it('should selectively merge the objects in default values, for principle', () => { + expect( + DifferV3.diff( + { + services: [ + { + name: 'Test Service', + //@ts-expect-error just for test + test1: {}, + }, + ], + }, + { + services: [ + { + name: 'Test Service', + test: 'test', + test1: { + test2: 'test2', + }, + }, + ], + }, + { + core: { + service: { + test: 'test', + test1: { + test2: 'test2', + test3: { + test4: 'test4', + }, + }, + }, + }, + }, + ), + ).toEqual([]); + }); + + it('ensure remote metadata does not affect the differ, delete', () => { + const serviceName = 'Test Service'; + const routeName = 'Test Route'; + const route1Name = `${routeName} 1`; + const route2Name = `${routeName} 2`; + const remoteServiceId = 'not_hashed_service_name'; + const remoteRouteId = 'not_hashed_route_name'; + expect( + DifferV3.diff( + {}, + { + services: [ + { + name: serviceName, + routes: [ + { + name: route1Name, + uris: ['/test1'], + }, + { + name: route2Name, + uris: ['/test2'], + metadata: { id: remoteRouteId }, + }, + ], + metadata: { + id: remoteServiceId, + }, + }, + ], + }, + ), + ).toEqual([ + { + oldValue: { name: route1Name, uris: ['/test1'] }, + parentId: remoteServiceId, + resourceId: ADCSDK.utils.generateId(`${serviceName}.${route1Name}`), + resourceName: route1Name, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.DELETE, + }, + { + oldValue: { name: route2Name, uris: ['/test2'] }, + parentId: remoteServiceId, + resourceId: remoteRouteId, + resourceName: route2Name, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.DELETE, + }, + { + oldValue: { + name: serviceName, + routes: [ + { name: route1Name, uris: ['/test1'] }, + { name: route2Name, uris: ['/test2'] }, + ], + }, + resourceId: remoteServiceId, + resourceName: serviceName, + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.DELETE, + }, + ]); + }); + + it('ensure remote metadata does not affect the differ, update', () => { + const serviceName = 'Test Service'; + const routeName = 'Test Route'; + const route1Name = `${routeName} 1`; + const route2Name = `${routeName} 2`; + const remoteServiceId = 'not_hashed_service_name'; + const remoteRouteId = 'not_hashed_route_name'; + expect( + DifferV3.diff( + { + services: [ + { + name: serviceName, + path_prefix: '/test', + routes: [ + { + name: route1Name, + uris: ['/test1u'], + }, + { + name: route2Name, + uris: ['/test2u'], + }, + ], + }, + ], + }, + { + services: [ + { + name: serviceName, + routes: [ + { + name: route1Name, + uris: ['/test1'], + }, + { + name: route2Name, + uris: ['/test2'], + metadata: { id: remoteRouteId }, + }, + ], + metadata: { + id: remoteServiceId, + }, + }, + ], + }, + ), + ).toEqual([ + { + oldValue: { name: route1Name, uris: ['/test1'] }, + parentId: remoteServiceId, + resourceId: ADCSDK.utils.generateId(`${serviceName}.${route1Name}`), + resourceName: route1Name, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.DELETE, + }, + { + oldValue: { name: route2Name, uris: ['/test2'] }, + parentId: remoteServiceId, + resourceId: remoteRouteId, + resourceName: route2Name, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.DELETE, + }, + { + oldValue: { + name: serviceName, + routes: [ + { name: route1Name, uris: ['/test1'] }, + { name: route2Name, uris: ['/test2'] }, + ], + }, + resourceId: remoteServiceId, + resourceName: serviceName, + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.DELETE, + }, + { + newValue: { + name: serviceName, + path_prefix: '/test', + routes: [ + { name: route1Name, uris: ['/test1u'] }, + { + name: route2Name, + uris: ['/test2u'], + }, + ], + }, + resourceId: ADCSDK.utils.generateId(serviceName), + resourceName: serviceName, + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.CREATE, + }, + { + newValue: { name: route1Name, uris: ['/test1u'] }, + parentId: ADCSDK.utils.generateId(serviceName), + resourceId: ADCSDK.utils.generateId(`${serviceName}.${route1Name}`), + resourceName: route1Name, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.CREATE, + }, + { + newValue: { + name: route2Name, + uris: ['/test2u'], + }, + parentId: ADCSDK.utils.generateId(serviceName), + resourceId: ADCSDK.utils.generateId(`${serviceName}.${route2Name}`), + resourceName: route2Name, + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.CREATE, + }, + ]); + }); + + it('ensure default values for array nested object formats are merged correctly', () => { + const serviceName = 'Test Service'; + const newNode: ADCSDK.UpstreamNode = { + host: '0.0.0.0', + port: 443, + weight: 1, + }; + const oldNode = structuredClone(newNode); + oldNode.priority = 0; + + expect( + DifferV3.diff( + { + services: [ + { + name: serviceName, + upstream: { nodes: [newNode] }, + }, + ], + }, + { + services: [ + { + name: serviceName, + upstream: { nodes: [oldNode] }, + }, + ], + }, + { + core: { + service: { + upstream: { + nodes: [{ priority: 0 }], + }, + }, + }, + }, + ), + ).toEqual([]); + }); + + it('ensure route and stream route id generated currect', () => { + const services: Array = [ + { + name: 'HTTP', + routes: [ + { + name: 'HTTP 1', + uris: ['/1'], + }, + ], + }, + { + name: 'Stream', + stream_routes: [ + { + name: 'Stream 1', + server_port: 5432, + }, + ], + }, + ]; + + expect(DifferV3.diff({ services }, { services }, {})).toEqual([]); + }); + + it('ensure boolean defaults are merged correctly', () => { + const service: ADCSDK.Service = { + name: 'HTTP', + path_prefix: '/test', + strip_path_prefix: false, + }; + const oldService = structuredClone(service); + oldService.strip_path_prefix = true; + + expect( + DifferV3.diff( + { services: [service] }, + { services: [oldService] }, + { + core: { + service: { + strip_path_prefix: true, + }, + }, + }, + ), + ).toEqual([ + { + diff: [ + { kind: 'E', lhs: true, path: ['strip_path_prefix'], rhs: false }, + ], + newValue: service, + oldValue: oldService, + resourceId: ADCSDK.utils.generateId(service.name), + resourceName: service.name, + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.UPDATE, + }, + ]); + }); +}); diff --git a/apps/cli/src/differ/specs/usecase.spec.ts b/apps/cli/src/differ/specs/usecase.spec.ts new file mode 100644 index 00000000..04330d6f --- /dev/null +++ b/apps/cli/src/differ/specs/usecase.spec.ts @@ -0,0 +1,262 @@ +import * as ADCSDK from '@api7/adc-sdk'; + +import { DifferV3 } from '../differv3'; + +describe('Differ V3 - usecase', () => { + it('rename service with nested routes', () => { + expect( + DifferV3.diff( + { + services: [ + { + name: 'HTTPBIN Service1', + routes: [ + { + name: 'Anything', + methods: ['GET'], + uris: ['/anything'], + }, + { + name: 'Generate UUID', + methods: ['GET'], + uris: ['/uuid'], + }, + ], + upstream: { + scheme: 'http', + nodes: [ + { + host: 'httpbin.org', + port: 80, + weight: 1, + priority: 0, + }, + ], + }, + }, + ], + }, + { + services: [ + { + name: 'HTTPBIN Service', + description: '', + routes: [ + { + name: 'Anything', + methods: ['GET'], + uris: ['/anything'], + }, + { + name: 'Generate UUID', + methods: ['GET'], + uris: ['/uuid'], + }, + ], + upstream: { + scheme: 'http', + nodes: [ + { + host: 'httpbin.org', + port: 80, + weight: 1, + priority: 0, + }, + ], + }, + }, + ], + }, + ), + ).toEqual([ + { + oldValue: { methods: ['GET'], name: 'Anything', uris: ['/anything'] }, + parentId: ADCSDK.utils.generateId('HTTPBIN Service'), + resourceId: ADCSDK.utils.generateId('HTTPBIN Service.Anything'), + resourceName: 'Anything', + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.DELETE, + }, + { + oldValue: { methods: ['GET'], name: 'Generate UUID', uris: ['/uuid'] }, + parentId: ADCSDK.utils.generateId('HTTPBIN Service'), + resourceId: ADCSDK.utils.generateId('HTTPBIN Service.Generate UUID'), + resourceName: 'Generate UUID', + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.DELETE, + }, + { + oldValue: { + description: '', + name: 'HTTPBIN Service', + routes: [ + { methods: ['GET'], name: 'Anything', uris: ['/anything'] }, + { + name: 'Generate UUID', + methods: ['GET'], + uris: ['/uuid'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 80, priority: 0, weight: 1 }], + scheme: 'http', + }, + }, + resourceId: ADCSDK.utils.generateId('HTTPBIN Service'), + resourceName: 'HTTPBIN Service', + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.DELETE, + }, + { + newValue: { + name: 'HTTPBIN Service1', + routes: [ + { methods: ['GET'], name: 'Anything', uris: ['/anything'] }, + { + name: 'Generate UUID', + methods: ['GET'], + uris: ['/uuid'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 80, priority: 0, weight: 1 }], + scheme: 'http', + }, + }, + resourceId: ADCSDK.utils.generateId('HTTPBIN Service1'), + resourceName: 'HTTPBIN Service1', + resourceType: ADCSDK.ResourceType.SERVICE, + type: ADCSDK.EventType.CREATE, + }, + { + newValue: { methods: ['GET'], name: 'Anything', uris: ['/anything'] }, + parentId: ADCSDK.utils.generateId('HTTPBIN Service1'), + resourceId: ADCSDK.utils.generateId('HTTPBIN Service1.Anything'), + resourceName: 'Anything', + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.CREATE, + }, + { + newValue: { methods: ['GET'], name: 'Generate UUID', uris: ['/uuid'] }, + parentId: ADCSDK.utils.generateId('HTTPBIN Service1'), + resourceId: ADCSDK.utils.generateId('HTTPBIN Service1.Generate UUID'), + resourceName: 'Generate UUID', + resourceType: ADCSDK.ResourceType.ROUTE, + type: ADCSDK.EventType.CREATE, + }, + ] as Array); + }); + + it('should selectively merge the objects in default values, on a service', () => { + expect( + DifferV3.diff( + { + services: [ + { + name: 'Test Service', + upstream: { + nodes: [ + { + host: 'httpbin.org', + port: 80, + weight: 100, + }, + ], + }, + routes: [ + { + name: 'anything', + uris: ['/anything'], + }, + ], + }, + ], + }, + { + services: [ + { + name: 'Test Service', + upstream: { + name: 'default', + scheme: 'http', + type: 'roundrobin', + hash_on: 'vars', + nodes: [ + { + host: 'httpbin.org', + port: 80, + weight: 100, + priority: 0, + }, + ], + retry_timeout: 0, + pass_host: 'pass', + }, + routes: [ + { + name: 'anything', + uris: ['/anything'], + }, + ], + }, + ], + }, + { + core: { + service: { + upstream: { + checks: { + active: { + concurrency: 10, + healthy: { + http_statuses: [200, 302], + interval: 1, + successes: 2, + }, + http_path: '/', + https_verify_certificate: true, + timeout: 1, + type: 'http', + unhealthy: { + http_failures: 5, + http_statuses: [429, 404, 500, 501, 502, 503, 504, 505], + interval: 1, + tcp_failures: 2, + timeouts: 3, + }, + }, + passive: { + healthy: { + http_statuses: [ + 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, + 301, 302, 303, 304, 305, 306, 307, 308, + ], + successes: 5, + }, + type: 'http', + unhealthy: { + http_failures: 5, + http_statuses: [429, 500, 503], + tcp_failures: 2, + timeouts: 7, + }, + }, + }, + discovery_args: {}, + hash_on: 'vars', + keepalive_pool: { idle_timeout: 60, requests: 1000, size: 320 }, + name: 'default', + pass_host: 'pass', + retry_timeout: 0, + scheme: 'http', + timeout: { connect: 60, read: 60, send: 60 }, + type: 'roundrobin', + nodes: [{ priority: 0 }], + }, + }, + }, + }, + ), + ).toEqual([]); + }); +}); diff --git a/apps/cli/src/linter/index.ts b/apps/cli/src/linter/index.ts new file mode 100644 index 00000000..000fcbaa --- /dev/null +++ b/apps/cli/src/linter/index.ts @@ -0,0 +1,7 @@ +import { Configuration } from '@api7/adc-sdk'; + +import { ConfigurationSchema } from './schema'; + +export const check = (config: Configuration) => { + return ConfigurationSchema.safeParse(config); +}; diff --git a/apps/cli/src/linter/schema.ts b/apps/cli/src/linter/schema.ts new file mode 100644 index 00000000..e777b939 --- /dev/null +++ b/apps/cli/src/linter/schema.ts @@ -0,0 +1,287 @@ +import { z } from 'zod'; + +const nameSchema = z.string().min(1).max(100); +const descriptionSchema = z.string().max(256).optional(); +const labelsSchema = z.record( + z.string(), + z.union([z.string(), z.array(z.string())]), +); +const pluginsSchema = z.record(z.string(), z.record(z.string(), z.any())); +const exprSchema = z.array( + z.union([z.string(), z.array(z.lazy(() => exprSchema))]), +); +const timeoutSchema = z.object({ + connect: z.number(), + send: z.number(), + read: z.number(), +}); +const portSchema = z.number().int().min(1).max(65535); +const certificateSchema = z + .string() + .min(128) + .max(64 * 1024); + +const upstreamHealthCheckPassiveHealthy = z + .object({ + http_statuses: z + .array(z.number().int().min(200).max(599)) + .min(1) + .default([200, 302]) + .optional(), + successes: z.number().int().min(1).max(254).default(2).optional(), + }) + .strict(); +const upstreamHealthCheckPassiveUnhealthy = z + .object({ + http_statuses: z + .array(z.number().int().min(200).max(599)) + .min(1) + .default([200, 302]) + .optional(), + http_failures: z.number().int().min(1).max(254).default(5).optional(), + tcp_failures: z.number().int().min(1).max(254).default(2).optional(), + timeouts: z.number().int().min(1).max(254).default(3).optional(), + }) + .strict(); +const upstreamHealthCheckType = z + .enum(['http', 'https', 'tcp']) + .default('http'); +const upstreamSchema = z.object({ + name: nameSchema, + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + type: z.enum(['roundrobin', 'chash', 'least_conn', 'ewma']).optional(), + hash_on: z.string().optional(), + key: z.string().optional(), + checks: z + .object({ + active: z + .object({ + type: upstreamHealthCheckType.optional(), + timeout: z.number().default(1).optional(), + concurrency: z.number().default(10).optional(), + host: z.string(), //TODO + port: portSchema, + http_path: z.string().default('/').optional(), + https_verify_cert: z.boolean().default(true).optional(), + http_request_headers: z.array(z.string()).min(1).optional(), + healthy: z + .object({ + interval: z.number().int().min(1).default(1), + }) + .merge(upstreamHealthCheckPassiveHealthy) + .strict() + .optional(), + unhealthy: z + .object({ + interval: z.number().int().min(1).default(1), + }) + .merge(upstreamHealthCheckPassiveUnhealthy) + .strict() + .optional(), + }) + .optional(), + passive: z + .object({ + type: upstreamHealthCheckType.optional(), + healthy: upstreamHealthCheckPassiveHealthy.optional(), + unhealthy: upstreamHealthCheckPassiveUnhealthy.optional(), + }) + .optional(), + }) + .refine( + (data) => (data.active && data.passive) || (data.active && !data.passive), + ) + .optional(), + nodes: z.array( + z.object({ + host: z.string(), + port: portSchema.optional(), + weight: z.number().int().min(0), + priority: z.number().default(0).optional(), + metadata: z.record(z.string(), z.any()).optional(), + }), + ), + scheme: z + .enum(['grpc', 'grpcs', 'http', 'https', 'tcp', 'tls', 'udp', 'kafka']) + .default('http') + .optional(), + retries: z.number().int().min(0).optional(), + retry_timeout: z.number().min(0).optional(), + timeout: timeoutSchema.optional(), + tls: z + .object({ + cert: z.string(), + key: z.string(), + client_cert_id: z.string(), + verify: z.boolean(), + }) + .refine( + (data) => + (data.cert && data.key && !data.client_cert_id) || + (data.client_cert_id && !data.cert && !data.key), + ) + .optional(), + keepalive_pool: z + .object({ + size: z.number().int().min(1).default(320), + idle_timeout: z.number().min(0).default(60), + requests: z.number().int().min(1).default(1000), + }) + .optional(), + pass_host: z.enum(['pass', 'node', 'rewrite']).default('pass').optional(), + upstream_host: z.string().optional(), + + service_name: z.string().optional(), + discovery_type: z.string().optional(), + discovery_args: z.record(z.string(), z.any()).optional(), +}); + +const refineUpstream = (obj: z.ZodObject) => { + return obj.refine( + (data) => + (data.nodes && !data.discovery_type && !data.service_name) || + (data.discovery_type && data.service_name && !data.nodes), + ); +}; + +const routeSchema = z.object({ + name: nameSchema, + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + hosts: z.array(z.string()).optional(), + uris: z.array(z.string()).min(1), + priority: z.number().int().optional(), + timeout: timeoutSchema.optional(), + vars: exprSchema.optional(), + methods: z + .array( + z.enum([ + 'GET', + 'POST', + 'PUT', + 'DELETE', + 'PATCH', + 'HEAD', + 'OPTIONS', + 'CONNECT', + 'TRACE', + 'PURGE', + ]), + ) + .optional(), + enable_websocket: z.boolean().optional(), + remote_addrs: z.array(z.string().ip()).optional(), + plugins: pluginsSchema.optional(), + + plugin_config_id: z.string().optional(), + filter_func: z.string().optional(), +}); + +const streamRouteSchema = z.object({ + name: nameSchema, + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + remote_addr: z.string().optional(), + server_addr: z.string().optional(), + server_port: portSchema.optional(), + sni: z.string().optional(), +}); + +const serviceSchema = z.object({ + name: nameSchema, + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + upstream: refineUpstream( + upstreamSchema.extend({ name: nameSchema.optional() }), + ).optional(), + plugins: pluginsSchema.optional(), + + routes: z.array(routeSchema).optional(), + stream_routes: z.array(streamRouteSchema).optional(), +}); + +const sslSchema = z.object({ + labels: labelsSchema.optional(), + + type: z.enum(['server', 'client']).default('server').optional(), + snis: z.array(z.string().min(1)), + certificates: z.array( + z + .object({ + certificate: certificateSchema, + key: certificateSchema, + }) + .strict(), + ), + client: z + .object({ + ca: certificateSchema, + depth: z.number().int().min(0).default(1).optional(), + skip_mtls_uri_regex: z.array(z.string()).min(1).optional(), + }) + .strict(), + ssl_protocols: z.array(z.enum(['TLSv1.1', 'TLSv1.2', 'TLSv1.3'])).max(3), +}); + +const pluginConfigSchema = z.object({ + name: nameSchema, + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + plugins: pluginsSchema.optional(), +}); + +const consumerSchema = z.object({ + username: z.string().min(1), + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + plugins: pluginsSchema.optional(), +}); + +const consumerGroupSchema = z.object({ + name: nameSchema, + description: descriptionSchema.optional(), + labels: labelsSchema.optional(), + + plugins: pluginsSchema, + + consumers: z.array(consumerSchema).optional(), +}); + +export const ConfigurationSchema = z.object({ + routes: z.array(routeSchema).optional(), + services: z.array(serviceSchema).optional(), + upstreams: z.array(refineUpstream(upstreamSchema)).optional(), + ssls: z.array(sslSchema).optional(), + plugin_configs: z.array(pluginConfigSchema).optional(), + consumers: z.array(consumerSchema).optional(), + consumer_groups: z.array(consumerGroupSchema).optional(), + stream_routes: z.array(streamRouteSchema).optional(), + global_rules: z.record(z.string(), z.record(z.string(), z.any())).optional(), + plugin_metadata: z + .record(z.string(), z.record(z.string(), z.any())) + .optional(), +}); + +/* const res = configurationSchema.safeParse({ + services: [ + { + name: 'str', + description: '', + labels: { + ADC_TEST: '123', + ADC_TSET: ['123', '234'], + }, + }, + ], +} as z.infer); + +if (!res.success) { + console.log((res as SafeParseError).error); +} */ diff --git a/apps/cli/src/main.ts b/apps/cli/src/main.ts new file mode 100644 index 00000000..38610449 --- /dev/null +++ b/apps/cli/src/main.ts @@ -0,0 +1,7 @@ +import { setupCommands } from './command'; + +async function bootstrap() { + await setupCommands().parseAsync(process.argv); +} + +bootstrap(); diff --git a/apps/cli/src/utils/listr.ts b/apps/cli/src/utils/listr.ts new file mode 100644 index 00000000..d7aa2ea3 --- /dev/null +++ b/apps/cli/src/utils/listr.ts @@ -0,0 +1,113 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { + ListrRenderer, + ListrTaskEventType, + ListrTaskObject, + ListrTaskState, +} from 'listr2'; +import { attempt, isError } from 'lodash'; + +type SignaleRendererTask = ListrTaskObject< + T, + typeof SignaleRenderer, + typeof SignaleRenderer +>; + +export interface SignaleRendererOptions { + verbose?: number; + scope?: Array; +} + +export interface SignaleRendererOutput { + type: string; + messages: Array; +} + +export class SignaleRenderer implements ListrRenderer { + public static nonTTY = true; + public static rendererOptions: SignaleRendererOptions = { + verbose: 1, + scope: ['ADC'], + }; + public static rendererTaskOptions: never; + + // get tasks to be rendered and options of the renderer from the parent + constructor( + private readonly tasks: SignaleRendererTask[], + private options: SignaleRendererOptions, + ) { + this.options = { + ...SignaleRenderer.rendererOptions, + ...this.options, + }; + } + + // implement custom logic for render functionality + public render() { + this.renderer(this.tasks); + } + + private renderer(tasks: SignaleRendererTask[]) { + tasks.forEach((task) => { + const rendererOptions = { + ...this.options, + ...task.rendererOptions, + }; + + task.on(ListrTaskEventType.SUBTASK, (subTasks) => { + return this.renderer(subTasks); + }); + + task.on(ListrTaskEventType.STATE, (state) => { + if (!task.hasTitle()) return; + + if (state === ListrTaskState.STARTED) { + rendererOptions?.verbose > 0 && + ADCSDK.utils.getLogger(rendererOptions?.scope).start(task.title); + } + if (state === ListrTaskState.COMPLETED) { + rendererOptions?.verbose > 0 && + ADCSDK.utils.getLogger(rendererOptions?.scope).success(task.title); + } + if (state === ListrTaskState.SKIPPED) { + rendererOptions?.verbose > 0 && + ADCSDK.utils + .getLogger(rendererOptions?.scope) + .info( + `${task.title} is skipped${task.message.skip ? `: ${task.message.skip}` : ''}`, + ); + } + if (state === ListrTaskState.FAILED) { + rendererOptions?.verbose > 0 && + ADCSDK.utils.getLogger(rendererOptions?.scope).error(task.title); + } + }); + + task.on(ListrTaskEventType.OUTPUT, (str) => { + const output = attempt(JSON.parse, str) as SignaleRendererOutput; + if (isError(output)) return; + if (!output.type || !output.messages) return; + + switch (output.type) { + case 'debug': { + if (output?.messages && rendererOptions?.verbose === 2) { + ADCSDK.utils + .getLogger(rendererOptions?.scope) + .debug(output.messages.join('')); + } + break; + } + } + }); + }); + } + + public end(err?: Error) { + if (err) { + ADCSDK.utils.getLogger().fatal(err); + } else { + this.options.verbose > 0 && + ADCSDK.utils.getLogger().star('All is well, see you next time!'); + } + } +} diff --git a/apps/cli/tsconfig.app.json b/apps/cli/tsconfig.app.json new file mode 100644 index 00000000..c8caae7b --- /dev/null +++ b/apps/cli/tsconfig.app.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": [ + "node" + ], + "emitDecoratorMetadata": true, + "target": "es2021", + "lib": [ + "ESNext" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "include": [ + "src/**/*.ts" + ] +} diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json new file mode 100644 index 00000000..6f020769 --- /dev/null +++ b/apps/cli/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "esModuleInterop": true + }, + "ts-node": { + "compilerOptions": { + "module": "commonjs" + } + } +} diff --git a/apps/cli/tsconfig.spec.json b/apps/cli/tsconfig.spec.json new file mode 100644 index 00000000..9b2a121d --- /dev/null +++ b/apps/cli/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/cli/webpack.config.js b/apps/cli/webpack.config.js new file mode 100644 index 00000000..11ea4c61 --- /dev/null +++ b/apps/cli/webpack.config.js @@ -0,0 +1,23 @@ +const { composePlugins, withNx } = require('@nx/webpack'); +const { DefinePlugin, optimize } = require('webpack'); + +// Nx plugins for webpack. +module.exports = composePlugins( + withNx({ + target: 'node', + }), + (config) => { + config.externals = { + '*': false + } + config.plugins.push(...[ + new DefinePlugin({ + 'process.env.NODE_ENV': `'${process.env.NODE_ENV ?? 'development'}'` + }), + new optimize.LimitChunkCountPlugin({ + maxChunks: 1 + }), + ]) + return config; + } +); diff --git a/cmd/configure.go b/cmd/configure.go deleted file mode 100644 index f39e600f..00000000 --- a/cmd/configure.go +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "bufio" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "github.com/fatih/color" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "golang.org/x/term" - "net/url" - "os" - "path/filepath" - "strings" -) - -// newConfigureCmd represents the configure command -func newConfigureCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "configure", - Short: "Configure ADC with APISIX instance", - Long: `Configures ADC with APISIX's server address and token.`, - RunE: func(cmd *cobra.Command, args []string) error { - return saveConfiguration(cmd) - }, - } - - cmd.Flags().BoolP("overwrite", "f", false, "overwrite existed configuration file") - - cmd.Flags().StringP("address", "a", "", "APISIX server address") - - cmd.Flags().StringP("token", "t", "", "APISIX token") - cmd.Flags().String("capath", "", "ca path for mtls connection") - cmd.Flags().String("cert", "", "certificate for mtls connection") - cmd.Flags().String("cert-key", "", "certificate key for mtls connection") - cmd.Flags().BoolP("insecure", "k", false, "insecure connection for mtls connection") - - return cmd -} - -func saveConfiguration(cmd *cobra.Command) error { - overwrite, err := cmd.Flags().GetBool("overwrite") - if err != nil { - color.Red("Failed to get key: %v", err) - return err - } - - if !overwrite && rootConfig.Server != "" && rootConfig.Token != "" { - color.Yellow("ADC configured. Run `adc ping` to test the configuration, or pass `-f` to overwrite configuration file.") - return nil - } - - rootConfig.Server, err = cmd.Flags().GetString("address") - if err != nil { - color.Red("Failed to get APISIX address: %v", err) - return err - } - - rootConfig.Token, err = cmd.Flags().GetString("token") - if err != nil { - color.Red("Failed to get token: %v", err) - return err - } - - rootConfig.CAPath, err = cmd.Flags().GetString("capath") - if err != nil { - color.Red("Failed to get ca path: %v", err) - return err - } - - rootConfig.Certificate, err = cmd.Flags().GetString("cert") - if err != nil { - color.Red("Failed to get certificate path: %v", err) - return err - } - rootConfig.CertificateKey, err = cmd.Flags().GetString("cert-key") - if err != nil { - color.Red("Failed to get certificate key path: %v", err) - return err - } - rootConfig.Insecure, err = cmd.Flags().GetBool("insecure") - if err != nil { - color.Red("Failed to get insecure option: %v", err) - return err - } - - if rootConfig.CAPath != "" { - if rootConfig.Certificate != "" && rootConfig.CertificateKey == "" { - color.Red("Certificate key file path no provided!") - return errors.New("certificate key file path no provided") - } - - if rootConfig.Certificate == "" && rootConfig.CertificateKey != "" { - color.Red("Certificate file path no provided!") - return errors.New("certificate file path no provided") - } - - rootConfig.CAPath, err = filepath.Abs(rootConfig.CAPath) - if err != nil { - color.Red("Failed to resolve CA path: %v", err) - return err - } - rootConfig.Certificate, err = filepath.Abs(rootConfig.Certificate) - if err != nil { - color.Red("Failed to resolve certificate path: %v", err) - return err - } - rootConfig.CertificateKey, err = filepath.Abs(rootConfig.CertificateKey) - if err != nil { - color.Red("Failed to resolve certificate key path: %v", err) - return err - } - - if strings.HasPrefix(rootConfig.Server, "http://") { - color.Yellow("APISIX address is configured with HTTP protocol, replaced by HTTPS") - rootConfig.Server = strings.Replace(rootConfig.Server, "http://", "https://", 1) - } - - rootCA, err := os.ReadFile(rootConfig.CAPath) - if err != nil { - color.Red("Failed to read CA file: %v", err) - return err - } - - caCertPool := x509.NewCertPool() - ok := caCertPool.AppendCertsFromPEM(rootCA) - if !ok { - color.Red("Failed to parse CA certificate") - return errors.New("failed to parse CA certificate") - } - - cert, err := os.ReadFile(rootConfig.Certificate) - if err != nil { - color.Red("Failed to read certificate file: %v", err) - return err - } - key, err := os.ReadFile(rootConfig.CertificateKey) - if err != nil { - color.Red("Failed to read certificate key file: %v", err) - return err - } - _, err = tls.X509KeyPair(cert, key) - if err != nil { - color.Red("Failed to parse x509 key pair: %v", err) - return err - } - } - - reader := bufio.NewReader(os.Stdin) - if rootConfig.Server == "" { - fmt.Println("Please enter the APISIX server address: ") - server, err := reader.ReadString('\n') - if err != nil { - return err - } - rootConfig.Server = strings.TrimSpace(server) - } - - if !strings.HasPrefix(rootConfig.Server, "http://") && !strings.HasPrefix(rootConfig.Server, "https://") { - color.Yellow("APISIX address " + rootConfig.Server + " is configured without protocol, using HTTP") - rootConfig.Server = "http://" + rootConfig.Server - } - rootConfig.Server = strings.TrimSuffix(rootConfig.Server, "/") - - _, err = url.Parse(rootConfig.Server) - if err != nil { - color.Red("Parse APISIX server address failed: %v", err) - return err - } - - if rootConfig.Token == "" || overwrite { - fmt.Println("Please enter the APISIX token: ") - if term.IsTerminal(int(os.Stdin.Fd())) { - token, err := term.ReadPassword(int(os.Stdin.Fd())) - if err != nil { - return err - } - rootConfig.Token = strings.TrimSpace(string(token)) - } else { - token, err := reader.ReadString('\n') - if err != nil { - return err - } - rootConfig.Token = strings.TrimSpace(string(token)) - } - } - - // use viper to save the configuration - viper.Set("server", rootConfig.Server) - viper.Set("token", rootConfig.Token) - viper.Set("capath", rootConfig.CAPath) - viper.Set("cert", rootConfig.Certificate) - viper.Set("cert-key", rootConfig.CertificateKey) - viper.Set("insecure", rootConfig.Insecure) - - if overwrite { - // because WriteConfig fails to write if the file does not exist - // and WriteConfigAs does write even if the file does not exist - // see: https://github.com/spf13/viper/issues/433 - err = viper.WriteConfigAs(cfgFile) - } else { - err = viper.SafeWriteConfig() - } - if err != nil { - color.Red("Failed to configure ADC") - return err - } - - color.Green("ADC configured successfully!") - - return pingAPISIX() -} diff --git a/cmd/diff.go b/cmd/diff.go deleted file mode 100644 index c4e67bc9..00000000 --- a/cmd/diff.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "github.com/spf13/cobra" -) - -// newDiffCmd represents the diff command -func newDiffCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "diff", - Short: "Show the differences between the local and existing APISIX configuration", - Long: `Shows the differences in the configuration between the local confguration file and the connected APISIX instance.`, - RunE: func(cmd *cobra.Command, args []string) error { - checkConfig() - - // todo: support multiple files - err := sync(cmd, true) - return err - }, - } - - cmd.Flags().StringArrayP("file", "f", []string{"apisix.yaml"}, "configuration file path") - return cmd -} diff --git a/cmd/dump.go b/cmd/dump.go deleted file mode 100644 index 88484d6f..00000000 --- a/cmd/dump.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "context" - "fmt" - - "github.com/fatih/color" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" -) - -// newDumpCmd represents the dump command -func newDumpCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "dump", - Short: "Dump the APISIX configuration", - Long: `Dumps the configuration of the connected APISIX instance to a local file.`, - RunE: func(cmd *cobra.Command, args []string) error { - checkConfig() - - err := dumpConfiguration(cmd) - if err != nil { - color.Red(err.Error()) - } - return err - }, - } - - cmd.Flags().StringP("output", "o", "/dev/stdout", "output file path") - cmd.Flags().StringToStringP("labels", "l", map[string]string{}, "labels to filter resources") - - return cmd -} - -func dumpConfiguration(cmd *cobra.Command) error { - path, err := cmd.Flags().GetString("output") - if err != nil { - color.Red("Failed to get output file path: %v", err) - return err - } - if path == "" { - path = "/dev/stdout" - } - - save := true - if path == "/dev/stdout" { - save = false - } - - labels, err := cmd.Flags().GetStringToString("labels") - if err != nil { - return err - } - - cluster, err := apisix.NewCluster(context.Background(), rootConfig.ClientConfig) - if err != nil { - return err - } - - svcs, err := cluster.Service().List(context.Background()) - if err != nil { - return err - } - - svcs = types.FilterResources(labels, svcs) - - routes, err := cluster.Route().List(context.Background()) - if err != nil { - return err - } - - routes = types.FilterResources(labels, routes) - - consumers, err := cluster.Consumer().List(context.Background()) - if err != nil { - return err - } - - consumers = types.FilterResources(labels, consumers) - - ssls, err := cluster.SSL().List(context.Background()) - if err != nil { - return err - } - - ssls = types.FilterResources(labels, ssls) - - globalRules, err := cluster.GlobalRule().List(context.Background()) - if err != nil { - return err - } - - pluginConfigs, err := cluster.PluginConfig().List(context.Background()) - if err != nil { - return err - } - - pluginConfigs = types.FilterResources(labels, pluginConfigs) - - consumerGroups, err := cluster.ConsumerGroup().List(context.Background()) - if err != nil { - return err - } - - consumerGroups = types.FilterResources(labels, consumerGroups) - - pluginMetadatas, err := cluster.PluginMetadata().List(context.Background()) - if err != nil { - return err - } - - streamRoutes, err := cluster.StreamRoute().List(context.Background()) - if err != nil { - return err - } - - streamRoutes = types.FilterResources(labels, streamRoutes) - - upstreams, err := cluster.Upstream().List(context.Background()) - if err != nil { - return err - } - - upstreams = types.FilterResources(labels, upstreams) - - conf := &types.Configuration{ - Routes: routes, - Services: svcs, - Consumers: consumers, - SSLs: ssls, - GlobalRules: globalRules, - PluginConfigs: pluginConfigs, - ConsumerGroups: consumerGroups, - PluginMetadatas: pluginMetadatas, - StreamRoutes: streamRoutes, - Upstreams: upstreams, - } - - if len(labels) > 0 { - conf.Meta = &types.ConfigurationMeta{ - Mode: types.ModePartial, - Labels: labels, - } - } - - if save { - err = common.SaveAPISIXConfiguration(path, conf) - if err != nil { - return err - } - color.Green("Successfully dump configurations to " + path) - } else { - data, err := yaml.Marshal(conf) - if err != nil { - color.Red(err.Error()) - return err - } - - _, err = fmt.Printf("%s", data) - if err != nil { - return err - } - } - - return nil -} diff --git a/cmd/openapi2apisix.go b/cmd/openapi2apisix.go deleted file mode 100644 index 0bb2a02d..00000000 --- a/cmd/openapi2apisix.go +++ /dev/null @@ -1,105 +0,0 @@ -package cmd - -import ( - "bufio" - "context" - "fmt" - "io" - "os" - - "github.com/fatih/color" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "github.com/api7/adc/internal/pkg/openapi2apisix" - "github.com/api7/adc/pkg/common" -) - -// newOpenAPI2APISIXCmd represents the openapi2apisix command -func newOpenAPI2APISIXCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "openapi2apisix", - Short: "Convert OpenAPI configuration to ADC configuration", - Long: `Converts the configuration in OpenAPI format to the ADC configuration format.`, - RunE: func(cmd *cobra.Command, args []string) error { - - err := openAPI2APISIX(cmd) - if err != nil { - color.Red(err.Error()) - } - return err - }, - } - - cmd.Flags().StringP("file", "f", "", "OpenAPI configuration file path") - cmd.Flags().StringP("output", "o", "/dev/stdout", "output file path") - - return cmd -} - -func openAPI2APISIX(cmd *cobra.Command) error { - output, err := cmd.Flags().GetString("output") - if err != nil { - color.Red("Failed to get output file path: %v", err) - return err - } - if output == "" { - output = "/dev/stdout" - } - - save := true - if output == "/dev/stdout" { - save = false - } - - filename, err := cmd.Flags().GetString("file") - if err != nil { - color.Red("Failed to get OpenAPI file path: %v", err) - return err - } - if filename == "" { - color.Red("OpenAPI file path is empty.") - return nil - } - - f, err := os.Open(filename) - if err != nil { - color.Red("Failed to open %s: %s", filename, err) - return err - } - defer f.Close() - - reader := bufio.NewReader(f) - fileContent, err := io.ReadAll(reader) - if err != nil { - color.Red("Failed to read file %s: %s", filename, err) - return err - } - - conf, err := openapi2apisix.Convert(context.Background(), fileContent) - if err != nil { - color.Red("Failed to convert OpenAPI file %s: %s", filename, err) - return err - } - - if save { - err = common.SaveAPISIXConfiguration(output, conf) - if err != nil { - return err - } - color.Green("Converted OpenAPI file to %s successfully ", output) - } else { - data, err := yaml.Marshal(conf) - if err != nil { - color.Red(err.Error()) - return err - } - - _, err = fmt.Printf("%s", data) - if err != nil { - return err - } - } - - return nil -} diff --git a/cmd/ping.go b/cmd/ping.go deleted file mode 100644 index cfc2d907..00000000 --- a/cmd/ping.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "context" - - "github.com/fatih/color" - "github.com/spf13/cobra" - - "github.com/api7/adc/pkg/api/apisix" -) - -// newPingCmd represents the ping command -func newPingCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "ping", - Short: "Verify connectivity with APISIX", - Long: `Pings the configured APISIX instance to verify connectivity.`, - RunE: func(cmd *cobra.Command, args []string) error { - checkConfig() - - return pingAPISIX() - }, - } - - return cmd -} - -// pingAPISIX check the connection to the APISIX -func pingAPISIX() error { - cluster, err := apisix.NewCluster(context.Background(), rootConfig.ClientConfig) - if err != nil { - return err - } - - err = cluster.Ping() - if err != nil { - color.Red("Failed to ping backend, response: %s", err.Error()) - } else { - color.Green("Connected to backend successfully!") - } - return nil -} diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index a28ac009..00000000 --- a/cmd/root.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "context" - "os" - - "github.com/fatih/color" - homedir "github.com/mitchellh/go-homedir" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/config" -) - -type Config struct { - config.ClientConfig - APISIXCluster apisix.Cluster -} - -var ( - cfgFile string - rootConfig Config -) - -// rootCmd represents the base command when called without any subcommands -func newRootCmd() *cobra.Command { - rootCmd := &cobra.Command{ - Use: "adc", - Short: "APISIX Declarative CLI", - Long: `A command line interface for configuring APISIX declaratively. - -It can be used to validate, dump, diff, and sync configurations with an APISIX instance. - `, - } - cobra.OnInitialize(initConfig) - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.adc.yaml)") - - rootCmd.AddCommand(newConfigureCmd()) - rootCmd.AddCommand(newPingCmd()) - rootCmd.AddCommand(newDumpCmd()) - rootCmd.AddCommand(newDiffCmd()) - rootCmd.AddCommand(newSyncCmd()) - rootCmd.AddCommand(newValidateCmd()) - rootCmd.AddCommand(newVersionCmd()) - rootCmd.AddCommand(newOpenAPI2APISIXCmd()) - return rootCmd -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - rootCmd := newRootCmd() - err := rootCmd.Execute() - if err != nil { - os.Exit(1) - } -} - -func initConfig() { - if cfgFile == "" { - home, err := homedir.Dir() - if err != nil { - color.Red("Failed to get home dir: %s", err.Error()) - os.Exit(1) - } - viper.AddConfigPath(home) - viper.SetConfigName(".adc") - viper.SetConfigType("yaml") - cfgFile = home + "/.adc.yaml" - } else { - viper.SetConfigFile(cfgFile) - } - - _, err := os.Stat(os.ExpandEnv(cfgFile)) - - if err != nil { - if os.IsNotExist(err) { - color.Yellow("Configuration file %s doesn't exist.", cfgFile) - return - } else { - color.Red("Error reading configuration file: %s", err.Error()) - return - } - } - - err = viper.ReadInConfig() - if err != nil { - color.Red("Failed to read configuration file: %s", err.Error()) - return - } - - rootConfig.Server = viper.GetString("server") - rootConfig.Token = viper.GetString("token") - rootConfig.CAPath = viper.GetString("capath") - rootConfig.Certificate = viper.GetString("cert") - rootConfig.CertificateKey = viper.GetString("cert-key") - rootConfig.Insecure = viper.GetBool("insecure") - cluster, err := apisix.NewCluster(context.Background(), rootConfig.ClientConfig) - if err != nil { - color.RedString("Failed to create a new cluster: %v", err.Error()) - return - } - rootConfig.APISIXCluster = cluster -} diff --git a/cmd/sync.go b/cmd/sync.go deleted file mode 100644 index f45569a9..00000000 --- a/cmd/sync.go +++ /dev/null @@ -1,190 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "fmt" - "strings" - "time" - - "github.com/fatih/color" - "github.com/spf13/cobra" - - "github.com/api7/adc/internal/pkg/differ" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" - "github.com/api7/adc/pkg/data" -) - -// newSyncCmd represents the configure command -func newSyncCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "sync", - Short: "Sync local configuration to APISIX", - Long: `Syncs the configuration in apisix.yaml (or other provided file) to APISIX.`, - RunE: func(cmd *cobra.Command, args []string) error { - checkConfig() - - // TODO: add validate before sync - err := sync(cmd, false) - return err - }, - } - - cmd.Flags().StringArrayP("file", "f", []string{"apisix.yaml"}, "configuration file path") - cmd.Flags().BoolP("partial", "p", false, "partial apply mode. In partial mode, only add and update event will be applied.") - - return cmd -} - -type summary struct { - created int - updated int - deleted int -} - -func syncFile(dryRun, partial bool, file string) (*summary, error) { - config, err := common.GetContentFromFile(file) - if err != nil { - color.Red("Failed to read configuration file: %v", err) - return nil, err - } - if config.Meta != nil { - if config.Meta.Mode == types.ModePartial { - partial = true - } - } - - if len(config.StreamRoutes) > 0 { - supportStreamRoute, err := rootConfig.APISIXCluster.SupportStreamRoute() - if err != nil { - color.Red("Failed to check stream mode: %v", err) - return nil, err - } - if !supportStreamRoute { - color.Yellow("Backend stream mode is disabled but configuration contains stream routes, abort") - return &summary{ - created: 0, - updated: 0, - deleted: 0, - }, nil - } - } - - remoteConfig, err := common.GetContentFromRemote(rootConfig.APISIXCluster) - if err != nil { - color.Red("Failed to get remote configuration: %v", err) - return nil, err - } - - d, err := differ.NewDiffer(config, remoteConfig) - if err != nil { - color.Red("Failed to create a Differ object: %v", err) - return nil, err - } - - events, err := d.Diff() - if err != nil { - color.Red("Failed to compare local and remote configuration: %v", err) - return nil, err - } - - summary := &summary{ - created: 0, - updated: 0, - deleted: 0, - } - - for _, event := range events { - if event.Option == data.CreateOption { - summary.created++ - } else if event.Option == data.UpdateOption { - summary.updated++ - } else if event.Option == data.DeleteOption { - if partial { - continue - } - summary.deleted++ - } - - str, err := event.Output(dryRun) - if err != nil { - color.Red("Failed to get output of the event: %v", err) - return nil, err - } - - if !dryRun { - err = event.Apply(rootConfig.APISIXCluster) - if err != nil { - color.Red("Failed to apply configuration: %v", err) - return nil, err - } - time.Sleep(100 * time.Millisecond) - } - - for _, line := range strings.Split(str, "\n") { - if strings.HasPrefix(line, "+") || strings.HasPrefix(line, "creating") { - color.Green(line) - } else if strings.HasPrefix(line, "-") || strings.HasPrefix(line, "deleting") { - color.Red(line) - } else { - fmt.Println(line) - } - } - } - - return summary, nil -} - -func sync(cmd *cobra.Command, dryRun bool) error { - files, err := cmd.Flags().GetStringArray("file") - if err != nil { - color.Red("Failed to get the configuration file: %v", err) - return err - } - if len(files) == 0 { - color.Red("No input files") - return nil - } - - partial := false - - if !dryRun { - partial, err = cmd.Flags().GetBool("partial") - if err != nil { - color.Red("Failed to get partial option: %v", err) - return err - } - } - - if len(files) > 1 { - partial = true - } - - summary := &summary{ - created: 0, - updated: 0, - deleted: 0, - } - - for _, file := range files { - sum, err := syncFile(dryRun, partial, file) - if err != nil { - color.Red("failed to sync file %v, error: %v", file, err) - continue - } - - summary.created += sum.created - summary.updated += sum.updated - summary.deleted += sum.deleted - } - - if dryRun { - color.Green("Summary: create %d, update %d, delete %d", summary.created, summary.updated, summary.deleted) - } else { - color.Green("Summary: created %d, updated %d, deleted %d", summary.created, summary.updated, summary.deleted) - } - - return nil -} diff --git a/cmd/utils.go b/cmd/utils.go deleted file mode 100644 index 83c8ca73..00000000 --- a/cmd/utils.go +++ /dev/null @@ -1,14 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/fatih/color" -) - -func checkConfig() { - if rootConfig.Server == "" || rootConfig.Token == "" { - color.Yellow("ADC isn't configured, run `adc configure` to configure ADC.") - os.Exit(0) - } -} diff --git a/cmd/validate.go b/cmd/validate.go deleted file mode 100644 index 504707b5..00000000 --- a/cmd/validate.go +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package cmd - -import ( - "context" - "fmt" - - "github.com/fatih/color" - "github.com/spf13/cobra" - - "github.com/api7/adc/internal/pkg/validator" - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" -) - -// newValidateCmd represents the configure command -func newValidateCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "validate", - Short: "Validate the provided configuration file", - Long: `Validates the provided configuration file with the connected APISIX instance.`, - RunE: func(cmd *cobra.Command, args []string) error { - checkConfig() - - file, err := cmd.Flags().GetString("file") - if err != nil { - color.Red("Failed to get file path: %v", err) - return err - } - if file == "" { - color.Red("File path is empty. Please specify a file path: adc validate -f apisix.yaml") - return nil - } - - d, err := common.GetContentFromFile(file) - if err != nil { - color.Red("Failed to read configuration file: %v", err) - return err - } - - err = validateContent(d) - if err != nil { - color.Red("Failed to validate configuration file: %v", err) - return err - } - return nil - }, - } - - cmd.Flags().StringP("file", "f", "apisix.yaml", "configuration file path") - - return cmd -} - -func displayConfigOverview(d *types.Configuration) { - msg := fmt.Sprintf("Read configuration file successfully: config name: %v, version: %v", d.Name, d.Version) - changed := false - if len(d.Routes) > 0 { - msg += fmt.Sprintf(", routes: %v", len(d.Routes)) - changed = true - } - if len(d.Services) > 0 { - msg += fmt.Sprintf(", services: %v", len(d.Services)) - changed = true - } - if len(d.Consumers) > 0 { - msg += fmt.Sprintf(", consumers: %v", len(d.Consumers)) - changed = true - } - if len(d.SSLs) > 0 { - msg += fmt.Sprintf(", ssls: %v", len(d.SSLs)) - changed = true - } - if len(d.GlobalRules) > 0 { - msg += fmt.Sprintf(", global_rules: %v", len(d.GlobalRules)) - changed = true - } - if len(d.PluginConfigs) > 0 { - msg += fmt.Sprintf(", plugin_configs: %v", len(d.PluginConfigs)) - changed = true - } - if len(d.ConsumerGroups) > 0 { - msg += fmt.Sprintf(", consumer_groups: %v", len(d.ConsumerGroups)) - changed = true - } - // TODO: enable this when APISIX supports - //if len(d.PluginMetadatas) > 0 { - // msg += fmt.Sprintf(", plugin_metadatas: %v", len(d.PluginMetadatas)) - // changed = true - //} - if len(d.StreamRoutes) > 0 { - msg += fmt.Sprintf(", stream_routes: %v", len(d.StreamRoutes)) - changed = true - } - if len(d.Upstreams) > 0 { - msg += fmt.Sprintf(", upstreams: %v", len(d.StreamRoutes)) - changed = true - } - if !changed { - msg += "nothing changed" - } - msg += "." - color.Green(msg) -} - -// validateContent validates the content of the configuration file -func validateContent(c *types.Configuration) error { - cluster, err := apisix.NewCluster(context.Background(), rootConfig.ClientConfig) - if err != nil { - return err - } - supportValidate, err := cluster.SupportValidate() - if err != nil { - return err - } - if !supportValidate { - color.Yellow("Backend doesn't support validate API, abort") - return nil - } - - displayConfigOverview(c) - - v, err := validator.NewValidator(c, cluster) - if err != nil { - color.Red("Failed to create validator: %v", err) - return err - } - errs := v.Validate() - if len(errs) > 0 { - color.Red("Some validation failed:") - for _, err := range errs { - color.Red(err.Error()) - } - } else { - color.Green("Successfully validated configuration file!") - } - return nil -} diff --git a/cmd/version.go b/cmd/version.go deleted file mode 100644 index 50a6f043..00000000 --- a/cmd/version.go +++ /dev/null @@ -1,25 +0,0 @@ -package cmd - -import ( - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -var ( - VERSION = "dev" - GitRevision = "unknown" -) - -// newVersionCmd represents the version command -func newVersionCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "version", - Short: "Print the version of ADC", - Long: `Prints the version of ADC. See https://github.com/api7/adc for details on how to update.`, - Run: func(cmd *cobra.Command, args []string) { - color.Green("ADC version: %s - %s\n", VERSION, GitRevision) - }, - } - - return cmd -} diff --git a/go.mod b/go.mod deleted file mode 100644 index d82dfd16..00000000 --- a/go.mod +++ /dev/null @@ -1,87 +0,0 @@ -module github.com/api7/adc - -go 1.20 - -require ( - github.com/api7/gopkg v0.2.0 - github.com/fatih/color v1.16.0 - github.com/gavv/httpexpect/v2 v2.16.0 - github.com/getkin/kin-openapi v0.120.0 - github.com/hashicorp/go-memdb v1.3.4 - github.com/hexops/gotextdiff v1.0.3 - github.com/mitchellh/go-homedir v1.1.0 - github.com/mozillazg/go-slugify v0.2.0 - github.com/onsi/ginkgo/v2 v2.13.0 - github.com/onsi/gomega v1.29.0 - github.com/pkg/errors v0.9.1 - github.com/spf13/cobra v1.8.0 - github.com/spf13/viper v1.17.0 - github.com/stretchr/testify v1.8.4 - go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.26.0 - golang.org/x/term v0.13.0 - sigs.k8s.io/yaml v1.4.0 -) - -require ( - github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect - github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/fatih/structs v1.1.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/gobwas/glob v0.2.3 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect - github.com/gorilla/websocket v1.4.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imkira/go-interpol v1.1.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/yaml v0.2.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/mozillazg/go-unidecode v0.2.0 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/sagikazarmark/locafero v0.3.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sanity-io/litter v1.5.5 // indirect - github.com/sergi/go-diff v1.3.1 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.10.0 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.48.0 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect - github.com/yudai/gojsondiff v1.0.0 // indirect - github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.13.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - moul.io/http2curl/v2 v2.3.0 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index d1ca499f..00000000 --- a/go.sum +++ /dev/null @@ -1,695 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= -github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= -github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/api7/gopkg v0.2.0 h1:+5vw6Rt9IeEpJoL2JMZFDwGDhH4aaeUJize21/3Qdzc= -github.com/api7/gopkg v0.2.0/go.mod h1:LlplmjGCrkcS3nyCYpTwkdLkuNlYRYAnI8XH1qwsICw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gavv/httpexpect/v2 v2.16.0 h1:Ty2favARiTYTOkCRZGX7ojXXjGyNAIohM1lZ3vqaEwI= -github.com/gavv/httpexpect/v2 v2.16.0/go.mod h1:uJLaO+hQ25ukBJtQi750PsztObHybNllN+t+MbbW8PY= -github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= -github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= -github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= -github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mozillazg/go-slugify v0.2.0 h1:SIhqDlnJWZH8OdiTmQgeXR28AOnypmAXPeOTcG7b9lk= -github.com/mozillazg/go-slugify v0.2.0/go.mod h1:z7dPH74PZf2ZPFkyxx+zjPD8CNzRJNa1CGacv0gg8Ns= -github.com/mozillazg/go-unidecode v0.2.0 h1:vFGEzAH9KSwyWmXCOblazEWDh7fOkpmy/Z4ArmamSUc= -github.com/mozillazg/go-unidecode v0.2.0/go.mod h1:zB48+/Z5toiRolOZy9ksLryJ976VIwmDmpQ2quyt1aA= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= -github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= -github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/sony/sonyflake v1.1.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -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/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= -github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= -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 v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79Tc= -github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= -moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/install.sh b/install.sh deleted file mode 100755 index c0323b0f..00000000 --- a/install.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash - -ARCH="$(uname -m)" -OS="$(uname)" - -# convert to standard arch names used in files -if [ "x${ARCH}" = "xx86_64" ]; then - ARCH="amd64" -fi - -# convert to standard os names used in files -# TODO: support Windows -if [ "x${OS}" = "xDarwin" ]; then - OS="darwin" -else - OS="linux" -fi - -# either specify the version in an environment variable or get the latest from GitHub -if [ "x${ADC_VERSION}" = "x" ]; then - ADC_VERSION=$(curl -L -s https://github.com/api7/adc/releases/latest | - grep "adc/releases/tag/" | head -1 | awk -F '"' '{print $4}' | - awk -F '/' '{print $NF}') -fi - -if [ "x${ADC_VERSION}" = "x" ]; then - printf "Unable to find the latest version of ADC. Please set the ADC_VERSION environment variable and try again. For example, export ADC_VERSION=0.5.0\n" - exit 1 -fi - -# if version has v in prefix, remove it -ADC_VERSION=${ADC_VERSION#v} - -FILENAME="adc_${ADC_VERSION}_${OS}_${ARCH}.tar.gz" - -# example download URL format: https://github.com/api7/adc/releases/download/v0.5.0/adc_0.5.0_darwin_arm64.tar.gz -URL="https://github.com/api7/adc/releases/download/v${ADC_VERSION}/${FILENAME}" - -printf "Downloading ADC v${ADC_VERSION} for ${OS} ${ARCH}...\n\n" -# printf "Download URL: %s\n" "$URL" - -curl -L ${URL} -o ${PWD}/adc.tar.gz -if [ $? -ne 0 ]; then - echo "Error downloading ADC. Please check your internet connection and try again." - exit 1 -fi - -# temporary folder name to extract the downloaded file -TEMP_FOLDER_NAME=$(tr -dc A-Za-z0-9 /dev/null | head -c 16) -if [ -z "$TEMP_FOLDER_NAME" ]; then - TEMP_FOLDER_NAME="TEMP_ADC_FOLDER" -fi - -mkdir $TEMP_FOLDER_NAME - -printf "\nExtracting ADC to temporary folder %s...\n" "$TEMP_FOLDER_NAME" - -tar -xzf "${PWD}/adc.tar.gz" -C "${PWD}/${TEMP_FOLDER_NAME}" -if [ $? -ne 0 ]; then - echo "Error extracting ADC. The downloaded file might be corrupted. Please try again and make sure that you are installing the correct version." - exit 1 -fi - -INSTALL_DIR=${ADC_DIR} -if [ -z "$INSTALL_DIR" ]; then - INSTALL_DIR="/usr/local/bin" -fi - -printf "Installing ADC in $INSTALL_DIR...\n" - -WHOAMI=$(whoami) - -# install adc binary, use sudo if user doesn't have permission to install in INSTALL_DIR -if mv "${PWD}/$TEMP_FOLDER_NAME/adc" "$INSTALL_DIR/adc" >/dev/null 2>&1; then - echo "ADC installed successfully!" -else - if sudo mv ${PWD}/$TEMP_FOLDER_NAME/adc $INSTALL_DIR/adc; then - echo "ADC installed successfully with sudo permissions!" - else - echo "Unable to install ADC. Please check the permissions of the user $WHOAMI for the directory $INSTALL_DIR." - exit 1 - fi -fi - -# clean up temporary files -printf "Removing temporary files...\n" -rm -rf adc.tar.gz ${PWD}/$TEMP_FOLDER_NAME/ - -printf "Done!\n" diff --git a/internal/pkg/db/memdb.go b/internal/pkg/db/memdb.go deleted file mode 100644 index 3b7d1650..00000000 --- a/internal/pkg/db/memdb.go +++ /dev/null @@ -1,261 +0,0 @@ -package db - -import ( - "errors" - - "github.com/hashicorp/go-memdb" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" -) - -var schema = &memdb.DBSchema{ - Tables: map[string]*memdb.TableSchema{ - "services": { - Name: "services", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "routes": { - Name: "routes", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "consumers": { - Name: "consumers", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "Username"}, - }, - }, - }, - "ssls": { - Name: "ssls", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "global_rules": { - Name: "global_rules", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "plugin_configs": { - Name: "plugin_configs", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "consumer_groups": { - Name: "consumer_groups", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "plugin_metadatas": { - Name: "plugin_metadatas", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "stream_routes": { - Name: "stream_routes", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - "upstreams": { - Name: "upstreams", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - Unique: true, - Indexer: &memdb.StringFieldIndex{Field: "ID"}, - }, - }, - }, - }, -} - -type DB struct { - memDB *memdb.MemDB -} - -var ( - NotFound = errors.New("data not found") -) - -func NewMemDB(config *types.Configuration) (*DB, error) { - db, err := memdb.NewMemDB(schema) - if err != nil { - return nil, err - } - - txn := db.Txn(true) - - common.NormalizeConfiguration(config) - - for _, service := range config.Services { - err = txn.Insert("services", service) - if err != nil { - return nil, err - } - } - - for _, routes := range config.Routes { - err = txn.Insert("routes", routes) - if err != nil { - return nil, err - } - } - - for _, consumers := range config.Consumers { - err = txn.Insert("consumers", consumers) - if err != nil { - return nil, err - } - } - - for _, ssls := range config.SSLs { - err = txn.Insert("ssls", ssls) - if err != nil { - return nil, err - } - } - - for _, globalRule := range config.GlobalRules { - err = txn.Insert("global_rules", globalRule) - if err != nil { - return nil, err - } - } - - for _, pluginConfig := range config.PluginConfigs { - err = txn.Insert("plugin_configs", pluginConfig) - if err != nil { - return nil, err - } - } - - for _, consumerGroup := range config.ConsumerGroups { - err = txn.Insert("consumer_groups", consumerGroup) - if err != nil { - return nil, err - } - } - - for _, pluginMetadata := range config.PluginMetadatas { - err = txn.Insert("plugin_metadatas", pluginMetadata) - if err != nil { - return nil, err - } - } - - for _, streamRoute := range config.StreamRoutes { - err = txn.Insert("stream_routes", streamRoute) - if err != nil { - return nil, err - } - } - - for _, upstream := range config.Upstreams { - err = txn.Insert("upstreams", upstream) - if err != nil { - return nil, err - } - } - - txn.Commit() - - return &DB{memDB: db}, nil -} - -func getByID[T any](db *DB, table, id string) (*T, error) { - obj, err := db.memDB.Txn(false).First(table, "id", id) - if err != nil { - return nil, err - } - - if obj == nil { - return nil, NotFound - } - - return obj.(*T), err -} - -func (db *DB) GetServiceByID(id string) (*types.Service, error) { - return getByID[types.Service](db, "services", id) -} - -func (db *DB) GetRouteByID(id string) (*types.Route, error) { - return getByID[types.Route](db, "routes", id) -} - -func (db *DB) GetConsumerByID(username string) (*types.Consumer, error) { - return getByID[types.Consumer](db, "consumers", username) -} - -func (db *DB) GetSSLByID(id string) (*types.SSL, error) { - return getByID[types.SSL](db, "ssls", id) -} - -func (db *DB) GetGlobalRuleByID(id string) (*types.GlobalRule, error) { - return getByID[types.GlobalRule](db, "global_rules", id) -} - -func (db *DB) GetPluginConfigByID(id string) (*types.PluginConfig, error) { - return getByID[types.PluginConfig](db, "plugin_configs", id) -} - -func (db *DB) GetConsumerGroupByID(id string) (*types.ConsumerGroup, error) { - return getByID[types.ConsumerGroup](db, "consumer_groups", id) -} - -func (db *DB) GetPluginMetadataByID(id string) (*types.PluginMetadata, error) { - return getByID[types.PluginMetadata](db, "plugin_metadatas", id) -} - -func (db *DB) GetStreamRouteByID(id string) (*types.StreamRoute, error) { - return getByID[types.StreamRoute](db, "stream_routes", id) -} - -func (db *DB) GetUpstreamByID(id string) (*types.Upstream, error) { - return getByID[types.Upstream](db, "upstreams", id) -} diff --git a/internal/pkg/db/memdb_test.go b/internal/pkg/db/memdb_test.go deleted file mode 100644 index 23943e73..00000000 --- a/internal/pkg/db/memdb_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package db - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -var ( - svc = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "svc.example.com", - }, - Labels: map[string]string{ - "label1": "v1", - "label2": "v2", - }, - Upstream: &types.Upstream{ - Name: "upstream1", - Nodes: []types.UpstreamNode{ - { - Host: "httpbin.org", - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Labels: map[string]string{ - "label1": "v1", - "label2": "v2", - }, - Methods: []string{http.MethodGet}, - Uris: []string{"/get"}, - ServiceID: "svc", - } -) - -func TestGetServiceByID(t *testing.T) { - // Test Case 1: get service by id - config := types.Configuration{ - Services: []*types.Service{svc}, - } - - db, _ := NewMemDB(&config) - service, err := db.GetServiceByID("svc") - assert.Nil(t, err, "check the error") - assert.Equal(t, svc, service, "check the service") - - // Test Case 2: get service by id (not found) - _, err = db.GetServiceByID("not-found") - assert.Equal(t, NotFound, err, "check the error") - - // Test Case 3: Service don't have id - svc1 := *svc - svc1.ID = "" - config = types.Configuration{ - Services: []*types.Service{&svc1}, - } - - db, _ = NewMemDB(&config) - service, err = db.GetServiceByID("svc") - assert.Nil(t, err, "check the error") - assert.Equal(t, svc, service, "check the service") -} - -func TestGetRouteByID(t *testing.T) { - // Test Case 1: get route by id - config := types.Configuration{ - Routes: []*types.Route{route}, - } - - db, _ := NewMemDB(&config) - route1, err := db.GetRouteByID("route") - assert.Nil(t, err, "check the error") - assert.Equal(t, route, route1, "check the route") - - // Test Case 2: get route by id (not found) - _, err = db.GetRouteByID("not-found") - assert.Equal(t, NotFound, err, "check the error") - - // Test Case 3: Route don't have id - route.ID = "" - config = types.Configuration{ - Routes: []*types.Route{route}, - } - - db, _ = NewMemDB(&config) - route1, err = db.GetRouteByID("route") - assert.Nil(t, err, "check the error") - assert.Equal(t, route, route1, "check the route") -} diff --git a/internal/pkg/differ/differ.go b/internal/pkg/differ/differ.go deleted file mode 100644 index c974baa7..00000000 --- a/internal/pkg/differ/differ.go +++ /dev/null @@ -1,695 +0,0 @@ -package differ - -import ( - "fmt" - "reflect" - "sort" - - "github.com/api7/adc/internal/pkg/db" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/data" -) - -var _orderIndex = -1 - -func _order() int { - _orderIndex += 1 - return _orderIndex -} - -func _key(typ data.ResourceType, option int) string { - return fmt.Sprintf("%s:%d", typ, option) -} - -// order is the events order to ensure the data dependency. Higher takes priority -// stream route requires: upstream, service. -// service requires: upstream (shouldn't) -// route requires: service, plugin config, consumer (soft require), upstream (shouldn't, use service instead) -// consumer requires: consumer group -// The dependent resources should be created/updated first but deleted later -var order = map[string]int{ - _key(data.UpstreamResourceType, data.DeleteOption): _order(), - _key(data.ServiceResourceType, data.DeleteOption): _order(), - _key(data.PluginConfigResourceType, data.DeleteOption): _order(), - _key(data.ConsumerGroupResourceType, data.DeleteOption): _order(), - _key(data.ConsumerResourceType, data.DeleteOption): _order(), - _key(data.StreamRouteResourceType, data.DeleteOption): _order(), - _key(data.RouteResourceType, data.DeleteOption): _order(), - - _key(data.RouteResourceType, data.UpdateOption): _order(), - _key(data.StreamRouteResourceType, data.UpdateOption): _order(), - _key(data.ServiceResourceType, data.UpdateOption): _order(), - _key(data.UpstreamResourceType, data.UpdateOption): _order(), - _key(data.PluginConfigResourceType, data.UpdateOption): _order(), - _key(data.ConsumerResourceType, data.UpdateOption): _order(), - _key(data.ConsumerGroupResourceType, data.UpdateOption): _order(), - - _key(data.RouteResourceType, data.CreateOption): _order(), - _key(data.StreamRouteResourceType, data.CreateOption): _order(), - _key(data.ServiceResourceType, data.CreateOption): _order(), - _key(data.UpstreamResourceType, data.CreateOption): _order(), - _key(data.PluginConfigResourceType, data.CreateOption): _order(), - _key(data.ConsumerResourceType, data.CreateOption): _order(), - _key(data.ConsumerGroupResourceType, data.CreateOption): _order(), - - // no dependency - _key(data.SSLResourceType, data.DeleteOption): _order(), - _key(data.SSLResourceType, data.CreateOption): _order(), - _key(data.SSLResourceType, data.UpdateOption): _order(), - _key(data.GlobalRuleResourceType, data.DeleteOption): _order(), - _key(data.GlobalRuleResourceType, data.CreateOption): _order(), - _key(data.GlobalRuleResourceType, data.UpdateOption): _order(), - _key(data.PluginMetadataResourceType, data.DeleteOption): _order(), - _key(data.PluginMetadataResourceType, data.CreateOption): _order(), - _key(data.PluginMetadataResourceType, data.UpdateOption): _order(), -} - -// Differ is the object of comparing two configurations. -type Differ struct { - localDB *db.DB - localConfig *types.Configuration - remoteConfig *types.Configuration -} - -// NewDiffer creates a new Differ object. -func NewDiffer(local, remote *types.Configuration) (*Differ, error) { - db, err := db.NewMemDB(local) - if err != nil { - return nil, err - } - - return &Differ{ - localDB: db, - localConfig: local, - remoteConfig: remote, - }, nil -} - -// sortEvents sorts events descending, higher priority events will be executed first -func sortEvents(events []*data.Event) { - sort.Slice(events, func(i, j int) bool { - return order[_key(events[i].ResourceType, events[i].Option)] > order[_key(events[j].ResourceType, events[j].Option)] - }) -} - -// Diff compares the local configuration and remote configuration, and returns the events. -func (d *Differ) Diff() ([]*data.Event, error) { - var events []*data.Event - var err error - - serviceEvents, err := d.diffServices() - if err != nil { - return nil, err - } - - routeEvents, err := d.diffRoutes() - if err != nil { - return nil, err - } - - consumerEvents, err := d.diffConsumers() - if err != nil { - return nil, err - } - - sslEvents, err := d.diffSSLs() - if err != nil { - return nil, err - } - - globalRuleEvents, err := d.diffGlobalRules() - if err != nil { - return nil, err - } - - pluginConfigEvents, err := d.diffPluginConfigs() - if err != nil { - return nil, err - } - - consumerGroupEvents, err := d.diffConsumerGroups() - if err != nil { - return nil, err - } - - pluginMetadataEvents, err := d.diffPluginMetadata() - if err != nil { - return nil, err - } - - streamRouteEvents, err := d.diffStreamRoutes() - if err != nil { - return nil, err - } - - upstreamEvents, err := d.diffUpstreams() - if err != nil { - return nil, err - } - - events = append(events, serviceEvents...) - events = append(events, routeEvents...) - events = append(events, consumerEvents...) - events = append(events, sslEvents...) - events = append(events, globalRuleEvents...) - events = append(events, pluginConfigEvents...) - events = append(events, pluginMetadataEvents...) - events = append(events, consumerGroupEvents...) - events = append(events, streamRouteEvents...) - events = append(events, upstreamEvents...) - - sortEvents(events) - - return events, nil -} - -// diffService compares the services between local and remote. -func (d *Differ) diffServices() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteSvc := range d.remoteConfig.Services { - localSvc, err := d.localDB.GetServiceByID(remoteSvc.ID) - if err != nil { - // If we can't find the service in local, it means the service should be deleted. - // So we add a delete event and the value is the service from remote. - if err == db.NotFound { - e := data.Event{ - ResourceType: data.ServiceResourceType, - Option: data.DeleteOption, - OldValue: remoteSvc, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localSvc.ID] = true - // If the service is equal, we don't need to add an event. - // Else, we use the local service to update the remote service. - if equal := reflect.DeepEqual(localSvc, remoteSvc); equal { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.ServiceResourceType, - Option: data.UpdateOption, - OldValue: remoteSvc, - Value: localSvc, - }) - } - - // If the service is not in the remote configuration, it means the service should be created. - for _, service := range d.localConfig.Services { - if mark[service.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.ServiceResourceType, - Option: data.CreateOption, - Value: service, - }) - } - - return events, nil -} - -// diffRoutes compares the routes between local and remote. -func (d *Differ) diffRoutes() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteRoute := range d.remoteConfig.Routes { - localRoute, err := d.localDB.GetRouteByID(remoteRoute.ID) - if err != nil { - // If we can't find the route in local, it means the route should be deleted. - // So we add a delete event and the value is the route from remote. - if err == db.NotFound { - e := data.Event{ - ResourceType: data.RouteResourceType, - Option: data.DeleteOption, - OldValue: remoteRoute, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localRoute.ID] = true - // If the route is equal, we don't need to add an event. - // Else, we use the local routes to update the remote routes. - if equal := reflect.DeepEqual(localRoute, remoteRoute); equal { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.RouteResourceType, - Option: data.UpdateOption, - OldValue: remoteRoute, - Value: localRoute, - }) - } - - // If the route is not in the remote configuration, it means the route should be created. - for _, route := range d.localConfig.Routes { - if mark[route.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.RouteResourceType, - Option: data.CreateOption, - Value: route, - }) - } - - return events, nil -} - -// diffConsumers compares the consumers between local and remote. -func (d *Differ) diffConsumers() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteConsumers := range d.remoteConfig.Consumers { - localConsumer, err := d.localDB.GetConsumerByID(remoteConsumers.Username) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.ConsumerResourceType, - Option: data.DeleteOption, - OldValue: remoteConsumers, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localConsumer.Username] = true - // skip when equals - if equal := reflect.DeepEqual(localConsumer, remoteConsumers); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.ConsumerResourceType, - Option: data.UpdateOption, - OldValue: remoteConsumers, - Value: localConsumer, - }) - } - - // only in local, create - for _, consumer := range d.localConfig.Consumers { - if mark[consumer.Username] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.ConsumerResourceType, - Option: data.CreateOption, - Value: consumer, - }) - } - - return events, nil -} - -// diffSSLs compares the SSLs between local and remote. -func (d *Differ) diffSSLs() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteSSL := range d.remoteConfig.SSLs { - localSSL, err := d.localDB.GetSSLByID(remoteSSL.ID) - if err != nil { - // we can't find the route in local, it means the route should be deleted. - if err == db.NotFound { - e := data.Event{ - ResourceType: data.SSLResourceType, - Option: data.DeleteOption, - OldValue: remoteSSL, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localSSL.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localSSL, remoteSSL); equal { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.SSLResourceType, - Option: data.UpdateOption, - OldValue: remoteSSL, - Value: localSSL, - }) - } - - // only in local, create - for _, ssl := range d.localConfig.SSLs { - if mark[ssl.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.SSLResourceType, - Option: data.CreateOption, - Value: ssl, - }) - } - - return events, nil -} - -// diffGlobalRules compares the GlobalRules between local and remote. -func (d *Differ) diffGlobalRules() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteGlobalRule := range d.remoteConfig.GlobalRules { - localGlobalRule, err := d.localDB.GetGlobalRuleByID(remoteGlobalRule.ID) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.GlobalRuleResourceType, - Option: data.DeleteOption, - OldValue: remoteGlobalRule, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localGlobalRule.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localGlobalRule, remoteGlobalRule); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.GlobalRuleResourceType, - Option: data.UpdateOption, - OldValue: remoteGlobalRule, - Value: localGlobalRule, - }) - } - - // only in local, create - for _, globalRule := range d.localConfig.GlobalRules { - if mark[globalRule.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.GlobalRuleResourceType, - Option: data.CreateOption, - Value: globalRule, - }) - } - - return events, nil -} - -// diffPluginConfigs compares the PluginConfigs between local and remote. -func (d *Differ) diffPluginConfigs() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remotePluginConfig := range d.remoteConfig.PluginConfigs { - localPluginConfig, err := d.localDB.GetPluginConfigByID(remotePluginConfig.ID) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.PluginConfigResourceType, - Option: data.DeleteOption, - OldValue: remotePluginConfig, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localPluginConfig.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localPluginConfig, remotePluginConfig); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.PluginConfigResourceType, - Option: data.UpdateOption, - OldValue: remotePluginConfig, - Value: localPluginConfig, - }) - } - - // only in local, create - for _, pluginConfig := range d.localConfig.PluginConfigs { - if mark[pluginConfig.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.PluginConfigResourceType, - Option: data.CreateOption, - Value: pluginConfig, - }) - } - - return events, nil -} - -// diffConsumerGroups compares the ConsumerGroups between local and remote. -func (d *Differ) diffConsumerGroups() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteConsumerGroup := range d.remoteConfig.ConsumerGroups { - localConsumerGroup, err := d.localDB.GetConsumerGroupByID(remoteConsumerGroup.ID) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.ConsumerGroupResourceType, - Option: data.DeleteOption, - OldValue: remoteConsumerGroup, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localConsumerGroup.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localConsumerGroup, remoteConsumerGroup); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.ConsumerGroupResourceType, - Option: data.UpdateOption, - OldValue: remoteConsumerGroup, - Value: localConsumerGroup, - }) - } - - // only in local, create - for _, consumerGroup := range d.localConfig.ConsumerGroups { - if mark[consumerGroup.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.ConsumerGroupResourceType, - Option: data.CreateOption, - Value: consumerGroup, - }) - } - - return events, nil -} - -// diffPluginMetadata compares the global_rules between local and remote. -func (d *Differ) diffPluginMetadata() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remotePluginMetadata := range d.remoteConfig.PluginMetadatas { - localPluginMetadata, err := d.localDB.GetPluginMetadataByID(remotePluginMetadata.ID) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.PluginMetadataResourceType, - Option: data.DeleteOption, - OldValue: remotePluginMetadata, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localPluginMetadata.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localPluginMetadata, remotePluginMetadata); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.PluginMetadataResourceType, - Option: data.UpdateOption, - OldValue: remotePluginMetadata, - Value: localPluginMetadata, - }) - } - - // only in local, create - for _, pluginMetadata := range d.localConfig.PluginMetadatas { - if mark[pluginMetadata.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.PluginMetadataResourceType, - Option: data.CreateOption, - Value: pluginMetadata, - }) - } - - return events, nil -} - -// diffStreamRoutes compares the StreamRoutes between local and remote. -func (d *Differ) diffStreamRoutes() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteStreamRoute := range d.remoteConfig.StreamRoutes { - localStreamRoute, err := d.localDB.GetStreamRouteByID(remoteStreamRoute.ID) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.StreamRouteResourceType, - Option: data.DeleteOption, - OldValue: remoteStreamRoute, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localStreamRoute.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localStreamRoute, remoteStreamRoute); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.StreamRouteResourceType, - Option: data.UpdateOption, - OldValue: remoteStreamRoute, - Value: localStreamRoute, - }) - } - - // only in local, create - for _, streamRoute := range d.localConfig.StreamRoutes { - if mark[streamRoute.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.StreamRouteResourceType, - Option: data.CreateOption, - Value: streamRoute, - }) - } - - return events, nil -} - -// diffUpstreams compares the Upstreams between local and remote. -func (d *Differ) diffUpstreams() ([]*data.Event, error) { - var events []*data.Event - var mark = make(map[string]bool) - - for _, remoteUpstream := range d.remoteConfig.Upstreams { - localUpstream, err := d.localDB.GetUpstreamByID(remoteUpstream.ID) - if err != nil { - // we can't find in local config, should delete it - if err == db.NotFound { - e := data.Event{ - ResourceType: data.UpstreamResourceType, - Option: data.DeleteOption, - OldValue: remoteUpstream, - } - events = append(events, &e) - continue - } - - return nil, err - } - - mark[localUpstream.ID] = true - // skip when equals - if equal := reflect.DeepEqual(localUpstream, remoteUpstream); equal { - continue - } - - // otherwise update - events = append(events, &data.Event{ - ResourceType: data.UpstreamResourceType, - Option: data.UpdateOption, - OldValue: remoteUpstream, - Value: localUpstream, - }) - } - - // only in local, create - for _, upstream := range d.localConfig.Upstreams { - if mark[upstream.ID] { - continue - } - - events = append(events, &data.Event{ - ResourceType: data.UpstreamResourceType, - Option: data.CreateOption, - Value: upstream, - }) - } - - return events, nil -} diff --git a/internal/pkg/differ/differ_test.go b/internal/pkg/differ/differ_test.go deleted file mode 100644 index d3fa9990..00000000 --- a/internal/pkg/differ/differ_test.go +++ /dev/null @@ -1,346 +0,0 @@ -package differ - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/data" -) - -var ( - svc = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "svc.example.com", - }, - Labels: map[string]string{ - "label1": "v1", - "label2": "v2", - }, - Upstream: &types.Upstream{ - Name: "upstream1", - Nodes: []types.UpstreamNode{ - { - Host: "httpbin.org", - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Labels: map[string]string{ - "label1": "v1", - "label2": "v2", - }, - Methods: []string{http.MethodGet}, - Uris: []string{"/get"}, - ServiceID: "svc", - } -) - -func TestSortEvents(t *testing.T) { - events := []*data.Event{ - { - ResourceType: data.RouteResourceType, - Option: data.CreateOption, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.CreateOption, - }, - { - ResourceType: data.RouteResourceType, - Option: data.DeleteOption, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.DeleteOption, - }, - { - ResourceType: data.RouteResourceType, - Option: data.UpdateOption, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.UpdateOption, - }, - } - - sortEvents(events) - assert.Equal(t, []*data.Event{ - { - ResourceType: data.ServiceResourceType, - Option: data.CreateOption, - }, - { - ResourceType: data.RouteResourceType, - Option: data.CreateOption, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.UpdateOption, - }, - { - ResourceType: data.RouteResourceType, - Option: data.UpdateOption, - }, - { - ResourceType: data.RouteResourceType, - Option: data.DeleteOption, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.DeleteOption, - }, - }, events, "check the content of sorted events") - -} - -func TestDiff(t *testing.T) { - // Test case 1: delete events - localConfig := &types.Configuration{ - Services: []*types.Service{}, - Routes: []*types.Route{route}, - } - - route1 := *route - route1.ID = "route1" - route1.Name = "route1" - remoteConfig := &types.Configuration{ - Services: []*types.Service{svc}, - Routes: []*types.Route{&route1}, - } - - differ, _ := NewDiffer(localConfig, remoteConfig) - events, _ := differ.Diff() - assert.Equal(t, 3, len(events), "check the number of delete events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.RouteResourceType, - Option: data.CreateOption, - Value: route, - }, - { - ResourceType: data.RouteResourceType, - Option: data.DeleteOption, - OldValue: &route1, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.DeleteOption, - OldValue: svc, - }, - }, events, "check the content of delete events") -} - -func TestDiffServices(t *testing.T) { - // Test case 1: delete events - localConfig := &types.Configuration{ - Services: []*types.Service{}, - } - remoteConfig := &types.Configuration{ - Services: []*types.Service{svc}, - } - - differ, _ := NewDiffer(localConfig, remoteConfig) - events, _ := differ.diffServices() - assert.Equal(t, 1, len(events), "check the number of delete events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.ServiceResourceType, - Option: data.DeleteOption, - OldValue: svc, - }, - }, events, "check the content of delete events") - - // Test case 2: update events - localConfig = &types.Configuration{ - Services: []*types.Service{svc}, - } - svc1 := *svc - svc1.Name = "svc1" - svc1.Hosts[0] = "svc1.example.com" - remoteConfig = &types.Configuration{ - Services: []*types.Service{&svc1}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffServices() - assert.Equal(t, 1, len(events), "check the number of update events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.ServiceResourceType, - Option: data.UpdateOption, - OldValue: &svc1, - Value: svc, - }, - }, events, "check the content of update events") - - // Test case 3: create events - localConfig = &types.Configuration{ - Services: []*types.Service{svc}, - } - remoteConfig = &types.Configuration{ - Services: []*types.Service{}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffServices() - assert.Equal(t, 1, len(events), "check the number of create events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.ServiceResourceType, - Option: data.CreateOption, - Value: svc, - }, - }, events, "check the content of create events") - - // Test case 4: no events - localConfig = &types.Configuration{ - Services: []*types.Service{svc}, - } - - remoteConfig = &types.Configuration{ - Services: []*types.Service{svc}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffServices() - assert.Equal(t, 0, len(events), "check the number of no events") - - // Test case 5: delete and create events - localConfig = &types.Configuration{ - Services: []*types.Service{svc}, - } - svc1 = *svc - svc1.ID = "svc1" - svc1.Name = "svc1" - svc1.Hosts[0] = "svc1.example.com" - remoteConfig = &types.Configuration{ - Services: []*types.Service{&svc1}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffServices() - assert.Equal(t, 2, len(events), "check the number of delete and create events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.ServiceResourceType, - Option: data.DeleteOption, - OldValue: &svc1, - }, - { - ResourceType: data.ServiceResourceType, - Option: data.CreateOption, - Value: svc, - }, - }, events, "check the content of delete and create events") -} - -func TestDifferRoutes(t *testing.T) { - // Test case 1: delete events - localConfig := &types.Configuration{ - Routes: []*types.Route{}, - } - remoteConfig := &types.Configuration{ - Routes: []*types.Route{route}, - } - - differ, _ := NewDiffer(localConfig, remoteConfig) - events, _ := differ.diffRoutes() - assert.Equal(t, 1, len(events), "check the number of delete events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.RouteResourceType, - Option: data.DeleteOption, - OldValue: route, - }, - }, events, "check the content of delete events") - - // Test case 2: update events - localConfig = &types.Configuration{ - Routes: []*types.Route{route}, - } - route1 := *route - route1.Name = "route1" - remoteConfig = &types.Configuration{ - Routes: []*types.Route{&route1}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffRoutes() - assert.Equal(t, 1, len(events), "check the number of update events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.RouteResourceType, - Option: data.UpdateOption, - OldValue: &route1, - Value: route, - }, - }, events, "check the content of update events") - - // Test case 3: create events - localConfig = &types.Configuration{ - Routes: []*types.Route{route}, - } - remoteConfig = &types.Configuration{ - Routes: []*types.Route{}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffRoutes() - assert.Equal(t, 1, len(events), "check the number of create events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.RouteResourceType, - Option: data.CreateOption, - Value: route, - }, - }, events, "check the content of create events") - - // Test case 4: no events - localConfig = &types.Configuration{ - Routes: []*types.Route{}, - } - - remoteConfig = &types.Configuration{ - Routes: []*types.Route{}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffRoutes() - assert.Equal(t, 0, len(events), "check the number of no events") - - // Test case 5: delete and create events - localConfig = &types.Configuration{ - Routes: []*types.Route{route}, - } - route1 = *route - route1.ID = "route1" - route1.Name = "route1" - remoteConfig = &types.Configuration{ - Routes: []*types.Route{&route1}, - } - - differ, _ = NewDiffer(localConfig, remoteConfig) - events, _ = differ.diffRoutes() - assert.Equal(t, 2, len(events), "check the number of delete and create events") - assert.Equal(t, []*data.Event{ - { - ResourceType: data.RouteResourceType, - Option: data.DeleteOption, - OldValue: &route1, - }, - { - ResourceType: data.RouteResourceType, - Option: data.CreateOption, - Value: route, - }, - }, events, "check the content of delete and create events") -} diff --git a/internal/pkg/openapi2apisix/openapi2apisix.go b/internal/pkg/openapi2apisix/openapi2apisix.go deleted file mode 100644 index 316b544b..00000000 --- a/internal/pkg/openapi2apisix/openapi2apisix.go +++ /dev/null @@ -1,266 +0,0 @@ -package openapi2apisix - -import ( - "context" - "fmt" - "net/url" - "regexp" - "sort" - "strconv" - "strings" - - "github.com/getkin/kin-openapi/openapi3" - "github.com/mozillazg/go-slugify" - "github.com/pkg/errors" - - apitypes "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" -) - -var ( - _pathVariableRegex = regexp.MustCompile(`{[^}]+}`) -) - -func convertPathVariables(path string) string { - return _pathVariableRegex.ReplaceAllStringFunc(path, func(match string) string { - return ":" + match[1:len(match)-1] - }) -} - -// Slugify converts a name to a valid name by removing and replacing unallowed characters -// and sanitizing non-latin characters. Multiple inputs will be concatenated using '_'. -func Slugify(name ...string) string { - for i, elem := range name { - name[i] = slugify.Slugify(elem) - } - - return strings.Join(name, "_") -} - -// Convert converts OAS to API Service and Routes -func Convert(ctx context.Context, oas []byte) (*apitypes.Configuration, error) { - - result := apitypes.Configuration{} - - doc, err := OAS(oas).LoadOpenAPI(ctx) - if err != nil { - return nil, errors.Wrap(LoadOpenAPIError, err.Error()) - } - - serverUris, varUris, err := parseServerUris(&doc.Servers) - if err != nil { - return nil, errors.Wrap(LoadOpenAPIError, err.Error()) - } - - ups := &apitypes.Upstream{ - Nodes: []apitypes.UpstreamNode{ - apitypes.UpstreamNode{}, - }, - } - - if len(serverUris)+len(varUris) > 0 { - ups = createUpstream(serverUris, varUris) - } - - // handle services - upsName := Slugify("Upstream for ", doc.Info.Title) - ups.Name = upsName - ups.ID = common.GenID(ups.Name) - - result.Services = []*apitypes.Service{ - { - ID: common.GenID(doc.Info.Title), - Name: doc.Info.Title, - Description: doc.Info.Description, - //Labels: getLabelsByTags(doc.Tags), - Upstream: ups, - }, - } - - // handle routes - routes := createRoutes(doc, result.Services[0].ID) - result.Routes = routes - - return &result, nil -} - -func createRoutes(doc *openapi3.T, serviceID string) []*apitypes.Route { - // create a sorted array of paths, to be deterministic in our output order - sortedPaths := make([]string, len(doc.Paths)) - i := 0 - for path := range doc.Paths { - sortedPaths[i] = path - i++ - } - sort.Strings(sortedPaths) - - var routes []*apitypes.Route - for _, path := range sortedPaths { - pathItem := doc.Paths[path] - operations := pathItem.Operations() - sortedMethods := make([]string, len(operations)) - i := 0 - for method := range operations { - sortedMethods[i] = method - i++ - } - sort.Strings(sortedMethods) - for _, method := range sortedMethods { - operation := operations[method] - // route name - routeName := operation.OperationID - if routeName == "" { - routeName = Slugify(doc.Info.Title, method, path) - } - routeDescription := operation.Summary - if routeDescription == "" { - routeDescription = operation.Description - } - routePath := convertPathVariables(path) - // var tags []string - // for _, tag := range operation.Tags { - // tags = append(tags, Slugify(tag)) - // } - route := apitypes.Route{ - ID: common.GenID(routeName), - Name: routeName, - Description: routeDescription, - // Labels: tags, - Methods: []string{method}, - Uris: []string{routePath}, - ServiceID: serviceID, - } - routes = append(routes, &route) - } - } - return routes -} - -//nolint:unused -func getLabelsByTags(tags openapi3.Tags) apitypes.Labels { - labels := apitypes.Labels{} - for i, tag := range tags { - labels[strconv.Itoa(i)] = Slugify(tag.Name) - } - - return labels -} - -//nolint:unused -func getServiceProtocols(targets []*url.URL) []string { - protocols := map[string]struct{}{} - for _, target := range targets { - protocols[strings.ToUpper(target.Scheme)] = struct{}{} - } - var result []string - for protocol := range protocols { - result = append(result, protocol) - } - - return result -} - -func createUpstream(targets []*url.URL, varUris []string) *apitypes.Upstream { - var upstreamNodes apitypes.UpstreamNodes - for _, target := range targets { - upstreamNodes = append(upstreamNodes, apitypes.UpstreamNode{ - Host: target.Hostname(), - Port: getURLPort(target), - Weight: 100, - }) - } - for _, uri := range varUris { - host := strings.ReplaceAll(uri, "https://", "") - host = strings.ReplaceAll(host, "http://", "") - upstreamNodes = append(upstreamNodes, apitypes.UpstreamNode{ - Host: host, - Port: getURLPortFromString(uri), - Weight: 100, - }) - } - upstream := apitypes.Upstream{ - Scheme: targets[0].Scheme, - Nodes: upstreamNodes, - Timeout: &apitypes.UpstreamTimeout{ - Connect: 60, - Send: 60, - Read: 60, - }, - PassHost: apitypes.UpstreamPassHostPass, - } - - return &upstream -} - -// parseServerUris parses the server uri's after rendering the template variables. -// result will always have at least 1 entry, but not necessarily a hostname/port/scheme -func parseServerUris(servers *openapi3.Servers) ([]*url.URL, []string, error) { - var ( - targets []*url.URL - ignored []string - ) - - if servers == nil || len(*servers) == 0 { - uriObject, _ := url.ParseRequestURI("/") // path '/' is the default for empty server blocks - targets = make([]*url.URL, 1) - targets[0] = uriObject - } else { - targets = []*url.URL{} - - for _, server := range *servers { - uriString := server.URL - for name, svar := range server.Variables { - uriString = strings.ReplaceAll(uriString, "{"+name+"}", svar.Default) - } - if strings.Contains(uriString, "{") && strings.Contains(uriString, "}") { - //color.Yellow("Server url with variable " + uriString + " ignored") - ignored = append(ignored, uriString) - continue - } - - uriObject, err := url.ParseRequestURI(uriString) - if err != nil { - return targets, ignored, fmt.Errorf("failed to parse uri '%s'; %w", uriString, err) - } - - if uriObject.Path == "" { - uriObject.Path = "/" // path '/' is the default - } - - targets = append(targets, uriObject) - //targets[i] = uriObject - } - } - - return targets, ignored, nil -} - -func getURLPort(u *url.URL) int { - var port int - if u.Port() != "" { - port, _ = strconv.Atoi(u.Port()) - } else { - if u.Scheme == "https" { - port = 443 - } else { - port = 80 - } - } - - return port -} - -func getURLPortFromString(s string) int { - port := 80 - if strings.HasPrefix(s, "https://") { - port = 443 - } - - s = strings.ReplaceAll(s, "{", "") - s = strings.ReplaceAll(s, "}", "") - uri, err := url.ParseRequestURI(s) - if err != nil { - return port - } - return getURLPort(uri) -} diff --git a/internal/pkg/openapi2apisix/openapi2apisix_test.go b/internal/pkg/openapi2apisix/openapi2apisix_test.go deleted file mode 100644 index 6e8848a9..00000000 --- a/internal/pkg/openapi2apisix/openapi2apisix_test.go +++ /dev/null @@ -1,382 +0,0 @@ -package openapi2apisix - -import ( - "context" - _ "embed" - "testing" - - "github.com/stretchr/testify/assert" - - apitypes "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" -) - -func TestSlugify(t *testing.T) { - type args struct { - name []string - } - tests := []struct { - name string - args args - want string - }{ - { - name: "one", - args: args{ - name: []string{"Pet Store"}, - }, - want: "pet-store", - }, - { - name: "path1", - args: args{ - name: []string{"GET", "/api/services"}, - }, - want: "get_api-services", - }, - { - name: "path2", - args: args{ - name: []string{"POST", "/api/services"}, - }, - want: "post_api-services", - }, - { - name: "path3", - args: args{ - name: []string{"PUT", "/api/services/{service_id}"}, - }, - want: "put_api-services-service-id", - }, - { - name: "path4", - args: args{ - name: []string{"DELETE", "/api/services/{service_id}"}, - }, - want: "delete_api-services-service-id", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := Slugify(tt.args.name...); got != tt.want { - t.Errorf("Slugify() = %v, want %v", got, tt.want) - } - }) - } -} - -var ( - //go:embed testdata/Postman-API101.yaml - postman101 []byte - //go:embed testdata/operationId.yaml - operationIdTest []byte - //go:embed testdata/tags.yaml - tagsTest []byte - //go:embed testdata/tags.json - tagsJsonTest []byte - //go:embed testdata/urlVariables.yaml - urlVariables []byte -) - -func TestConvert(t *testing.T) { - type args struct { - content []byte - } - tests := []struct { - name string - args args - want *apitypes.Configuration - wantErr bool - }{ - { - name: "postman101", - args: args{ - content: postman101, - }, - want: &apitypes.Configuration{ - Services: []*apitypes.Service{ - { - Name: "API 101", - Description: `API 101 template for learning API request basics. Follow along with the webinar / video or just open the first request and hit **Send**!`, - Upstream: &apitypes.Upstream{ - Scheme: "https", - Nodes: []apitypes.UpstreamNode{ - { - Host: "api-101.glitch.me", - Port: 443, - Weight: 100, - }, - { - Host: "{{apiurl}}", - Port: 80, - Weight: 100, - }, - }, - Timeout: &apitypes.UpstreamTimeout{ - Connect: 60, - Send: 60, - Read: 60, - }, - PassHost: apitypes.UpstreamPassHostPass, - }, - // Labels: make([]apitypes.Labels, 0), - }, - }, - Routes: []*apitypes.Route{ - { - Name: "api-101_get_customer", - Description: "Get one customer", - // Labels: []string{"default"}, - Methods: []string{"GET"}, - Uris: []string{"/customer"}, - }, - { - Name: "api-101_post_customer", - Description: "Add new customer", - // Labels: []string{"default"}, - Methods: []string{"POST"}, - Uris: []string{"/customer"}, - }, - { - Name: "api-101_delete_customer-customer-id", - Description: "Remove customer", - // Labels: []string{"default"}, - Methods: []string{"DELETE"}, - Uris: []string{"/customer/:customer_id"}, - }, - { - Name: "api-101_put_customer-customer-id", - Description: "Update customer", - // Labels: []string{"default"}, - Methods: []string{"PUT"}, - Uris: []string{"/customer/:customer_id"}, - }, - { - Name: "api-101_get_customers", - Description: "Get all customers", - // Labels: []string{"default"}, - Methods: []string{"GET"}, - Uris: []string{"/customers"}, - }, - }, - }, - wantErr: false, - }, - { - name: "operationId", - args: args{ - content: operationIdTest, - }, - want: &apitypes.Configuration{ - Services: []*apitypes.Service{ - { - Name: "API 101", - Description: "modify operationId", - Upstream: &apitypes.Upstream{ - Scheme: "https", - Nodes: []apitypes.UpstreamNode{ - { - Host: "api-101.glitch.me", - Port: 443, - Weight: 100, - }, - }, - Timeout: &apitypes.UpstreamTimeout{ - Connect: 60, - Send: 60, - Read: 60, - }, - PassHost: apitypes.UpstreamPassHostPass, - }, - // Labels: make([]apitypes.Labels, 0), - }, - }, - Routes: []*apitypes.Route{ - { - Name: "update Customer", - Description: "Update customer", - // Labels: []string{"default"}, - Methods: []string{"PUT"}, - Uris: []string{"/customer/:customer_id"}, - }, - { - Name: "getCustomers", - Description: "Get all customers", - // Labels: []string{"default"}, - Methods: []string{"GET"}, - Uris: []string{"/customers"}, - }, - }, - }, - wantErr: false, - }, - { - name: "tags", - args: args{ - content: tagsTest, - }, - want: &apitypes.Configuration{ - Services: []*apitypes.Service{ - { - Name: "API 101", - Description: "modify operationId", - Upstream: &apitypes.Upstream{ - Scheme: "https", - Nodes: []apitypes.UpstreamNode{ - { - Host: "api-101.glitch.me", - Port: 443, - Weight: 100, - }, - }, - Timeout: &apitypes.UpstreamTimeout{ - Connect: 60, - Send: 60, - Read: 60, - }, - PassHost: apitypes.UpstreamPassHostPass, - }, - // Labels: apitypes.Labels{ - // "web-spider", "blockchain", - // }, - }, - }, - Routes: []*apitypes.Route{ - { - Name: "getCustomers", - Description: "Get all customers", - // Labels: []string{"default", "customer"}, - Methods: []string{"GET"}, - Uris: []string{"/customers"}, - }, - }, - }, - wantErr: false, - }, - { - name: "tags -> json file", - args: args{ - content: tagsJsonTest, - }, - want: &apitypes.Configuration{ - Services: []*apitypes.Service{ - { - Name: "API 101", - Description: "modify operationId", - Upstream: &apitypes.Upstream{ - Scheme: "https", - Nodes: []apitypes.UpstreamNode{ - { - Host: "api-101.glitch.me", - Port: 443, - Weight: 100, - }, - }, - Timeout: &apitypes.UpstreamTimeout{ - Connect: 60, - Send: 60, - Read: 60, - }, - PassHost: apitypes.UpstreamPassHostPass, - }, - // Labels: []apitypes.Labels{ - // "web-spider", "blockchain", - //}, - }, - }, - Routes: []*apitypes.Route{ - { - Name: "getCustomers", - Description: "Get all customers", - // Labels: []string{"default", "customer"}, - Methods: []string{"GET"}, - Uris: []string{"/customers"}, - }, - }, - }, - wantErr: false, - }, - { - name: "urlVariables", - args: args{ - content: urlVariables, - }, - want: &apitypes.Configuration{ - Services: []*apitypes.Service{ - { - Name: "URL variables", - Upstream: &apitypes.Upstream{ - Scheme: "https", - Nodes: []apitypes.UpstreamNode{ - { - Host: "example.com", - Port: 443, - Weight: 100, - }, - }, - Timeout: &apitypes.UpstreamTimeout{ - Connect: 60, - Send: 60, - Read: 60, - }, - PassHost: apitypes.UpstreamPassHostPass, - }, - // Labels: make([]apitypes.Labels, 0), - }, - }, - Routes: []*apitypes.Route{ - { - Name: "url-variables_get_base64-value", - // Labels: []string{"default"}, - Methods: []string{"GET"}, - Uris: []string{"/base64/:value"}, - }, - { - Name: "url-variables_get_basic-auth-user-passwd", - // Labels: []string{"default"}, - Methods: []string{"GET"}, - Uris: []string{"/basic-auth/:user/:passwd"}, - }, - { - Name: "url-variables_get_digest-auth-qop-user-passwd-algorithm-stale-after", - // Labels: []string{"default"}, - Methods: []string{"GET"}, - Uris: []string{"/digest-auth/:qop/:user/:passwd/:algorithm/:stale_after"}, - }, - }, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Convert(context.TODO(), tt.args.content) - if (err != nil) != tt.wantErr { - t.Errorf("Convert() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != nil { - // normally the converter should only generate one service - svc := tt.want.Services[0] - svc.ID = common.GenID(svc.Name) - if svc.Upstream != nil { - if svc.Upstream.Nodes == nil { - svc.Upstream.Nodes = []apitypes.UpstreamNode{ - {}, - } - } - svc.Upstream.Name = Slugify("Upstream for", svc.Name) - svc.Upstream.ID = common.GenID(svc.Upstream.Name) - } - - for _, route := range tt.want.Routes { - route.ID = common.GenID(route.Name) - route.ServiceID = svc.ID - } - - assert.Equal(t, tt.want.Services, got.Services) - assert.Equal(t, tt.want.Routes, got.Routes) - } - }) - } -} diff --git a/internal/pkg/openapi2apisix/testdata/Postman-API101.yaml b/internal/pkg/openapi2apisix/testdata/Postman-API101.yaml deleted file mode 100644 index 1f0467d4..00000000 --- a/internal/pkg/openapi2apisix/testdata/Postman-API101.yaml +++ /dev/null @@ -1,152 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -### -# This is the definition file for the Postman API101 sample. -# It is converted from Postman Collection using postman2openapi. -openapi: 3.0.0 -info: - title: API 101 - description: >- - API 101 template for learning API request basics. Follow along with the - webinar / video or just open the first request and hit **Send**! - version: 1.0.0 -servers: - - url: https://api-101.glitch.me - - url: http://{{apiurl}} -components: - securitySchemes: - apikeyAuth: - type: http - scheme: apikey -paths: - /customers: - get: - tags: - - default - summary: Get all customers - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - responses: - '200': - description: Successful response - content: - application/json: {} - /customer: - get: - tags: - - default - summary: Get one customer - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - - name: id - in: query - schema: - type: integer - example: '1' - responses: - '200': - description: Successful response - content: - application/json: {} - post: - tags: - - default - summary: Add new customer - requestBody: - content: - application/json: - schema: - type: object - example: - name: Dorothy Zborna - type: Individual - security: - - apikeyAuth: [] - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - responses: - '200': - description: Successful response - content: - application/json: {} - /customer/{customer_id}: - put: - tags: - - default - summary: Update customer - requestBody: - content: - application/json: - schema: - type: object - example: - name: Sophia Petrillo - type: Individual - security: - - apikeyAuth: [] - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - - name: customer_id - in: path - schema: - type: integer - required: true - example: '1311' - responses: - '200': - description: Successful response - content: - application/json: {} - delete: - tags: - - default - summary: Remove customer - security: - - apikeyAuth: [] - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - - name: customer_id - in: path - schema: - type: integer - required: true - example: '1310' - responses: - '200': - description: Successful response - content: - application/json: {} diff --git a/internal/pkg/openapi2apisix/testdata/operationId.yaml b/internal/pkg/openapi2apisix/testdata/operationId.yaml deleted file mode 100644 index b86b3908..00000000 --- a/internal/pkg/openapi2apisix/testdata/operationId.yaml +++ /dev/null @@ -1,82 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -### -# This is the definition file for the Postman API101 sample. -# It is converted from Postman Collection using postman2openapi. -openapi: 3.0.0 -info: - title: API 101 - description: modify operationId - version: 1.0.0 -servers: - - url: https://api-101.glitch.me -components: - securitySchemes: - apikeyAuth: - type: http - scheme: apikey -paths: - /customers: - get: - tags: - - default - summary: Get all customers - operationId: getCustomers - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - responses: - '200': - description: Successful response - content: - application/json: {} - /customer/{customer_id}: - put: - tags: - - default - summary: Update customer - operationId: update Customer - requestBody: - content: - application/json: - schema: - type: object - example: - name: Sophia Petrillo - type: Individual - security: - - apikeyAuth: [] - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - - name: customer_id - in: path - schema: - type: integer - required: true - example: '1311' - responses: - '200': - description: Successful response - content: - application/json: {} diff --git a/internal/pkg/openapi2apisix/testdata/tags.json b/internal/pkg/openapi2apisix/testdata/tags.json deleted file mode 100644 index d3022d80..00000000 --- a/internal/pkg/openapi2apisix/testdata/tags.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "API 101", - "description": "modify operationId", - "version": "1.0.0" - }, - "servers": [ - { - "url": "https://api-101.glitch.me" - } - ], - "tags": [ - { - "name": "web spider", - "description": "web desc" - }, - { - "name": "blockchain", - "description": "blockchain desc" - } - ], - "components": { - "securitySchemes": { - "apikeyAuth": { - "type": "http", - "scheme": "apikey" - } - } - }, - "paths": { - "/customers": { - "get": { - "tags": [ - "default", - "customer" - ], - "summary": "Get all customers", - "operationId": "getCustomers", - "parameters": [ - { - "name": "user-id", - "in": "header", - "schema": { - "type": "string" - }, - "example": "{{userId}}" - } - ], - "responses": { - "200": { - "description": "Successful response", - "content": { - "application/json": {} - } - } - } - } - } - } -} diff --git a/internal/pkg/openapi2apisix/testdata/tags.yaml b/internal/pkg/openapi2apisix/testdata/tags.yaml deleted file mode 100644 index 622526bd..00000000 --- a/internal/pkg/openapi2apisix/testdata/tags.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -### -# This is the definition file for the Postman API101 sample. -# It is converted from Postman Collection using postman2openapi. -openapi: 3.0.0 -info: - title: API 101 - description: modify operationId - version: 1.0.0 -servers: - - url: https://api-101.glitch.me -tags: - - name: web spider - description: web desc - - name: blockchain - description: blockchain desc -components: - securitySchemes: - apikeyAuth: - type: http - scheme: apikey -paths: - /customers: - get: - tags: - - default - - customer - summary: Get all customers - operationId: getCustomers - parameters: - - name: user-id - in: header - schema: - type: string - example: '{{userId}}' - responses: - '200': - description: Successful response - content: - application/json: {} diff --git a/internal/pkg/openapi2apisix/testdata/urlVariables.yaml b/internal/pkg/openapi2apisix/testdata/urlVariables.yaml deleted file mode 100755 index ae03680a..00000000 --- a/internal/pkg/openapi2apisix/testdata/urlVariables.yaml +++ /dev/null @@ -1,86 +0,0 @@ -openapi: 3.0.1 -info: - title: URL variables - version: 1.0.0 -servers: -- url: https://example.com/ -paths: - /base64/{value}: - get: - tags: - - Dynamic data - parameters: - - name: value - in: path - required: true - schema: - type: string - default: SFRUUEJJTiBpcyBhd2Vzb21l - responses: - 200: - description: Decoded base64 content. - content: {} - /basic-auth/{user}/{passwd}: - get: - tags: - - Auth - parameters: - - name: user - in: path - required: true - schema: - type: string - - name: passwd - in: path - required: true - schema: - type: string - responses: - 200: - description: Sucessful authentication. - content: {} - 401: - description: Unsuccessful authentication. - content: {} - /digest-auth/{qop}/{user}/{passwd}/{algorithm}/{stale_after}: - get: - tags: - - Auth - parameters: - - name: qop - in: path - description: auth or auth-int - required: true - schema: - type: string - - name: user - in: path - required: true - schema: - type: string - - name: passwd - in: path - required: true - schema: - type: string - - name: algorithm - in: path - description: MD5, SHA-256, SHA-512 - required: true - schema: - type: string - default: MD5 - - name: stale_after - in: path - required: true - schema: - type: string - default: never - responses: - 200: - description: Sucessful authentication. - content: {} - 401: - description: Unsuccessful authentication. - content: {} -components: {} diff --git a/internal/pkg/openapi2apisix/types.go b/internal/pkg/openapi2apisix/types.go deleted file mode 100644 index 92501b0b..00000000 --- a/internal/pkg/openapi2apisix/types.go +++ /dev/null @@ -1,61 +0,0 @@ -package openapi2apisix - -import ( - "context" - - "github.com/api7/gopkg/pkg/log" - "github.com/getkin/kin-openapi/openapi3" - "github.com/pkg/errors" - "go.uber.org/zap" - - apitypes "github.com/api7/adc/pkg/api/apisix/types" -) - -var ( - // LoadOpenAPIError is the error type for loading OpenAPI - LoadOpenAPIError = errors.New("load OpenAPI file error") -) - -type ResultBundle struct { - Services []*apitypes.Service `json:"service"` - Routes []*apitypes.Route `json:"routes"` -} - -type OAS []byte - -func (s OAS) LoadOpenAPI(ctx context.Context) (*openapi3.T, error) { - doc, err := openapi3.NewLoader().LoadFromData(s) - if err != nil { - log.Warnw("load OpenAPI error", zap.Error(err)) - return nil, errors.New("failed to load OpenAPI") - } - if doc.OpenAPI == "" { - return nil, errors.New("the file you uploaded is not an OpenAPI document") - } - if doc.Info == nil { - return nil, errors.New("the file you uploaded is not a valid OpenAPI document") - } - // now we only care about info and paths in openapi - err = doc.Info.Validate( - ctx, - openapi3.DisableSchemaPatternValidation(), - openapi3.DisableSchemaDefaultsValidation(), - openapi3.DisableExamplesValidation(), - ) - if err != nil { - return nil, errors.Wrap(err, "the file you uploaded is not a valid OpenAPI document") - } - if doc.Paths != nil { - err = doc.Paths.Validate( - ctx, - openapi3.DisableSchemaPatternValidation(), - openapi3.DisableSchemaDefaultsValidation(), - openapi3.DisableExamplesValidation(), - ) - if err != nil { - return nil, errors.Wrap(err, "the file you uploaded is not a valid OpenAPI document") - } - } - - return doc, nil -} diff --git a/internal/pkg/openapi2apisix/types_test.go b/internal/pkg/openapi2apisix/types_test.go deleted file mode 100644 index 060a389a..00000000 --- a/internal/pkg/openapi2apisix/types_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package openapi2apisix - -import ( - "context" - "testing" - - "github.com/getkin/kin-openapi/openapi3" - "github.com/stretchr/testify/assert" -) - -func TestOAS_LoadOpenAPI(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - oas OAS - want *openapi3.T - pathItemKeys []string - errorReason string - }{ - { - name: "invalid openapi", - oas: OAS(`error`), - want: nil, - errorReason: "failed to load OpenAPI", - }, - { - name: "openapi is empty", - oas: OAS(`{"openapi": ""}`), - want: nil, - errorReason: "the file you uploaded is not an OpenAPI document", - }, - { - name: "info is nil", - oas: OAS(`{"openapi": "3.0.0"}`), - want: nil, - errorReason: "the file you uploaded is not a valid OpenAPI document", - }, - { - name: "success", - oas: tagsJsonTest, - want: &openapi3.T{ - OpenAPI: "3.0.0", - Info: &openapi3.Info{ - Title: "API 101", - Description: "modify operationId", - Version: "1.0.0", - }, - Paths: openapi3.Paths{ - "/customers": { - Get: &openapi3.Operation{ - Tags: []string{"default", "customer"}, - Summary: "Get all customers", - OperationID: "getCustomers", - }, - }, - }, - }, - pathItemKeys: []string{"/customers"}, - errorReason: "", - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := tt.oas.LoadOpenAPI(context.TODO()) - if tt.errorReason != "" { - assert.NotNil(t, err) - assert.Contains(t, err.Error(), tt.errorReason) - } else { - assert.Nil(t, err) - assert.Equal(t, tt.want.OpenAPI, got.OpenAPI) - assert.Equal(t, tt.want.Info.Title, got.Info.Title) - assert.Equal(t, tt.want.Info.Description, got.Info.Description) - assert.Equal(t, tt.want.Info.Version, got.Info.Version) - if tt.want.Paths != nil { - for _, key := range tt.pathItemKeys { - assert.NotNil(t, got.Paths[key], "%s should not be nil", key) - } - } - } - }) - } -} diff --git a/internal/pkg/validator/validator.go b/internal/pkg/validator/validator.go deleted file mode 100644 index 84acba1d..00000000 --- a/internal/pkg/validator/validator.go +++ /dev/null @@ -1,126 +0,0 @@ -package validator - -import ( - "context" - "errors" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" -) - -type Validator struct { - localConfig *types.Configuration - cluster apisix.Cluster -} - -func NewValidator(local *types.Configuration, cluster apisix.Cluster) (*Validator, error) { - return &Validator{ - localConfig: local, - cluster: cluster, - }, nil -} - -type ErrorsWrapper struct { - Errors []error -} - -func (v ErrorsWrapper) Error() string { - var errStr string - for _, e := range v.Errors { - errStr += e.Error() - if !errors.Is(e, v.Errors[len(v.Errors)-1]) { - errStr += "\n" - } - } - return errStr -} - -func (v *Validator) Validate() []error { - allErr := []error{} - - common.NormalizeConfiguration(v.localConfig) - - for _, service := range v.localConfig.Services { - service := service - err := v.cluster.Service().Validate(context.Background(), service) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, route := range v.localConfig.Routes { - route := route - err := v.cluster.Route().Validate(context.Background(), route) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, consumer := range v.localConfig.Consumers { - consumer := consumer - err := v.cluster.Consumer().Validate(context.Background(), consumer) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, ssl := range v.localConfig.SSLs { - ssl := ssl - err := v.cluster.SSL().Validate(context.Background(), ssl) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, globalRule := range v.localConfig.GlobalRules { - globalRule := globalRule - err := v.cluster.GlobalRule().Validate(context.Background(), globalRule) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, pluginConfig := range v.localConfig.PluginConfigs { - pluginConfig := pluginConfig - err := v.cluster.PluginConfig().Validate(context.Background(), pluginConfig) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, consumerGroup := range v.localConfig.ConsumerGroups { - consumerGroup := consumerGroup - err := v.cluster.ConsumerGroup().Validate(context.Background(), consumerGroup) - if err != nil { - allErr = append(allErr, err) - } - } - - // TODO: enable this when APISIX supports - //for _, pluginMetadata := range v.localConfig.PluginMetadatas { - // pluginMetadata := pluginMetadata - // err := v.cluster.PluginMetadata().Validate(context.Background(), pluginMetadata) - // if err != nil { - // allErr = append(allErr, err) - // } - //} - - for _, streamRoute := range v.localConfig.StreamRoutes { - streamRoute := streamRoute - err := v.cluster.StreamRoute().Validate(context.Background(), streamRoute) - if err != nil { - allErr = append(allErr, err) - } - } - - for _, upstream := range v.localConfig.Upstreams { - upstream := upstream - err := v.cluster.Upstream().Validate(context.Background(), upstream) - if err != nil { - allErr = append(allErr, err) - } - } - - return allErr -} diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000..d0dbd1b8 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,5 @@ +import { getJestProjects } from '@nx/jest'; + +export default { + projects: getJestProjects(), +}; diff --git a/jest.preset.js b/jest.preset.js new file mode 100644 index 00000000..f078ddce --- /dev/null +++ b/jest.preset.js @@ -0,0 +1,3 @@ +const nxPreset = require('@nx/jest/preset').default; + +module.exports = { ...nxPreset }; diff --git a/libs/backend-api7/.eslintrc.json b/libs/backend-api7/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/libs/backend-api7/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/backend-api7/README.md b/libs/backend-api7/README.md new file mode 100644 index 00000000..a095f7b5 --- /dev/null +++ b/libs/backend-api7/README.md @@ -0,0 +1,8 @@ +# API7 Enterprise Backend for ADC + +## Supported Features + +| Features | Supported | +| ------------- | --------- | +| Dump to ADC | ✅ | +| Sync from ADC | ✅ | diff --git a/libs/backend-api7/e2e/assets/api7-dashboard.crt b/libs/backend-api7/e2e/assets/api7-dashboard.crt new file mode 100644 index 00000000..b5086404 --- /dev/null +++ b/libs/backend-api7/e2e/assets/api7-dashboard.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5jCCAc6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDExBhcGk3 +ZWUtZGFzaGJvYXJkMB4XDTI0MDUxMzAzMTc1NloXDTM0MDUxMTAzMTc1NlowGzEZ +MBcGA1UEAxMQYXBpN2VlLWRhc2hib2FyZDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANB+TVH3akGfc2tL9Zw83s+dCzQ6OVw3XoMWapPQZ6+fXv47MBIb +5PCb0Cc9rVf4M2JidDBr2EZrp4di8toQVgIgO3uCLgDtfWFwNtga277UB0TjXIzq +T4aSsMTv6PG+slKNQ0KRzx/Op+20JBHqkKY3X/QabqaXBmMip+eaYN3FKZl7thIF +cpXoRc1h+aNbSRaoSg1itT92WsFbOEj4571TMsn7+3V2440g+0blMIg6wkCZ9a1l +p4jlY1RqaghwzQY3PQOlQCJs/ikVG2SQMSRsA30mdSWC3vIRHJ60A+EB7JiUTlXQ +COK58KloXeJjVCUqTRvjJP8V6XGeXFNe7lMCAwEAAaM1MDMwDgYDVR0PAQH/BAQD +AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN +AQELBQADggEBAEm6HFidG5S3uGhWXi5fkUaymr51AuzdplUgwlxdZRyFWfSyhnN6 +0rY7olfV5TOZI9ZjRAAohibT8V0c0UdkmXRx78bSpODE2BRQQRJyGxwkCdjCI1Xb +kr2TG/NHCzbpO1Fbj9nu4OSZd4HWQy75OEWnHr1J9/PiyGJRC+jfIRJykoQPBA6L +DhT7xV7mX47XuGTCIlHWlrrip3FR2T9zD2LLuyJe8m2g9is3WXfdXciqgF+/FRaT +RSiBeWd5dQ7USth5AKNtQI4+yUO27vMKsezEoWS0qMY0NX48BvFuKFoplvcnH8Kb +G8G19E/RRPeVfJIrnms4hg7xBW07YiQe9ac= +-----END CERTIFICATE----- diff --git a/libs/backend-api7/e2e/assets/certs/generate-ssl.sh b/libs/backend-api7/e2e/assets/certs/generate-ssl.sh new file mode 100755 index 00000000..c4d0139c --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/generate-ssl.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +openssl genrsa -out test-ssl1.key 2048 +openssl req -new -sha256 -key test-ssl1.key -out test-ssl1.csr -subj "/CN=test1" +openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey test-ssl1.key -in test-ssl1.csr -out test-ssl1.cer + +openssl genrsa -out test-ssl2.key 2048 +openssl req -new -sha256 -key test-ssl2.key -out test-ssl2.csr -subj "/CN=test2" +openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey test-ssl2.key -in test-ssl2.csr -out test-ssl2.cer diff --git a/libs/backend-api7/e2e/assets/certs/test-ssl1.cer b/libs/backend-api7/e2e/assets/certs/test-ssl1.cer new file mode 100644 index 00000000..2a7b6086 --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/test-ssl1.cer @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqTCCAZECFBBt5zPTCuCxdo9rcoAZOuu8i0LRMA0GCSqGSIb3DQEBCwUAMBAx +DjAMBgNVBAMMBXRlc3QxMCAXDTI0MDYxNzEzMTM0OFoYDzIxMjQwNTI0MTMxMzQ4 +WjAQMQ4wDAYDVQQDDAV0ZXN0MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAOw6reiIdIEgKgy7CVlv6AyJVp1dHW4RC/bvutbk/ZpvieziUetb18PeeKFj +wdw5URqQ79AfijeTfWCB3U0EAr85l8Gw2N6XttQu9V8BHyOj1RKw5JZR1tCfjmJN +FjxqENoxV3ytid69hZfQbicrTce1ascrbcEKfm4GGBiU1U5F7DW6+gcBQYOMjFlU +u0k/VbqKquL+SEE97JQAXZGsulV7c1hEpbhWaP/J3KIPbeRNmYl9j0T0e7XnJJvg +158jizG7qORIsHcIqftCk+xN106nHvWuzpNZz7H3BcnSxxERxBhyIJXeA73btRxA +MeXEJJMtyYCngx0KxcyvTy6mlyMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAk9/O +NAvT+XMfPIy6EcFimTdC4qIDzPoWk8hVGNXi67bydkllC9PU+o/j83iPaQ9mL8Kb +5lunpYBGu0GIUHLKC//IQTI0HM37Yyef3x+L6f8uS0J5MczvfZYcp//4q7O9sxxY +egC8/kzJZGk/0TsjnAveeMcwaGC8W6dVhmeJd20xByD8qsEjHqy11j3jDCFdWysx +lK6OHEDN3EnLh6OJ7GTQegnLiPl8H9+1lNIGiSH6sPBVcyTcpPNbavKx005OLKBk +A0qUQWy6UDcp4VpUXVnO7uB8SCj4JK4nf1QENwERqD+rTOdhUnxZDtNq5qrxkXTd +TlXoU3lcYpfxD7ZrWg== +-----END CERTIFICATE----- diff --git a/libs/backend-api7/e2e/assets/certs/test-ssl1.csr b/libs/backend-api7/e2e/assets/certs/test-ssl1.csr new file mode 100644 index 00000000..dbf09905 --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/test-ssl1.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVTCCAT0CAQAwEDEOMAwGA1UEAwwFdGVzdDEwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDsOq3oiHSBICoMuwlZb+gMiVadXR1uEQv277rW5P2ab4ns +4lHrW9fD3nihY8HcOVEakO/QH4o3k31ggd1NBAK/OZfBsNjel7bULvVfAR8jo9US +sOSWUdbQn45iTRY8ahDaMVd8rYnevYWX0G4nK03HtWrHK23BCn5uBhgYlNVORew1 +uvoHAUGDjIxZVLtJP1W6iqri/khBPeyUAF2RrLpVe3NYRKW4Vmj/ydyiD23kTZmJ +fY9E9Hu15ySb4NefI4sxu6jkSLB3CKn7QpPsTddOpx71rs6TWc+x9wXJ0scREcQY +ciCV3gO927UcQDHlxCSTLcmAp4MdCsXMr08uppcjAgMBAAGgADANBgkqhkiG9w0B +AQsFAAOCAQEAPHA46x+PURSIctsQuLbOv3XrzW2ua/iJtHcS73QbpHc6eU+PvAh4 +p8Kv9rdxJHc6/5Q9z7S0sqAr4Bz16+/4Sio4j64Hyio0lbqzB5Eh+r4DmJhby4Gd +KzAXXAofiz7F1eJHkHBDiuHC9rjaMB8xwEPIKcY+MfszB5OgN7ko7AxFNLxCRXI6 +qIUVvc/3N3JyJiBDIuCFti1Ij+CJVBxqWoGe8dDPCLXyBNpE8oAyrAYrIoenyEMk +Vhb3SHEJWxRTehpDVpMji0vgzAKWtAcSBlCBBMRyj/lrq+Iln217JMvH2cAhvkFU +7t95bBBZ884HsGaiAXq7cc0Q+/dFoM02YA== +-----END CERTIFICATE REQUEST----- diff --git a/libs/backend-api7/e2e/assets/certs/test-ssl1.key b/libs/backend-api7/e2e/assets/certs/test-ssl1.key new file mode 100644 index 00000000..8d8b447c --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/test-ssl1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDsOq3oiHSBICoM +uwlZb+gMiVadXR1uEQv277rW5P2ab4ns4lHrW9fD3nihY8HcOVEakO/QH4o3k31g +gd1NBAK/OZfBsNjel7bULvVfAR8jo9USsOSWUdbQn45iTRY8ahDaMVd8rYnevYWX +0G4nK03HtWrHK23BCn5uBhgYlNVORew1uvoHAUGDjIxZVLtJP1W6iqri/khBPeyU +AF2RrLpVe3NYRKW4Vmj/ydyiD23kTZmJfY9E9Hu15ySb4NefI4sxu6jkSLB3CKn7 +QpPsTddOpx71rs6TWc+x9wXJ0scREcQYciCV3gO927UcQDHlxCSTLcmAp4MdCsXM +r08uppcjAgMBAAECggEABCxao3MWQPVxuLralxKKiSNN/Q/Y/Qm6M0/zmrD98Rjd +xtfpkZOCAaswH2AQbgugUYfmrK1eiV/P4JM6v/uh8RObUjpOy6YwEJpFM01lIKZF +p987tyWMjObTkHoDKA1b7UkoS7nJZ9DjdV7sD9PMhMFTpoQdQHlLoOyt7CeI9XKB +YTX064mug7G7C6rRyyX6o3eG/uNWNRaOWS28yH05qeJOJwa2B6a/S1haeOVJ6zBu +22RfZkAIJeThX7ubHDHmbj12/gpBcFYUsI48pcKUq6C+H4pGNK6ElP+IeE6CqALc +dWQeC7Lha3GlrEjjX4Iq5jxwSEvyEWtReNs2VjVPSQKBgQDwcFYjk+M0XcSTw5BY +5VhjmSkXBjsERkyKOaLxD32PR/9OMseG98vM2eKrrigPryNbu72lvAq58BqBRuDg +9dQupdssPjhlCVNV0pNCDbbMlj7fRqAwqyZroqRk/7UyDDOcRRu5LTVoCqXep/B9 +4vbQaYcj5E7GJhdE7B6rez2X2wKBgQD7hJjRQTfo7bDgAEMjLNkkUaRARO16DwWJ +Au2IspMDgz/mX0bHBFh+JrUeQticQAxNWg60qXacoGC5PpPHo+P33LdjQFhFbCPx +KlwCskUSee5IHkh+ZH67TPjA0q4aRAHTDWjxpPGUmaAikDyjCi89C9YymK3xhj2t +yZd/pUYkWQKBgQCjtPkRdEoIh6emrEsGG/HYrpnpkVZxjKPkoWWuGEv3WKAeUDjG +TsW2jsvk9MlWFHtQpD12MNhkzXRefTH0OazkrgzSzPJ86KKTFrMpEElcdxgL0fyT +5pRcSPsBwZFPqDsjtXIw3PU2B7n0Jgh6ziQatx9KctKn25uQU+x1+Y1wbQKBgBg1 +dxANGviMiE3HOK8WDgB06z5UZBcvgvThF0PlDEl2RJxD52gYfvwFdcFn5bmEYKrB +VFEHTqdcuHKcF1VL7RFaaTxgYR6tffgkf56y0fspSUofwBKKUbhtfDPzE1br3NEX +pVY87hPDhapN/8ghntJdZUyRbYylwyILGU3r9ohhAoGBAKAnH7JCxYKR+r+b/U5b +DWyslMi/DSVgVrzdjl9BvQuS8DYQZXQF5kl1mf5JR8Ge0+K8w7txUd+d980ZsJsJ +sBWsPsQHyJinYT01I2tCXMR8UDzzr70qwfjskMIYDnILNZmNvHfHzeqme54L0T0R +j+wF3hsV6efjOkuRUtIuFKuG +-----END PRIVATE KEY----- diff --git a/libs/backend-api7/e2e/assets/certs/test-ssl2.cer b/libs/backend-api7/e2e/assets/certs/test-ssl2.cer new file mode 100644 index 00000000..3749cbfb --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/test-ssl2.cer @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqTCCAZECFDJYELjEoeFjh4fPrkHCmZ/gBY1pMA0GCSqGSIb3DQEBCwUAMBAx +DjAMBgNVBAMMBXRlc3QyMCAXDTI0MDYxNzEzMTM0OFoYDzIxMjQwNTI0MTMxMzQ4 +WjAQMQ4wDAYDVQQDDAV0ZXN0MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALxCPBokhV+z3Z91kKUtBuAHZt9K15JsheDn7MaAsnDwhAcBPgBN5SjBo4BP +SuxpHfwQHsnwkgQMDNrTMOjfOEBsoNgw/MuXfFmIEDWtzIUQ+sVBc/cu8vTMQBbA +zM0lf9sDBkzAXGkyP+/8gqc1nrm47K4JprShwh5AafmrVbv8bJGe55daYWVwXY+9 +zr3AWzxTK7py7fXbN0QWpfknmUU3K04PXriwyKpOEwt2zpfMfFp17GJZ+oS6PCb2 +qsqNbmnj05/V/OMu8e/llIJe9i3y5FP1KIGN01MLg3xLMOcbZOAatCdqD1s8NJNV +F1g5H2AfdmCuLb27jUNFYnvbqvECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEASnXr +aN5HGC7mYywTYx+4WYZmRWMk/tX5vKWxX8eTd1A9MEsSJKpRiqFBhWXUyMQzSMDC +KNEa16TwAICT60cw+d5Ybqw0KY5/D6F0zp2RQm/DzVVxWfPce9vtM20IZrp7QC7S +n1mCVILxm7XhTKAM/IVXiyOCieU3YRl+tG/cur1YgV68HIlSrsA42iOdGmabYMsU +uLoeadMjwOKoKOMUt33vimwGGhF2G2/GtQ4twHZBXN+0NB7x0BkKa/OlNL4yw3Dc +K9vYwmL4HR/1ByOKmHy9Ma5t7ufVnZ7OTc3WGItYYscZwI7piSc3yfuA6mF8HyvR +iYddK4HhuX4LZfW7Sg== +-----END CERTIFICATE----- diff --git a/libs/backend-api7/e2e/assets/certs/test-ssl2.csr b/libs/backend-api7/e2e/assets/certs/test-ssl2.csr new file mode 100644 index 00000000..866de29e --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/test-ssl2.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVTCCAT0CAQAwEDEOMAwGA1UEAwwFdGVzdDIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC8QjwaJIVfs92fdZClLQbgB2bfSteSbIXg5+zGgLJw8IQH +AT4ATeUowaOAT0rsaR38EB7J8JIEDAza0zDo3zhAbKDYMPzLl3xZiBA1rcyFEPrF +QXP3LvL0zEAWwMzNJX/bAwZMwFxpMj/v/IKnNZ65uOyuCaa0ocIeQGn5q1W7/GyR +nueXWmFlcF2Pvc69wFs8Uyu6cu312zdEFqX5J5lFNytOD164sMiqThMLds6XzHxa +dexiWfqEujwm9qrKjW5p49Of1fzjLvHv5ZSCXvYt8uRT9SiBjdNTC4N8SzDnG2Tg +GrQnag9bPDSTVRdYOR9gH3Zgri29u41DRWJ726rxAgMBAAGgADANBgkqhkiG9w0B +AQsFAAOCAQEACj4//wBGpu2OX+Z52ncxrd9XmK+n3CzX/5R4NIN+4sY+Y0o7yHRd +zzWAkzC0lMygw1sT9Yx6s+0u4V8V1uVYarrXzaEMbVj8ffnmuiTIRZ+NsdZROUJq +qgP+aQHubLuNJ1ZINz8lUjp22u1eczEHzbjPu48Zu7cinYQooEHq7kP0YX6DryOV +7DgaxQr0pvCiyt4bK2W5mcy8G5QjU0Z85aFRcpQXeJS0Eyxs/Lp/hiQZbWp6QoHT +Iszpz51PPzRw5ytX93fg5GxBUr4J08MG/NM0Wt6l8efqOaTigud8iFLWn97TjeiY +HRPQggrIi9BYKAGgixUTnNyIz3sWCmyjaw== +-----END CERTIFICATE REQUEST----- diff --git a/libs/backend-api7/e2e/assets/certs/test-ssl2.key b/libs/backend-api7/e2e/assets/certs/test-ssl2.key new file mode 100644 index 00000000..a4666171 --- /dev/null +++ b/libs/backend-api7/e2e/assets/certs/test-ssl2.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8QjwaJIVfs92f +dZClLQbgB2bfSteSbIXg5+zGgLJw8IQHAT4ATeUowaOAT0rsaR38EB7J8JIEDAza +0zDo3zhAbKDYMPzLl3xZiBA1rcyFEPrFQXP3LvL0zEAWwMzNJX/bAwZMwFxpMj/v +/IKnNZ65uOyuCaa0ocIeQGn5q1W7/GyRnueXWmFlcF2Pvc69wFs8Uyu6cu312zdE +FqX5J5lFNytOD164sMiqThMLds6XzHxadexiWfqEujwm9qrKjW5p49Of1fzjLvHv +5ZSCXvYt8uRT9SiBjdNTC4N8SzDnG2TgGrQnag9bPDSTVRdYOR9gH3Zgri29u41D +RWJ726rxAgMBAAECggEAAXMedLJp9y4IEN2eOty5PCm9GGRtJeAsD7/5dMsyPz26 +5orokqaxkw9wAOGbbsKdy9PGnkDPVF9Qt1v/o8DESsq2BQmA3i3RHqdg6JOUCu+c +iWmoSPOq70j7XVwoDOmzAwQSR65bh9Euk4cwQmAJVDN2Wci9PeWDpylRJ5kIKfsT +jS86TVRB/bkQAndH95ZrpWeJbROzjS89bOIaY5HXZeAZdLEkNaIInDGfKVx/Sr5N +loSCQtuyV7PFygJHHxouuRyx3YUCi2ic0tw3uCTkML35cYPuEnC7f9nEoereu+oJ +dTuwwQFr5PwBBdxg9EWG9R3xkEmRjSOOPJfCDF+gIQKBgQD+p70ENkrDHlIX5bOB +V8L8KdMcCpyAM8kaHOGDVbmnfE4HHdvM//+ROGlImlelYZY0DirCY6ZFg8rzYsJ7 +ARfaTZO6MBxLT5G1kjMFOZ7UqFtv2t3kTB6GCbLnuI9HV54yZsyQcpVkAtHPZrAT +R6mvHgbuPw+jcoDAD7niVQCIDQKBgQC9QLycauwdx8Rz5T4pcvtkkmhORsTc6Dix +43kj0aSTk7vTo44U4SZw0dGA5aCwh5R3OfuitQAzyWpM73ThpPN4D8HoSsIdlLfV +fUvMT4reEocg3SvEMKFh9SxmffvV4vdCZnYNCJwTTErw6Pl8IDXf3jb16/OBJHRF +Lcxxix8xdQKBgQCyndTmb25hDZNnT6ZowrXxSSTaNlcvVSNpPXiwNiDSaU4Rj8/s +KmOH6JlwY1G2noxMVuOHFX46pAZmUI8417tzU5aascQmztjVEobW02OkpbH88vdQ +elOukH78KWfWEo7Tq2cwqi4+x/uCDfTSkSFjwxv5YCIH8vh7vowTRKvqwQKBgQCA +EhURTRIoPQ3m2BWu+w7AyosnnaCPVd84EpXh9XcIH4uV9zKpUowvAO5j6gFKNRX1 +cRmmzBIs+XqkQc+4+2V/tDomtqCSYBffs4Dd7F4vVh530PRTOg2cOswnN856ubNW +DfyJaXAlZxW33hEnPKz4qGpNFbWdiUvdnNajZ+BeGQKBgCHDgFtDSeKkvIbQUDsn +JMwwrovemQTa3mIKx2oT3TyZ/+ug5W6qLTV8Napt3AMjSMBHfNEQgoDqojKgz5Cz +hIjBmQvmS+M6z6M+FxtJY4pmFZXaxj7RzChsmKu0YhbOJ1euZzezsTOeoAMpSnKn +tMJT8ToyxTx6lI7wK2KeVfbc +-----END PRIVATE KEY----- diff --git a/libs/backend-api7/e2e/assets/testdata/mixed-1-clean.json b/libs/backend-api7/e2e/assets/testdata/mixed-1-clean.json new file mode 100644 index 00000000..52208877 --- /dev/null +++ b/libs/backend-api7/e2e/assets/testdata/mixed-1-clean.json @@ -0,0 +1,72 @@ +[ + { + "resourceType": "route", + "type": "delete", + "resourceId": "9764db8eeb0f13c2a9762161c418504d74ac64a2", + "resourceName": "route1.2", + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "delete", + "resourceId": "801b75f6eb1432c889cd15d3cffb2fa28b7bf95c", + "resourceName": "route1.1", + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "delete", + "resourceId": "12e10811f0bf2d39998936bf81e33314ad7ff6ae", + "resourceName": "route2.2", + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "route", + "type": "delete", + "resourceId": "143e485dfbcace6afb3d40cd26a20cb6f3d8a469", + "resourceName": "route2.1", + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "service", + "type": "delete", + "resourceId": "6e899c1108b88e75d4887b85f9a62c26d9571739", + "resourceName": "service1" + }, + { + "resourceType": "service", + "type": "delete", + "resourceId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a", + "resourceName": "service2" + }, + { + "resourceType": "consumer", + "type": "delete", + "resourceId": "tom", + "resourceName": "tom" + }, + { + "resourceType": "ssl", + "type": "delete", + "resourceId": "5f543afdb6ba8aeef955c6c951d3bd70c1de8361", + "resourceName": "test.com" + }, + { + "resourceType": "global_rule", + "type": "delete", + "resourceId": "prometheus", + "resourceName": "prometheus" + }, + { + "resourceType": "plugin_metadata", + "type": "delete", + "resourceId": "tcp-logger", + "resourceName": "tcp-logger" + }, + { + "resourceType": "plugin_metadata", + "type": "delete", + "resourceId": "http-logger", + "resourceName": "http-logger" + } +] diff --git a/libs/backend-api7/e2e/assets/testdata/mixed-1.json b/libs/backend-api7/e2e/assets/testdata/mixed-1.json new file mode 100644 index 00000000..870dce4d --- /dev/null +++ b/libs/backend-api7/e2e/assets/testdata/mixed-1.json @@ -0,0 +1,307 @@ +[ + { + "resourceType": "ssl", + "type": "create", + "resourceId": "5f543afdb6ba8aeef955c6c951d3bd70c1de8361", + "resourceName": "test.com", + "newValue": { + "type": "server", + "snis": ["test.com"], + "certificates": [ + { + "certificate": "-----BEGIN CERTIFICATE-----\nMIICrTCCAZUCFCcH5+jEDUhpTxEQo/pZYC91e2aYMA0GCSqGSIb3DQEBCwUAMBEx\nDzANBgNVBAMMBlJPT1RDQTAgFw0yNDAxMTgwNjAzMDNaGA8yMTIzMTIyNTA2MDMw\nM1owEzERMA8GA1UEAwwIdGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCVkfufMRK2bckdpQ/aRfcaTxmjsv5Mb+sJdhb0QuEuXp/VgN3yzFM0\nzCmAeBZwNKpU3HZDv0tnkTx7OARYpj5Bw1ole0EfPVPKBRjlLE56tabzyd4vdLV2\nbk7jYH+H8NjGZNEYLm9MdWiB4Ulyc0+XFA0ZL5WWKOi+oSQVUibT8QK0CENFKNLP\nQjEXlbyujzRS3u6r99EEEy8+3psBA2EELq8GAjEp+jilWggBhUEpLQxCHhHeNevR\nkg5iEvhOhEVKtr5xvgolg5Wvz7GmDulIW9MCu0dIXim52H/spPwgi3yRraY1XjxU\nREyj5tcY7n7LBESkx/ODXEyCkICIPpo9AgMBAAEwDQYJKoZIhvcNAQELBQADggEB\nADBU5XvbnjaF4rpQoqdzgK6BuRvD/Ih/rh+xc+G9mm+qaHx0g3TdTqyhCvSg6aRW\njDq4Z0NILdb6wmJcunua1jjmOQMXER5y34Xfn21+dzjLN2Bl+/vZ/HyXlCjxkppG\nZAsd1H0/jmXqN1zddIThxOccmRcDEP+9GT3hba50sijFbO30Zx+ORJCoT8he6Kyw\nKdOs/yyukafoAtlpoPR+ao/kumto6w/rLfFlEsehU0dMGNgPVSxxVNtBSdxPTUBk\nD6mfqB4f//2DuAmiO+l5RmPUmumqzcYlpd+oAdy3OSnNEHbgxishZr/GI3s6DmUh\n16bgI69aQ5F+MnN3trvaufc=\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVkfufMRK2bckd\npQ/aRfcaTxmjsv5Mb+sJdhb0QuEuXp/VgN3yzFM0zCmAeBZwNKpU3HZDv0tnkTx7\nOARYpj5Bw1ole0EfPVPKBRjlLE56tabzyd4vdLV2bk7jYH+H8NjGZNEYLm9MdWiB\n4Ulyc0+XFA0ZL5WWKOi+oSQVUibT8QK0CENFKNLPQjEXlbyujzRS3u6r99EEEy8+\n3psBA2EELq8GAjEp+jilWggBhUEpLQxCHhHeNevRkg5iEvhOhEVKtr5xvgolg5Wv\nz7GmDulIW9MCu0dIXim52H/spPwgi3yRraY1XjxUREyj5tcY7n7LBESkx/ODXEyC\nkICIPpo9AgMBAAECggEADKrg+zIOSioRKvQUXCGp1G4Xl1AtDm86IkKAni5d4O39\nrnjsE0iv0VxrNWi2ScmEFYzYbVNxwPNBgpQTdiiaRDqV02/Va55njsgQvDQc73Kc\nEbCqoy1Iwx+Dield07cXvPHD7b7dCUY2VC2u6UUP3BVEKLppel67m42NP0sGY9O4\nALutC42zU8BFLRnt0koSfcTdEcwFPFDQ0N8H2wI/Z7Y65QniilUqD4AS/SH8MesO\n4pjEXhZz9ok7WgduK2LVukGvt1QedTsRh8qwHm3PgT/pF2+35eeT0nYTz2VscPBI\ngazEtLQFLhtXZ4aH3MUTsXVkQLEe7XeILHrsnCGbBwKBgQC7q8LzHFdVHc46qqiB\nppMfGEYlV4LCoWbHfEl+nPznKCtcjrN8unoN6nyGq+lt2bwUqZdnpvYSqydPMujH\nCAWnRIX+7H4ELIRKV62oVibd2ovfubMGIzCGUTObl8Vn2m/ovtMwZn9mvPCY4hve\n8TY80apdZae+Ew0Bfn2ZNDo+vwKBgQDMBviZdbE78tONwfKbfDeLVN62/TC5mQad\nIzz2G5AbolTKq6qByJlO7ZfcjdsLJTpeKfbzU2pHMpWNnTjN0mHgyBFuvLgoaN1n\n2qXnOPyv8Xir/N7eHZ2K9lvGrSPY4vszzLTabKqVYiufJYjb0okH0AWg8+4FxIHb\n+gms0eWiAwKBgEu3U7MUByQfH1pKCiws0YSlHX/pW6c8ySPIwDomCl8UtNHl/QJg\nlefRaCZJa6dXRmurtJssIHGNvhFU/9d1JBrFKa6dKYZzk3gPAdA92fZ+OxqraFAc\nmHJIhqLKy+lHlwj3HGuVnucLaaK07vu2o+RLzwlZfyDPvNqSdwf9q1YvAoGAdoia\nR7XbuUNzawlB5Nl+/6DYH6Hre/iOoh6F3UnYKGXgMzsWvX4Iq5VXxBhaKRiA15Iz\n2dwdg628u2CbTuCqYsh5cEeOClQaRar+9d3i2GlGvU0VQiAclk5YtY2DpQ8B+G82\npyu40z8MrtJEt8mSOQq/KmptX8Zx15ZlppTvf3cCgYBYxCWn49saCr0FnhIheenc\njlExhQ3ebcwJBSuzNKd2y94zUrQScVpxg27ZAXUfJpppcoY73elvYnczRe3JNaVY\n1b+66bQSAevT5GE8n2C3olLtNmZMvT41Pra0s3St0YnyPtH8acgeHw8Vy5/0/MhZ\nm7/qElKtlWlmUQO1CLdZhQ==\n-----END PRIVATE KEY-----\n" + } + ] + } + }, + { + "resourceType": "service", + "type": "create", + "resourceId": "6e899c1108b88e75d4887b85f9a62c26d9571739", + "resourceName": "service1", + "newValue": { + "name": "service1", + "description": "service1 description", + "upstream": { + "name": "default", + "scheme": "http", + "type": "roundrobin", + "hash_on": "vars", + "nodes": [ + { + "host": "host", + "port": 1100, + "weight": 1100, + "priority": 0 + } + ], + "retry_timeout": 0, + "pass_host": "pass", + "checks": { + "active": { + "type": "tcp", + "timeout": 1, + "concurrency": 10, + "http_path": "/", + "https_verify_certificate": true, + "healthy": { + "interval": 1, + "http_statuses": [200, 302], + "successes": 2 + }, + "unhealthy": { + "interval": 1, + "http_statuses": [429, 404, 500, 501, 502, 503, 504, 505], + "http_failures": 5, + "tcp_failures": 2, + "timeouts": 3 + } + } + } + }, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + }, + "routes": [ + { + "uris": ["/anything"], + "name": "route1.1", + "description": "", + "methods": ["GET"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + { + "uris": ["/anything"], + "name": "route1.2", + "description": "", + "methods": ["POST"], + "enable_websocket": false + } + ] + } + }, + { + "resourceType": "service", + "type": "create", + "resourceId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a", + "resourceName": "service2", + "newValue": { + "name": "service2", + "description": "service2 description", + "upstream": { + "name": "default", + "scheme": "http", + "type": "roundrobin", + "hash_on": "vars", + "nodes": [ + { + "host": "host", + "port": 1100, + "weight": 1100, + "priority": 0 + } + ], + "retry_timeout": 0, + "pass_host": "pass" + }, + "routes": [ + { + "uris": ["/getSomething"], + "name": "route2.1", + "description": "", + "methods": ["GET", "POST"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + { + "uris": ["/postSomething"], + "name": "route2.2", + "description": "", + "methods": ["POST", "PUT"], + "enable_websocket": false + } + ] + } + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "801b75f6eb1432c889cd15d3cffb2fa28b7bf95c", + "resourceName": "route1.1", + "newValue": { + "uris": ["/anything"], + "name": "route1.1", + "description": "", + "methods": ["GET"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "9764db8eeb0f13c2a9762161c418504d74ac64a2", + "resourceName": "route1.2", + "newValue": { + "uris": ["/anything"], + "name": "route1.2", + "description": "", + "methods": ["POST"], + "enable_websocket": false + }, + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "143e485dfbcace6afb3d40cd26a20cb6f3d8a469", + "resourceName": "route2.1", + "newValue": { + "uris": ["/getSomething"], + "name": "route2.1", + "description": "", + "methods": ["GET", "POST"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "12e10811f0bf2d39998936bf81e33314ad7ff6ae", + "resourceName": "route2.2", + "newValue": { + "uris": ["/postSomething"], + "name": "route2.2", + "description": "", + "methods": ["POST", "PUT"], + "enable_websocket": false + }, + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "consumer", + "type": "create", + "resourceId": "tom", + "resourceName": "tom", + "newValue": { + "username": "tom", + "description": "", + "plugins": { + "key-auth": { + "key": "tom" + } + } + } + }, + { + "resourceType": "global_rule", + "type": "create", + "resourceId": "prometheus", + "resourceName": "prometheus", + "newValue": { + "_meta": { + "disable": false + }, + "prefer_name": false + } + }, + { + "resourceType": "plugin_metadata", + "type": "create", + "resourceId": "http-logger", + "resourceName": "http-logger", + "newValue": { + "log_format": { + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr", + "host": "$host" + } + } + }, + { + "resourceType": "plugin_metadata", + "type": "create", + "resourceId": "tcp-logger", + "resourceName": "tcp-logger", + "newValue": { + "log_format": { + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr", + "host": "$host" + } + } + } +] diff --git a/libs/backend-api7/e2e/default-value.e2e-spec.ts b/libs/backend-api7/e2e/default-value.e2e-spec.ts new file mode 100644 index 00000000..e55c3a5e --- /dev/null +++ b/libs/backend-api7/e2e/default-value.e2e-spec.ts @@ -0,0 +1,75 @@ +import { BackendAPI7 } from '../src'; +import { getDefaultValue } from './support/utils'; + +describe('Default Value', () => { + let backend: BackendAPI7; + + beforeAll(() => { + backend = new BackendAPI7({ + server: globalThis.server, + token: globalThis.token, + tlsSkipVerify: true, + gatewayGroup: 'default', + }); + }); + + it('Check default value', async () => { + const defaultValue = await getDefaultValue(backend); + expect(defaultValue).toMatchObject({ + core: { + service: { + upstream: { + checks: { + active: { + concurrency: 10, + healthy: { + http_statuses: [200, 302], + interval: 1, + successes: 2, + }, + http_path: '/', + https_verify_certificate: true, + timeout: 1, + type: 'http', + unhealthy: { + http_failures: 5, + http_statuses: [429, 404, 500, 501, 502, 503, 504, 505], + interval: 1, + tcp_failures: 2, + timeouts: 3, + }, + }, + passive: { + healthy: { + http_statuses: [ + 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, + 302, 303, 304, 305, 306, 307, 308, + ], + successes: 5, + }, + type: 'http', + unhealthy: { + http_failures: 5, + http_statuses: [429, 500, 503], + tcp_failures: 2, + timeouts: 7, + }, + }, + }, + discovery_args: {}, + hash_on: 'vars', + keepalive_pool: { idle_timeout: 60, requests: 1000, size: 320 }, + name: 'default', + nodes: [{ priority: 0 }], + pass_host: 'pass', + retry_timeout: 0, + scheme: 'http', + timeout: { connect: 60, read: 60, send: 60 }, + type: 'roundrobin', + }, + }, + ssl: { client: { depth: 1 } }, + }, + }); + }); +}); diff --git a/libs/backend-api7/e2e/ping.e2e-spec.ts b/libs/backend-api7/e2e/ping.e2e-spec.ts new file mode 100644 index 00000000..c1cbbc4f --- /dev/null +++ b/libs/backend-api7/e2e/ping.e2e-spec.ts @@ -0,0 +1,30 @@ +import { BackendAPI7 } from '../src'; + +describe('Ping', () => { + it('should success', async () => { + const backend = new BackendAPI7({ + server: globalThis.server, + token: globalThis.token, + tlsSkipVerify: true, + }); + await backend.ping(); + }); + + it('should failed (invalid server)', async () => { + const backend = new BackendAPI7({ + server: 'http://0.0.0.0', + token: '', + }); + await expect(backend.ping()).rejects.toThrow( + 'connect ECONNREFUSED 0.0.0.0:80', + ); + }); + + it('should failed (self-signed certificate)', async () => { + const backend = new BackendAPI7({ + server: globalThis.server, + token: globalThis.token, + }); + await expect(backend.ping()).rejects.toThrow('self-signed certificate'); + }); +}); diff --git a/libs/backend-api7/e2e/support/global-setup.ts b/libs/backend-api7/e2e/support/global-setup.ts new file mode 100644 index 00000000..bd0d6b12 --- /dev/null +++ b/libs/backend-api7/e2e/support/global-setup.ts @@ -0,0 +1,73 @@ +import axios from 'axios'; +import { spawn } from 'node:child_process'; +import { randomUUID } from 'node:crypto'; +import { Agent } from 'node:https'; + +const httpClient = axios.create({ + baseURL: 'https://localhost:7443', + auth: { + username: 'admin', + password: 'admin', + }, + httpsAgent: new Agent({ + rejectUnauthorized: false, + }), +}); + +const setupAPI7 = async () => { + return new Promise((resolve, reject) => { + const ls = spawn( + 'sh', + [ + '-c', + `curl -O ${process.env.BACKEND_API7_DOWNLOAD_URL} && tar xf api7-ee-*.tar.gz && cd api7-ee && bash run.sh start`, + ], + { cwd: `/tmp` }, + ); + + console.log('\nSetup API7 Instance\n'); + + ls.stdout.on('data', function (data) { + console.log('stdout: ' + data.toString()); + }); + + ls.stderr.on('data', function (data) { + console.log('stderr: ' + data.toString()); + }); + + ls.on('exit', function (code) { + if (code) + reject(`child process exited with non-zero code: ${code?.toString()}`); + + console.log('Successful deployment'); + resolve(); + }); + }); +}; + +const activateAPI7 = async () => { + await httpClient.put(`/api/license`, { + data: process.env.BACKEND_API7_LICENSE, + }); +}; + +const generateToken = async () => { + const resp = await httpClient.post<{ value: { token: string } }>( + `/api/tokens`, + { + expires_at: 0, + name: randomUUID(), + }, + { validateStatus: () => true }, + ); + + globalThis.token = resp.data.value.token; +}; + +export default async () => { + if (process.env['SKIP_API7_SETUP'] !== 'true') await setupAPI7(); + await activateAPI7(); + await generateToken(); + + globalThis.server = 'https://localhost:7443'; +}; diff --git a/libs/backend-api7/e2e/support/global-teardown.ts b/libs/backend-api7/e2e/support/global-teardown.ts new file mode 100644 index 00000000..0bb7cc56 --- /dev/null +++ b/libs/backend-api7/e2e/support/global-teardown.ts @@ -0,0 +1,3 @@ +export default async () => { + //TODO +}; diff --git a/libs/backend-api7/e2e/support/utils.ts b/libs/backend-api7/e2e/support/utils.ts new file mode 100644 index 00000000..33f66da1 --- /dev/null +++ b/libs/backend-api7/e2e/support/utils.ts @@ -0,0 +1,94 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Listr, SilentRenderer } from 'listr2'; + +import { BackendAPI7 } from '../../src'; + +export const runTask = async (tasks: Listr, ctx = {}) => { + // add sync delay + if (Array.isArray(tasks.task)) { + const delayedTasks = tasks.task.reduce((pv, cv) => { + pv.push(cv, { + task: async () => new Promise((resolve) => setTimeout(resolve, 20)), + }); + return pv; + }, []); + tasks = new Listr(delayedTasks, { concurrent: false }); + } + + //@ts-expect-error just ignore + tasks.renderer = new SilentRenderer(); + await tasks.run(ctx); + return tasks.ctx.local; +}; + +export const syncEvents = async ( + backend: BackendAPI7, + events: Array = [], +) => { + return runTask(await backend.sync(), { diff: events }); +}; + +export const dumpConfiguration = async (backend: BackendAPI7) => { + const ctx = { remote: {} }; + await runTask(await backend.dump(), ctx); + return ctx.remote; +}; + +export const getDefaultValue = async (backend: BackendAPI7) => { + const ctx = { defaultValue: {} }; + await runTask(new Listr(backend.getResourceDefaultValueTask()), ctx); + return ctx.defaultValue; +}; + +export const createEvent = ( + resourceType: ADCSDK.ResourceType, + resourceName: string, + resource: object, + parentName?: string, +): ADCSDK.Event => ({ + type: ADCSDK.EventType.CREATE, + resourceType, + resourceName, + resourceId: + resourceType === ADCSDK.ResourceType.CONSUMER || + resourceType === ADCSDK.ResourceType.GLOBAL_RULE || + resourceType === ADCSDK.ResourceType.PLUGIN_METADATA + ? resourceName + : resourceType === ADCSDK.ResourceType.SSL + ? ADCSDK.utils.generateId((resource as ADCSDK.SSL).snis.join(',')) + : ADCSDK.utils.generateId( + parentName ? `${parentName}.${resourceName}` : resourceName, + ), + newValue: resource, + parentId: parentName ? ADCSDK.utils.generateId(parentName) : undefined, +}); + +export const updateEvent = ( + resourceType: ADCSDK.ResourceType, + resourceName: string, + resource: object, + parentName?: string, +): ADCSDK.Event => { + const event = createEvent(resourceType, resourceName, resource, parentName); + event.type = ADCSDK.EventType.UPDATE; + return event; +}; + +export const deleteEvent = ( + resourceType: ADCSDK.ResourceType, + resourceName: string, + parentName?: string, +): ADCSDK.Event => ({ + type: ADCSDK.EventType.DELETE, + resourceType, + resourceName, + resourceId: + resourceType === ADCSDK.ResourceType.CONSUMER || + resourceType === ADCSDK.ResourceType.GLOBAL_RULE || + resourceType === ADCSDK.ResourceType.PLUGIN_METADATA + ? resourceName + : ADCSDK.utils.generateId( + parentName ? `${parentName}.${resourceName}` : resourceName, + ), + parentId: parentName ? ADCSDK.utils.generateId(parentName) : undefined, +}); diff --git a/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts b/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts new file mode 100644 index 00000000..8f724242 --- /dev/null +++ b/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts @@ -0,0 +1,516 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { unset } from 'lodash'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { BackendAPI7 } from '../src'; +import { + createEvent, + deleteEvent, + dumpConfiguration, + syncEvents, + updateEvent, +} from './support/utils'; + +describe('Sync and Dump - 1', () => { + let backend: BackendAPI7; + + beforeAll(() => { + backend = new BackendAPI7({ + server: globalThis.server, + token: globalThis.token, + tlsSkipVerify: true, + gatewayGroup: 'default', + }); + }); + + describe('Sync and dump single service', () => { + const upstream = { + scheme: 'https', + nodes: [ + { + host: 'httpbin.org', + port: 443, + weight: 100, + }, + ], + }; + const service1Name = 'service1'; + const service1 = { + name: service1Name, + upstream: structuredClone(upstream), + } as ADCSDK.Service; + const service2Name = 'service2'; + const service2 = { + name: service2Name, + upstream: structuredClone(upstream), + } as ADCSDK.Service; + + it('Create services', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SERVICE, service1Name, service1), + createEvent(ADCSDK.ResourceType.SERVICE, service2Name, service2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + result.services = result.services.sort((a, b) => + a.name.localeCompare(b.name), + ); + expect(result.services).toHaveLength(2); + expect(result.services[0]).toMatchObject(service1); + expect(result.services[1]).toMatchObject(service2); + }); + + it('Update service1', async () => { + service1.description = 'desc'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.SERVICE, service1Name, service1), + ]); + }); + + it('Dump again (service1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services[0]).toMatchObject(service1); + }); + + it('Delete service1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SERVICE, service1Name), + ])); + + it('Dump again (service1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service2); + }); + + it('Delete service2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SERVICE, service2Name), + ])); + + it('Dump again (service2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(0); + }); + }); + + describe('Sync and dump service with routes', () => { + const serviceName = 'test'; + const service = { + name: serviceName, + upstream: { + scheme: 'https', + nodes: [ + { + host: 'httpbin.org', + port: 443, + weight: 100, + }, + ], + }, + path_prefix: '/test', + strip_path_prefix: true, + } as ADCSDK.Service; + const route1Name = 'route1'; + const route1 = { + name: route1Name, + uris: ['/route1'], + } as ADCSDK.Route; + const route2Name = 'route2'; + const route2 = { + name: route2Name, + uris: ['/route2'], + plugins: { + 'key-auth': {}, + }, + } as ADCSDK.Route; + + it('Create resources', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SERVICE, serviceName, service), + createEvent(ADCSDK.ResourceType.ROUTE, route1Name, route1, serviceName), + createEvent(ADCSDK.ResourceType.ROUTE, route2Name, route2, serviceName), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service); + expect(result.services[0].routes).toHaveLength(2); + expect(result.services[0].routes[0]).toMatchObject(route1); + expect(result.services[0].routes[1]).toMatchObject(route2); + }); + + it('Delete route1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.ROUTE, route1Name, serviceName), + ])); + + it('Dump again (check remain route2)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service); + expect(result.services[0].routes).toHaveLength(1); + expect(result.services[0].routes[0]).toMatchObject(route2); + }); + + it('Delete service', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SERVICE, serviceName), + ])); + + it('Dump again (service should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(0); + }); + }); + + describe('Sync and dump service with stream routes', () => { + const serviceName = 'test'; + const service = { + name: serviceName, + upstream: { + scheme: 'tcp', + nodes: [ + { + host: '1.1.1.1', + port: 853, + weight: 100, + }, + ], + }, + } as ADCSDK.Service; + const route1Name = 'sroute1'; + const route1 = { + name: route1Name, + server_port: 5432, + } as ADCSDK.StreamRoute; + const route2Name = 'sroute2'; + const route2 = { + name: route2Name, + server_port: 3306, + } as ADCSDK.StreamRoute; + const serviceForSync = Object.assign(structuredClone(service), { + stream_routes: [], + }); + + it('Create resources', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SERVICE, serviceName, serviceForSync), + createEvent( + ADCSDK.ResourceType.STREAM_ROUTE, + route1Name, + route1, + serviceName, + ), + createEvent( + ADCSDK.ResourceType.STREAM_ROUTE, + route2Name, + route2, + serviceName, + ), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service); + expect(result.services[0].stream_routes).toHaveLength(2); + expect(result.services[0].stream_routes[0]).toMatchObject(route2); + expect(result.services[0].stream_routes[1]).toMatchObject(route1); + }); + + it('Delete stream route1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.STREAM_ROUTE, route1Name, serviceName), + ])); + + it('Dump again (check remain stream route2)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service); + expect(result.services[0].stream_routes).toHaveLength(1); + expect(result.services[0].stream_routes[0]).toMatchObject(route2); + }); + + it('Delete service', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SERVICE, serviceName), + ])); + + it('Dump again (service should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(0); + }); + }); + + describe('Sync and dump consumers', () => { + const consumer1Name = 'consumer1'; + const consumer1 = { + username: consumer1Name, + plugins: { + 'key-auth': { + key: consumer1Name, + }, + }, + } as ADCSDK.Consumer; + const consumer2Name = 'consumer2'; + const consumer2 = { + username: consumer2Name, + plugins: { + 'key-auth': { + key: consumer2Name, + }, + }, + } as ADCSDK.Consumer; + + it('Create consumers', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.CONSUMER, consumer1Name, consumer1), + createEvent(ADCSDK.ResourceType.CONSUMER, consumer2Name, consumer2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers).toHaveLength(2); + expect(result.consumers[0]).toMatchObject(consumer2); + expect(result.consumers[1]).toMatchObject(consumer1); + }); + + it('Update consumer1', async () => { + consumer1.description = 'desc'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.CONSUMER, consumer1Name, consumer1), + ]); + }); + + it('Dump again (consumer1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers[0]).toMatchObject(consumer1); + }); + + it('Delete consumer1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.CONSUMER, consumer1Name), + ])); + + it('Dump again (consumer1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers).toHaveLength(1); + expect(result.consumers[0]).toMatchObject(consumer2); + }); + + it('Delete consumer2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.CONSUMER, consumer2Name), + ])); + + it('Dump again (consumer2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers).toHaveLength(0); + }); + }); + + describe('Sync and dump ssls', () => { + const certificates = [ + { + certificate: readFileSync( + join(__dirname, 'assets/certs/test-ssl1.cer'), + ).toString('utf-8'), + key: readFileSync( + join(__dirname, 'assets/certs/test-ssl1.key'), + ).toString('utf-8'), + }, + { + certificate: readFileSync( + join(__dirname, 'assets/certs/test-ssl2.cer'), + ).toString('utf-8'), + key: readFileSync( + join(__dirname, 'assets/certs/test-ssl2.key'), + ).toString('utf-8'), + }, + ]; + const ssl1SNIs = ['ssl1-1.com', 'ssl1-2.com']; + const ssl1 = { + snis: ssl1SNIs, + certificates: [certificates[0]], + } as ADCSDK.SSL; + const ssl2SNIs = ['ssl2-1.com', 'ssl2-2.com']; + const ssl2 = { + snis: ssl2SNIs, + certificates: [certificates[1]], + } as ADCSDK.SSL; + const sslName = (snis: Array) => snis.join(','); + + const ssl1test = structuredClone(ssl1); + const ssl2test = structuredClone(ssl2); + unset(ssl1test, 'certificates.0.key'); + unset(ssl2test, 'certificates.0.key'); + + it('Create ssls', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SSL, sslName(ssl1SNIs), ssl1), + createEvent(ADCSDK.ResourceType.SSL, sslName(ssl2SNIs), ssl2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.ssls).toHaveLength(2); + expect(result.ssls[0]).toMatchObject(ssl2test); + expect(result.ssls[1]).toMatchObject(ssl1test); + }); + + it('Update ssl1', async () => { + ssl1.labels = { test: 'test' }; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.SSL, sslName(ssl1SNIs), ssl1), + ]); + }); + + it('Dump again (ssl1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + const testCase = structuredClone(ssl1); + unset(testCase, 'certificates.0.key'); + expect(result.ssls[0]).toMatchObject(testCase); + }); + + it('Delete ssl1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SSL, sslName(ssl1SNIs)), + ])); + + it('Dump again (ssl1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.ssls).toHaveLength(1); + expect(result.ssls[0]).toMatchObject(ssl2test); + }); + + it('Delete ssl2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SSL, sslName(ssl2SNIs)), + ])); + + it('Dump again (ssl2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.ssls).toHaveLength(0); + }); + }); + + describe('Sync and dump global rules', () => { + const plugin1Name = 'prometheus'; + const plugin1 = { + prefer_name: true, + } as ADCSDK.GlobalRule; + const plugin2Name = 'file-logger'; + const plugin2 = { + path: 'logs/file.log', + } as ADCSDK.GlobalRule; + + it('Create global rules', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin1Name, plugin1), + createEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin2Name, plugin2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.global_rules)).toHaveLength(2); + expect(result.global_rules[plugin1Name]).toMatchObject(plugin1); + expect(result.global_rules[plugin2Name]).toMatchObject(plugin2); + }); + + it('Update plugin1', async () => { + plugin1.test = 'test'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin1Name, plugin1), + ]); + }); + + it('Dump again (plugin1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.global_rules[plugin1Name]).toMatchObject(plugin1); + }); + + it('Delete plugin1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin1Name), + ])); + + it('Dump again (plugin1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.global_rules)).toHaveLength(1); + expect(result.global_rules[plugin1Name]).toBeUndefined(); + expect(result.global_rules[plugin2Name]).toMatchObject(plugin2); + }); + + it('Delete plugin2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin2Name), + ])); + + it('Dump again (plugin2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.global_rules)).toHaveLength(0); + }); + }); + + describe('Sync and dump plugin metadata', () => { + const plugin1Name = 'http-logger'; + const plugin1 = { + log_format: { test: 'test', test1: 'test1' }, + } as ADCSDK.PluginMetadata; + const plugin2Name = 'tcp-logger'; + const plugin2 = { + log_format: { test: 'test', test1: 'test1' }, + } as ADCSDK.PluginMetadata; + + it('Create plugin metadata', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin1Name, plugin1), + createEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin2Name, plugin2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.plugin_metadata)).toHaveLength(2); + expect(result.plugin_metadata[plugin1Name]).toMatchObject(plugin1); + expect(result.plugin_metadata[plugin2Name]).toMatchObject(plugin2); + }); + + it('Update plugin1', async () => { + plugin1.test = 'test'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin1Name, plugin1), + ]); + }); + + it('Dump again (plugin1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.plugin_metadata[plugin1Name]).toMatchObject(plugin1); + }); + + it('Delete plugin1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin1Name), + ])); + + it('Dump again (plugin1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.plugin_metadata)).toHaveLength(1); + expect(result.plugin_metadata[plugin1Name]).toBeUndefined(); + expect(result.plugin_metadata[plugin2Name]).toMatchObject(plugin2); + }); + + it('Delete plugin2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin2Name), + ])); + + it('Dump again (plugin2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.plugin_metadata)).toHaveLength(0); + }); + }); +}); diff --git a/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts b/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts new file mode 100644 index 00000000..0cd83ba5 --- /dev/null +++ b/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts @@ -0,0 +1,209 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { BackendAPI7 } from '../src'; +import { dumpConfiguration, syncEvents } from './support/utils'; + +describe('Sync and Dump - 2', () => { + let backend: BackendAPI7; + + beforeAll(() => { + backend = new BackendAPI7({ + server: globalThis.server, + token: globalThis.token, + tlsSkipVerify: true, + gatewayGroup: 'default', + }); + }); + + describe('Sync and dump mixed configuration', () => { + const testData: Array = JSON.parse( + readFileSync(join(__dirname, 'assets/testdata/mixed-1.json')).toString( + 'utf-8', + ), + ); + const testDataClean: Array = JSON.parse( + readFileSync( + join(__dirname, 'assets/testdata/mixed-1-clean.json'), + ).toString('utf-8'), + ); + let dump: ADCSDK.Configuration; + + it('Sync', async () => syncEvents(backend, testData)); + + it('Dump', async () => { + dump = await dumpConfiguration(backend); + }); + + it('Check', () => { + expect(dump.ssls[0]).toMatchObject({ + type: 'server', + snis: ['test.com'], + certificates: [ + { + certificate: + '-----BEGIN CERTIFICATE-----\nMIICrTCCAZUCFCcH5+jEDUhpTxEQo/pZYC91e2aYMA0GCSqGSIb3DQEBCwUAMBEx\nDzANBgNVBAMMBlJPT1RDQTAgFw0yNDAxMTgwNjAzMDNaGA8yMTIzMTIyNTA2MDMw\nM1owEzERMA8GA1UEAwwIdGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCVkfufMRK2bckdpQ/aRfcaTxmjsv5Mb+sJdhb0QuEuXp/VgN3yzFM0\nzCmAeBZwNKpU3HZDv0tnkTx7OARYpj5Bw1ole0EfPVPKBRjlLE56tabzyd4vdLV2\nbk7jYH+H8NjGZNEYLm9MdWiB4Ulyc0+XFA0ZL5WWKOi+oSQVUibT8QK0CENFKNLP\nQjEXlbyujzRS3u6r99EEEy8+3psBA2EELq8GAjEp+jilWggBhUEpLQxCHhHeNevR\nkg5iEvhOhEVKtr5xvgolg5Wvz7GmDulIW9MCu0dIXim52H/spPwgi3yRraY1XjxU\nREyj5tcY7n7LBESkx/ODXEyCkICIPpo9AgMBAAEwDQYJKoZIhvcNAQELBQADggEB\nADBU5XvbnjaF4rpQoqdzgK6BuRvD/Ih/rh+xc+G9mm+qaHx0g3TdTqyhCvSg6aRW\njDq4Z0NILdb6wmJcunua1jjmOQMXER5y34Xfn21+dzjLN2Bl+/vZ/HyXlCjxkppG\nZAsd1H0/jmXqN1zddIThxOccmRcDEP+9GT3hba50sijFbO30Zx+ORJCoT8he6Kyw\nKdOs/yyukafoAtlpoPR+ao/kumto6w/rLfFlEsehU0dMGNgPVSxxVNtBSdxPTUBk\nD6mfqB4f//2DuAmiO+l5RmPUmumqzcYlpd+oAdy3OSnNEHbgxishZr/GI3s6DmUh\n16bgI69aQ5F+MnN3trvaufc=\n-----END CERTIFICATE-----\n', + }, + ], + }); + + dump.services = dump.services.sort((a, b) => + a.name.localeCompare(b.name), + ); + expect(dump.services[0]).toMatchObject({ + name: 'service1', + description: 'service1 description', + upstream: { + name: 'default', + scheme: 'http', + type: 'roundrobin', + hash_on: 'vars', + nodes: [ + { + host: 'host', + port: 1100, + weight: 1100, + priority: 0, + }, + ], + retry_timeout: 0, + pass_host: 'pass', + checks: { + active: { + type: 'tcp', + timeout: 1, + concurrency: 10, + http_path: '/', + https_verify_certificate: true, + healthy: { + interval: 1, + http_statuses: [200, 302], + successes: 2, + }, + unhealthy: { + interval: 1, + http_statuses: [429, 404, 500, 501, 502, 503, 504, 505], + http_failures: 5, + tcp_failures: 2, + timeouts: 3, + }, + }, + }, + }, + plugins: { + 'limit-count': { + allow_degradation: false, + count: 2, + key: '$consumer_name $remote_addr', + key_type: 'var_combination', + policy: 'local', + rejected_code: 503, + show_limit_quota_header: true, + time_window: 60, + }, + }, + }); + expect(dump.services[0].routes[0]).toMatchObject({ + uris: ['/anything'], + name: 'route1.1', + methods: ['GET'], + enable_websocket: false, + plugins: { + 'limit-count': { + allow_degradation: false, + count: 2, + key: '$consumer_name $remote_addr', + key_type: 'var_combination', + policy: 'local', + rejected_code: 503, + show_limit_quota_header: true, + time_window: 60, + }, + }, + }); + expect(dump.services[0].routes[1]).toMatchObject({ + uris: ['/anything'], + name: 'route1.2', + methods: ['POST'], + enable_websocket: false, + }); + + expect(dump.services[1]).toMatchObject({ + name: 'service2', + description: 'service2 description', + upstream: { + name: 'default', + scheme: 'http', + type: 'roundrobin', + hash_on: 'vars', + nodes: [ + { + host: 'host', + port: 1100, + weight: 1100, + priority: 0, + }, + ], + retry_timeout: 0, + pass_host: 'pass', + }, + }); + expect(dump.services[1].routes[0]).toMatchObject({ + uris: ['/postSomething'], + name: 'route2.2', + methods: ['POST', 'PUT'], + enable_websocket: false, + }); + expect(dump.services[1].routes[1]).toMatchObject({ + uris: ['/getSomething'], + name: 'route2.1', + methods: ['GET', 'POST'], + enable_websocket: false, + plugins: { + 'limit-count': { + allow_degradation: false, + count: 2, + key: '$consumer_name $remote_addr', + key_type: 'var_combination', + policy: 'local', + rejected_code: 503, + show_limit_quota_header: true, + time_window: 60, + }, + }, + }); + + expect(dump.consumers[0]).toMatchObject({ + username: 'tom', + plugins: { + 'key-auth': { + key: 'tom', + }, + }, + }); + + expect(dump.global_rules.prometheus).toMatchObject({ + prefer_name: false, + }); + + expect(dump.plugin_metadata['http-logger']).toMatchObject({ + log_format: { + '@timestamp': '$time_iso8601', + client_ip: '$remote_addr', + host: '$host', + }, + }); + + expect(dump.plugin_metadata['tcp-logger']).toMatchObject({ + log_format: { + '@timestamp': '$time_iso8601', + client_ip: '$remote_addr', + host: '$host', + }, + }); + }); + + it('Cleanup', async () => syncEvents(backend, testDataClean)); + }); +}); diff --git a/libs/backend-api7/jest.config.e2e.ts b/libs/backend-api7/jest.config.e2e.ts new file mode 100644 index 00000000..4f2d77f0 --- /dev/null +++ b/libs/backend-api7/jest.config.e2e.ts @@ -0,0 +1,19 @@ +/* eslint-disable */ +export default { + displayName: 'backend-api7-e2e', + preset: '../../jest.preset.js', + globalSetup: '/e2e/support/global-setup.ts', + globalTeardown: '/e2e/support/global-teardown.ts', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/backend-api7/e2e', + testMatch: ['**/?(*.)+(e2e-spec).[jt]s?(x)'], +}; diff --git a/libs/backend-api7/jest.config.ts b/libs/backend-api7/jest.config.ts new file mode 100644 index 00000000..49b952b0 --- /dev/null +++ b/libs/backend-api7/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'backend-api7', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/backend-api7', +}; diff --git a/libs/backend-api7/project.json b/libs/backend-api7/project.json new file mode 100644 index 00000000..a714e375 --- /dev/null +++ b/libs/backend-api7/project.json @@ -0,0 +1,34 @@ +{ + "name": "backend-api7", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/backend-api7/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ] + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}" + ], + "options": { + "jestConfig": "libs/backend-api7/jest.config.ts" + } + }, + "e2e": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{e2eProjectRoot}" + ], + "options": { + "jestConfig": "libs/backend-api7/jest.config.e2e.ts", + "passWithNoTests": true + } + } + }, + "tags": [] +} diff --git a/libs/backend-api7/src/fetcher.ts b/libs/backend-api7/src/fetcher.ts new file mode 100644 index 00000000..daaa49c6 --- /dev/null +++ b/libs/backend-api7/src/fetcher.ts @@ -0,0 +1,172 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Axios } from 'axios'; +import { ListrTask } from 'listr2'; + +import { ToADC } from './transformer'; +import * as typing from './typing'; +import { buildReqAndRespDebugOutput } from './utils'; + +type FetchTask = ListrTask<{ + gatewayGroupId: string; + remote: ADCSDK.Configuration; +}>; + +export class Fetcher { + private readonly toADC = new ToADC(); + + constructor(private readonly client: Axios) {} + + public listServices(): FetchTask { + return { + title: 'Fetch services', + task: async (ctx, task) => { + const resp = await this.client.get<{ list: Array }>( + `/api/gateway_groups/${ctx.gatewayGroupId}/services`, + ); + task.output = buildReqAndRespDebugOutput(resp, 'Get services'); + + const services = resp?.data?.list; + const fetchRoutes = services.map(async (service) => { + if (service.type === 'http') { + const resp = await this.client.get<{ + list: Array; + }>(`/api/service_versions/${service.service_version_id}/routes`); + task.output = buildReqAndRespDebugOutput( + resp, + `Get routes in service "${service.name}"`, + ); + service.routes = resp?.data?.list; + } else { + const resp = await this.client.get<{ + list: Array; + }>( + `/api/service_versions/${service.service_version_id}/stream_routes`, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Get stream routes in service "${service.name}"`, + ); + service.stream_routes = resp?.data?.list; + } + return service; + }); + await Promise.all(fetchRoutes); + + ctx.remote.services = services.map((item) => + this.toADC.transformService(item), + ); + }, + }; + } + + public listConsumers(): FetchTask { + return { + title: 'Fetch consumers', + task: async (ctx, task) => { + const resp = await this.client.get<{ list: Array }>( + '/apisix/admin/consumers', + { + params: { gateway_group_id: ctx.gatewayGroupId }, + }, + ); + task.output = buildReqAndRespDebugOutput(resp, 'Get consumers'); + + ctx.remote.consumers = resp?.data?.list?.map((item) => + this.toADC.transformConsumer(item), + ); + }, + }; + } + + public listSSLs(): FetchTask { + return { + title: 'Fetch ssls', + task: async (ctx, task) => { + const resp = await this.client.get<{ list: Array }>( + '/apisix/admin/ssls', + { + params: { gateway_group_id: ctx.gatewayGroupId }, + }, + ); + task.output = buildReqAndRespDebugOutput(resp, 'Get ssls'); + + ctx.remote.ssls = resp?.data?.list?.map((item) => + this.toADC.transformSSL(item), + ); + }, + }; + } + + public listGlobalRules(): FetchTask { + return { + title: 'Fetch global rules', + task: async (ctx, task) => { + const resp = await this.client.get<{ list: Array }>( + '/apisix/admin/global_rules', + { + params: { gateway_group_id: ctx.gatewayGroupId }, + }, + ); + task.output = buildReqAndRespDebugOutput(resp, 'Get global rules'); + + ctx.remote.global_rules = this.toADC.transformGlobalRule( + resp?.data?.list ?? [], + ); + }, + }; + } + + public listMetadatas(): FetchTask { + return { + title: 'Fetch plugin metadata', + task: async (ctx, task) => { + const resp = await this.client.get>( + '/apisix/admin/plugins/list', + { + params: { has_metadata: true }, + }, + ); + task.output = buildReqAndRespDebugOutput( + resp, + 'Get plugins that contain plugin metadata', + ); + + const plugins = resp.data; + const getMetadataConfig = plugins.map< + Promise<[string, typing.PluginMetadata]> + >(async (pluginName) => { + try { + const resp = await this.client.get<{ + value: typing.PluginMetadata; + }>(`/apisix/admin/plugin_metadata/${pluginName}`, { + params: { gateway_group_id: ctx.gatewayGroupId }, + }); + task.output = buildReqAndRespDebugOutput( + resp, + `Get plugin metadata for "${pluginName}"`, + ); + return [pluginName, resp?.data?.value]; + } catch (err) { + return [pluginName, null]; + } + }); + const metadataObj = Object.fromEntries( + (await Promise.all(getMetadataConfig)).filter((item) => item[1]), + ); + + ctx.remote.plugin_metadata = + this.toADC.transformPluginMetadatas(metadataObj); + }, + }; + } + + public allTask() { + return [ + this.listServices(), + this.listConsumers(), + this.listSSLs(), + this.listGlobalRules(), + this.listMetadatas(), + ]; + } +} diff --git a/libs/backend-api7/src/index.ts b/libs/backend-api7/src/index.ts new file mode 100644 index 00000000..1e9144f4 --- /dev/null +++ b/libs/backend-api7/src/index.ts @@ -0,0 +1,296 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import axios, { Axios, CreateAxiosDefaults } from 'axios'; +import { JSONSchema4 } from 'json-schema'; +import { Listr, ListrTask } from 'listr2'; +import { isEmpty, isNil } from 'lodash'; +import { readFileSync } from 'node:fs'; +import { AgentOptions, Agent as httpsAgent } from 'node:https'; + +import { Fetcher } from './fetcher'; +import { OperateContext, Operator } from './operator'; +import { ToADC } from './transformer'; +import * as typing from './typing'; +import { buildReqAndRespDebugOutput } from './utils'; + +export class BackendAPI7 implements ADCSDK.Backend { + private readonly client: Axios; + private readonly gatewayGroup: string; + private static logScope = ['API7']; + + private gatewayGroupId: string; + private defaultValue: ADCSDK.DefaultValue; + + constructor(private readonly opts: ADCSDK.BackendOptions) { + const config: CreateAxiosDefaults = { + baseURL: `${opts.server}`, + headers: { + 'X-API-KEY': opts.token, + 'Content-Type': 'application/json', + //'User-Agent': 'ADC/0.9.0-alpha.9 (ADC-API7-BACKEND)', + }, + }; + + if (opts.server.startsWith('https')) { + const agentConfig: AgentOptions = { + rejectUnauthorized: !opts?.tlsSkipVerify, + }; + + if (opts?.caCertFile) { + agentConfig.ca = readFileSync(opts.caCertFile); + } + if (opts?.tlsClientCertFile) { + agentConfig.cert = readFileSync(opts.tlsClientCertFile); + agentConfig.key = readFileSync(opts.tlsClientKeyFile); + } + + config.httpsAgent = new httpsAgent(agentConfig); + } + + if (opts.timeout) config.timeout = opts.timeout; + + this.client = axios.create(config); + this.gatewayGroup = opts.gatewayGroup; + } + + public async ping() { + await this.client.get('/api/gateway_groups'); + } + + public getResourceDefaultValueTask(): ListrTask { + const mergeAllOf = (items: Array) => { + if (items.length < 2) return items[0]; + if (!items.every((item) => item.type === 'object')) return null; + + const first = items.shift(); + if (!first.properties) first.properties = {}; + return items.reduce((pv, cv) => { + Object.entries(cv?.properties ?? {}).forEach(([key, val]) => { + pv.properties[key] = val; + }); + return pv; + }, first); + }; + const extractObjectDefault = (obj: JSONSchema4) => { + if (obj.type !== 'object') return null; + if (!obj.properties) return null; + + const defaults = Object.fromEntries( + Object.entries(obj?.properties ?? {}) + .map(([key, field]) => { + if (field.type === 'object') + return [key, extractObjectDefault(field)]; + + // For array nested object (e.g. service.upstream.nodes) + if (field.type === 'array' && !Array.isArray(field.items)) { + if (field.items.type === 'object') + return [key, [extractObjectDefault(field.items)]]; + } + + return [key, field?.default]; + }) + .filter( + ([, defaultValue]) => + !isNil(defaultValue) || !isEmpty(defaultValue), + ), + ); + + return defaults; + }; + return { + enabled: (ctx) => isEmpty(ctx.defaultValue), + task: async (ctx, task) => { + if (!isEmpty(this.defaultValue)) { + ctx.defaultValue = this.defaultValue; + return; + } + const resp = await this.client.get<{ + value: ADCSDK.DefaultValue['core']; + }>('/api/schema/core'); + task.output = buildReqAndRespDebugOutput( + resp, + 'Get core resoruces schema', + ); + + ctx.defaultValue = this.defaultValue = { + core: Object.fromEntries( + Object.entries(resp.data.value).map( + ([type, schema]: [string, JSONSchema4]) => { + const transformer = (type: ADCSDK.ResourceType) => { + const toADC = new ToADC(); + switch (type) { + case ADCSDK.ResourceType.ROUTE: + return toADC.transformRoute; + case ADCSDK.ResourceType.SERVICE: + return toADC.transformService; + case ADCSDK.ResourceType.SSL: + return toADC.transformSSL; + case ADCSDK.ResourceType.CONSUMER: + return toADC.transformConsumer; + default: + return (res: T): T => res; + } + }; + return [ + type, + transformer(type as ADCSDK.ResourceType)( + extractObjectDefault( + schema.allOf ? mergeAllOf(schema.allOf) : schema, + ) ?? {}, + ), + ]; + }, + ), + ), + }; + }, + }; + } + + private getGatewayGroupIdTask(name: string): ListrTask { + return { + enabled: (ctx) => !ctx.gatewayGroupId, + task: async (ctx, task) => { + if (this.gatewayGroupId) { + ctx.gatewayGroupId = this.gatewayGroupId; + return; + } + + const resp = await this.client.get<{ list: Array<{ id: string }> }>( + '/api/gateway_groups', + { + params: { + search: name, + }, + }, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Get id of gateway group "${this.gatewayGroup}"`, + ); + + const gatewayGroups = resp?.data?.list; + if (!gatewayGroups.length) { + throw Error(`Gateway group ${this.gatewayGroup} does not exist`); + } + ctx.gatewayGroupId = this.gatewayGroupId = gatewayGroups[0].id; + }, + }; + } + + public async dump(): Promise> { + const fetcher = new Fetcher(this.client); + + return new Listr<{ remote: ADCSDK.Configuration }>( + [ + this.getResourceDefaultValueTask(), + this.getGatewayGroupIdTask(this.gatewayGroup), + ...fetcher.allTask(), + ], + { + //@ts-expect-error reorg renderer + rendererOptions: { scope: BackendAPI7.logScope }, + }, + ); + } + + public async sync(): Promise { + const operator = new Operator(this.client, this.gatewayGroup); + return new Listr( + [ + this.getGatewayGroupIdTask(this.gatewayGroup), + this.syncPreprocessEventsTask(), + { + task: (ctx, task) => + task.newListr( + ctx.diff.map((event) => + event.type === ADCSDK.EventType.DELETE + ? operator.deleteResource(event) + : operator.updateResource(event), + ), + ), + }, + operator.publishService(), + ], + { + //@ts-expect-error TODO reorg renderer + rendererOptions: { scope: BackendAPI7.logScope }, + }, + ); + } + + // Preprocess events for sync: + // 1. Events that attempt to remove routes but not for the purpose of + // republishing the service will be ignored. + // 2. The service will at least be removed from the gateway group, i.e., + // it will stop processing such traffic. + // Sometimes service templates cannot be removed because it is still + /// referenced by published services on other gateway groups. + private syncPreprocessEventsTask(): ListrTask { + return { + task: (ctx) => { + const isRouteLike = (resourceType: ADCSDK.ResourceType) => + [ + ADCSDK.ResourceType.ROUTE, + ADCSDK.ResourceType.STREAM_ROUTE, + ].includes(resourceType); + const deletedServiceIds = ctx.diff + .filter( + (item) => + item.resourceType === ADCSDK.ResourceType.SERVICE && + item.type === ADCSDK.EventType.DELETE, + ) + .map((item) => item.resourceId); + + ctx.needPublishServices = Object.fromEntries( + ctx.diff + .filter((item) => { + // Include creation and update event types of services + if ( + item.resourceType === ADCSDK.ResourceType.SERVICE && + item.type !== ADCSDK.EventType.DELETE + ) + return true; + + // Include subroutes that have changed but the service itself has + // not been deleted + if ( + isRouteLike(item.resourceType) && + !deletedServiceIds.includes(item.parentId) + ) + return true; + + return false; + }) + .map((item) => [ + item.resourceType === ADCSDK.ResourceType.SERVICE + ? item.resourceId + : item.parentId, + null, + ]), + ); + + // Exclude those events we do not need + ctx.diff = ctx.diff + // Include route non deletion events + .filter((item) => { + if (!isRouteLike(item.resourceType)) return true; + if ( + isRouteLike(item.resourceType) && + item.type !== ADCSDK.EventType.DELETE + ) + return true; + + // Include routes removed just to change the service and republish it + if ( + isRouteLike(item.resourceType) && + item.type === ADCSDK.EventType.DELETE && + !deletedServiceIds.includes(item.parentId) + ) + return true; + + return false; + }); + }, + }; + } +} diff --git a/libs/backend-api7/src/operator.ts b/libs/backend-api7/src/operator.ts new file mode 100644 index 00000000..a73a108b --- /dev/null +++ b/libs/backend-api7/src/operator.ts @@ -0,0 +1,263 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Axios, AxiosResponse } from 'axios'; +import { ListrTask } from 'listr2'; +import { size } from 'lodash'; + +import { FromADC } from './transformer'; +import * as typing from './typing'; +import { buildReqAndRespDebugOutput, capitalizeFirstLetter } from './utils'; + +export interface OperateContext { + diff: Array; + gatewayGroupId: string; + needPublishServices: Record; +} +type OperateTask = ListrTask; + +export class Operator { + constructor( + private readonly client: Axios, + private readonly gatewayGroupName: string, + ) {} + + public updateResource(event: ADCSDK.Event): OperateTask { + return { + title: this.generateTaskName(event), + task: async (ctx, task) => { + let resp: AxiosResponse<{ error_msg?: string }>; + if (event.resourceType === ADCSDK.ResourceType.SERVICE) { + ctx.needPublishServices[event.resourceId] = this.fromADC( + event, + ) as typing.Service; + + // Create a service template instead of create published service directly + resp = await this.client.put( + `/api/services/template/${event.resourceId}`, + this.fromADC(event), + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput(resp); + } else if (event.resourceType === ADCSDK.ResourceType.ROUTE) { + // Create a route template instead of create route directly + resp = await this.client.put( + `/api/routes/template/${event.resourceId}`, + this.fromADC(event), + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput(resp); + } else if (event.resourceType === ADCSDK.ResourceType.STREAM_ROUTE) { + // Create a stream route template instead of create stream route directly + resp = await this.client.put( + `/api/stream_routes/template/${event.resourceId}`, + this.fromADC(event), + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput(resp); + } else { + resp = await this.client.put( + `/apisix/admin/${this.generateResourceTypeInAPI(event.resourceType)}/${event.resourceId}`, + this.fromADC(event), + { + params: { + gateway_group_id: ctx.gatewayGroupId, + }, + validateStatus: () => true, + }, + ); + task.output = buildReqAndRespDebugOutput(resp); + } + + if (resp?.data?.error_msg) throw new Error(resp.data.error_msg); + // [200, 201].includes(resp.status); + }, + }; + } + + public deleteResource(event: ADCSDK.Event): OperateTask { + return { + title: this.generateTaskName(event), + task: async (ctx, task) => { + // If the resource type is Service, we should first disable it + if (event.resourceType === ADCSDK.ResourceType.SERVICE) { + // Modify the status of published service on gateway group + let resp = await this.client.patch( + `/api/gateway_groups/${ctx.gatewayGroupId}/services/${event.resourceId}/runtime_configuration`, + [ + { + op: 'replace', + path: '/status', + value: 0, + }, + ], + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Disable service "${event.resourceName}" on the gateway group ${this.gatewayGroupName}`, + ); + + // Remove disabled service on the gateway group + resp = await this.client.delete( + `/api/gateway_groups/${ctx.gatewayGroupId}/services/${event.resourceId}`, + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Remove service "${event.resourceName}" on the gateway group ${this.gatewayGroupName}`, + ); + + // Check for other references to the service template and do not attempt + // to delete the service template if there are other references + interface UnknownList { + list: Array; + } + resp = await this.client.get( + `/api/services/${event.resourceId}/published_services`, + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Get publish record for service "${event.resourceName}"`, + ); + if ((resp.data as UnknownList)?.list?.length > 0) { + task.output = JSON.stringify({ + type: 'debug', + messages: [ + 'Service template delete is skipped: it is still used by other gateway groups', + ], + }); + return; + } + + resp = await this.client.delete( + `/api/services/template/${event.resourceId}`, + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Remove service template "${event.resourceName}"`, + ); + return; + } else if (event.resourceType === ADCSDK.ResourceType.ROUTE) { + const resp = await this.client.delete( + `/api/routes/template/${event.resourceId}`, + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput(resp); + return; + } else if (event.resourceType === ADCSDK.ResourceType.STREAM_ROUTE) { + const resp = await this.client.delete( + `/api/stream_routes/template/${event.resourceId}`, + { validateStatus: () => true }, + ); + task.output = buildReqAndRespDebugOutput(resp); + return; + } + + const resp = await this.client.delete( + `/apisix/admin/${this.generateResourceTypeInAPI(event.resourceType)}/${event.resourceId}`, + { + params: { + gateway_group_id: ctx.gatewayGroupId, + }, + validateStatus: () => true, + }, + ); + task.output = buildReqAndRespDebugOutput(resp); + + if (resp?.data?.error_msg) throw new Error(resp.data.error_msg); + // [200, 404].includes(resp.status); + }, + }; + } + + public publishService(): OperateTask { + return { + title: 'Publish services', + skip: (ctx) => { + if (!ctx.needPublishServices || size(ctx.needPublishServices) <= 0) + return 'No services to be published'; + }, + task: async (ctx, task) => { + const services = ctx.needPublishServices; + + for (const [serviceId, service] of Object.entries(services)) { + if (!service) { + const resp = await this.client.get<{ value: typing.Service }>( + `/api/gateway_groups/${ctx.gatewayGroupId}/services/${serviceId}`, + ); + task.output = buildReqAndRespDebugOutput( + resp, + `Fill in nil service, id ${serviceId}`, + ); + services[serviceId] = resp?.data?.value; + } + } + + const resp = await this.client.post( + '/api/services/publish', + { + create_new_version: true, + gateway_group_id: ctx.gatewayGroupId, + services: Object.values(services).map( + (service: typing.Service) => ({ + ...service, + version: new Date().getTime().toString(), + hosts: service?.hosts?.length ? service.hosts : undefined, + }), + ), + }, + { + validateStatus: () => true, + }, + ); + task.output = buildReqAndRespDebugOutput(resp, 'Publish all services'); + + if (resp?.data?.error_msg) throw new Error(resp.data.error_msg); + // [200, 201].includes(resp.status); + }, + }; + } + + private generateTaskName(event: ADCSDK.Event) { + return `${capitalizeFirstLetter( + event.type, + )} ${event.resourceType}: "${event.resourceName}"`; + } + + private generateResourceTypeInAPI(resourceType: ADCSDK.ResourceType) { + return resourceType !== ADCSDK.ResourceType.PLUGIN_METADATA + ? `${resourceType}s` + : ADCSDK.ResourceType.PLUGIN_METADATA; + } + + private fromADC(event: ADCSDK.Event) { + const fromADC = new FromADC(); + switch (event.resourceType) { + case ADCSDK.ResourceType.CONSUMER: + return fromADC.transformConsumer(event.newValue as ADCSDK.Consumer); + case ADCSDK.ResourceType.GLOBAL_RULE: + return { + plugins: { + [event.resourceId]: event.newValue, + }, + }; + case ADCSDK.ResourceType.PLUGIN_METADATA: + return event.newValue; + case ADCSDK.ResourceType.SERVICE: + return fromADC.transformService(event.newValue as ADCSDK.Service); + case ADCSDK.ResourceType.ROUTE: + return fromADC.transformRoute( + event.newValue as ADCSDK.Route, + event.parentId, + ); + case ADCSDK.ResourceType.STREAM_ROUTE: + return fromADC.transformStreamRoute( + event.newValue as ADCSDK.StreamRoute, + event.parentId, + ); + case ADCSDK.ResourceType.SSL: + return fromADC.transformSSL(event.newValue as ADCSDK.SSL); + } + } +} diff --git a/libs/backend-api7/src/transformer.ts b/libs/backend-api7/src/transformer.ts new file mode 100644 index 00000000..cf033a9f --- /dev/null +++ b/libs/backend-api7/src/transformer.ts @@ -0,0 +1,214 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { attempt, isError } from 'lodash'; + +import * as typing from './typing'; + +export class ToADC { + private static transformLabels( + labels?: ADCSDK.Labels, + ): Record { + if (!labels) return undefined; + return Object.entries(labels).reduce((pv, [key, value]) => { + const res = attempt(JSON.parse, value as string); + pv[key] = !isError(res) && Array.isArray(res) ? res : value; + return pv; + }, {}); + } + + public transformRoute(route: typing.Route): ADCSDK.Route { + return ADCSDK.utils.recursiveOmitUndefined({ + uris: route?.paths?.[0] ? [route?.paths?.[0]] : undefined, + name: route.name, + description: route.desc, + labels: ToADC.transformLabels(route.labels), + methods: route.methods, + enable_websocket: route.enable_websocket, + plugins: route.plugins, + metadata: { id: route.route_id }, + }); + } + + public transformStreamRoute(route: typing.StreamRoute): ADCSDK.StreamRoute { + return ADCSDK.utils.recursiveOmitUndefined({ + name: route.name, + description: route.desc, + labels: ToADC.transformLabels(route.labels), + server_addr: route.server_addr, + server_port: route.server_port, + remote_addr: route.remote_addr, + metadata: { id: route.stream_route_id }, + }); + } + + public transformService(service: typing.Service): ADCSDK.Service { + return ADCSDK.utils.recursiveOmitUndefined({ + name: service.name, + description: service.desc, + labels: ToADC.transformLabels(service.labels), + upstream: service.upstream, + plugins: service.plugins, + path_prefix: service.path_prefix, + strip_path_prefix: service.strip_path_prefix, + hosts: service.hosts, + routes: service.routes?.map((item) => this.transformRoute(item)), + stream_routes: service.stream_routes?.map((item) => + this.transformStreamRoute(item), + ), + metadata: { id: service.service_id }, + }); + } + + public transformConsumer(consumer: typing.Consumer): ADCSDK.Consumer { + return ADCSDK.utils.recursiveOmitUndefined({ + username: consumer.username, + description: consumer.desc, + labels: ToADC.transformLabels(consumer.labels), + plugins: consumer.plugins, + }); + } + + public transformSSL(ssl: typing.SSL): ADCSDK.SSL { + return ADCSDK.utils.recursiveOmitUndefined({ + labels: ToADC.transformLabels(ssl.labels), + type: ssl.type, + snis: ssl.snis, + certificates: ssl?.cert + ? [{ certificate: ssl.cert, key: '' }] + : undefined, + client: ssl.client + ? { + ca: ssl.client.ca, + depth: ssl.client.depth, + skip_mtls_uri_regex: undefined, + } + : undefined, + metadata: { id: ssl.id }, + }); + } + + public transformGlobalRule( + globalRules: Array, + ): Record { + return Object.fromEntries( + globalRules.map((globalRule) => { + const pluginName = Object.keys(globalRule.plugins)[0]; + const pluginConfig = Object.values(globalRule.plugins)[0]; + return [pluginName, pluginConfig]; + }), + ); + } + + public transformPluginMetadatas( + pluginMetadatas: typing.PluginMetadata, + ): Record { + return pluginMetadatas; + } +} + +export class FromADC { + private static transformLabels( + labels?: ADCSDK.Labels, + ): Record { + if (!labels) return undefined; + return Object.entries(labels).reduce((pv, [key, value]) => { + pv[key] = typeof value === 'string' ? value : JSON.stringify(value); + return pv; + }, {}); + } + + public transformRoute(route: ADCSDK.Route, serviceId: string): typing.Route { + return ADCSDK.utils.recursiveOmitUndefined({ + route_id: ADCSDK.utils.generateId(route.name), + name: route.name, + desc: route.description, + labels: FromADC.transformLabels(route.labels), + methods: route.methods, + enable_websocket: route.enable_websocket, + plugins: route.plugins, + service_id: serviceId, + paths: [route.uris[0]], + }); + } + + public transformStreamRoute( + route: ADCSDK.StreamRoute, + serviceId: string, + ): typing.StreamRoute { + return ADCSDK.utils.recursiveOmitUndefined({ + stream_route_id: ADCSDK.utils.generateId(route.name), + name: route.name, + desc: route.description, + labels: FromADC.transformLabels(route.labels), + service_id: serviceId, + plugins: route.plugins, + server_addr: route.server_addr, + server_port: route.server_port, + remote_addr: route.remote_addr, + }); + } + + public transformService(service: ADCSDK.Service): typing.Service { + return ADCSDK.utils.recursiveOmitUndefined({ + service_id: ADCSDK.utils.generateId(service.name), + name: service.name, + desc: service.description, + labels: FromADC.transformLabels(service.labels), + upstream: service.upstream, + plugins: service.plugins, + path_prefix: service.path_prefix, + strip_path_prefix: service.strip_path_prefix, + hosts: service.hosts, + type: ['tcp', 'udp', 'tls'].includes(service?.upstream?.scheme) + ? 'stream' + : 'http', + }); + } + + public transformConsumer(consumer: ADCSDK.Consumer): typing.Consumer { + return ADCSDK.utils.recursiveOmitUndefined({ + username: consumer.username, + desc: consumer.description, + labels: FromADC.transformLabels(consumer.labels), + plugins: consumer.plugins, + }); + } + + public transformSSL(ssl: ADCSDK.SSL): typing.SSL { + return ADCSDK.utils.recursiveOmitUndefined({ + id: ADCSDK.utils.generateId(ssl.snis.join(',')), + labels: FromADC.transformLabels(ssl.labels), + status: 1, + certificates: undefined, + type: ssl.type, + + snis: ssl.snis, + cert: ssl.certificates[0].certificate, + key: ssl.certificates[0].key, + ...(ssl.certificates.length > 1 + ? { + certs: ssl.certificates + .slice(1) + .map((certificate) => certificate.certificate), + keys: ssl.certificates + .slice(1) + .map((certificate) => certificate.key), + } + : {}), + client: ssl.client, + }); + } + + public transformGlobalRule( + globalRules: Record, + ): Array { + return Object.entries(globalRules).map(([pluginName, pluginConfig]) => ({ + plugins: { [pluginName]: pluginConfig }, + })) as Array; + } + + public transformPluginMetadatas( + pluginMetadatas: Record, + ): typing.PluginMetadata { + return ADCSDK.utils.recursiveOmitUndefined(pluginMetadatas); + } +} diff --git a/libs/backend-api7/src/typing.ts b/libs/backend-api7/src/typing.ts new file mode 100644 index 00000000..f3cc86ab --- /dev/null +++ b/libs/backend-api7/src/typing.ts @@ -0,0 +1,121 @@ +import { + PluginMetadata as ADCPluginMetadata, + Upstream as ADCUpstream, + Labels, + Plugins, + UpstreamBalancer, + UpstreamHealthCheck, + UpstreamNode, + UpstreamPassHost, + UpstreamScheme, + UpstreamTimeout, +} from '@api7/adc-sdk'; + +export interface Route { + id?: string; + name: string; + desc?: string; + labels?: Record; + service_id: string; + route_id: string; + + // policies + plugins?: Plugins; + + // matcher + paths?: Array; + methods?: Array; + + // misc + enable_websocket?: boolean; +} +export interface StreamRoute { + id?: string; + name: string; + desc: string; + labels?: Record; + service_id: string; + stream_route_id: string; + + plugins?: Plugins; + + server_addr: string; + server_port: number; + remote_addr: string; +} +export interface Service { + id?: string; + name: string; + desc?: string; + labels?: Labels; + type?: 'http' | 'stream'; + hosts?: Array; + path_prefix?: string; + strip_path_prefix?: boolean; + upstream: ADCUpstream; + plugins?: Plugins; + version?: string; + service_version_id?: string; + service_id: string; + routes?: Array; + stream_routes?: Array; +} +export interface Consumer { + username: string; + desc?: string; + labels?: Labels; + plugins?: Plugins; +} +export interface SSL { + id?: string; + labels?: Labels; + type: 'server' | 'client'; + cert?: string; + certs?: Array; + key?: string; + keys?: Array; + client?: { + ca: string; + depth: number; + }; + snis?: string[]; +} +export interface GlobalRule { + plugins: Plugins; +} +export type PluginMetadata = Record; +export interface Upstream { + id?: string; + name: string; + desc?: string; + labels?: Labels; + + nodes?: Array>; + scheme?: Extract; + type?: UpstreamBalancer; + hash_on?: string; + key?: string; + checks?: UpstreamHealthCheck; + + discovery_type?: string; + service_name?: string; + discovery_args?: Record; + + pass_host?: UpstreamPassHost; + upstream_host?: string; + retries?: number; + retry_timeout?: number; + timeout?: UpstreamTimeout; + keepalive_pool?: { + size: number; + idle_timeout: number; + requests: number; + }; +} +export interface Resources { + services: Array; + consumers: Array; + ssls: Array; + globalRules: Array; + pluginMetadatas: PluginMetadata; +} diff --git a/libs/backend-api7/src/utils.ts b/libs/backend-api7/src/utils.ts new file mode 100644 index 00000000..b7ab83d5 --- /dev/null +++ b/libs/backend-api7/src/utils.ts @@ -0,0 +1,37 @@ +import { AxiosResponse } from 'axios'; + +export const capitalizeFirstLetter = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1); + +export const buildReqAndRespDebugOutput = ( + resp: AxiosResponse, + desc?: string, +) => { + const config = resp.config; + + // NodeJS will not keep the response header in Xxx-Xxx format, correct it + const normalizeHeaderKey = (key: string) => + key.split('-').map(capitalizeFirstLetter).join('-'); + + // Transforms HTTP headers to a single line of text formatting + const transformHeaders = (headers: object, normalizeKey = false) => + Object.entries(headers).map( + ([key, value]) => + `${normalizeKey ? normalizeHeaderKey(key) : key}: ${key !== 'X-API-KEY' ? value : '*****'}\n`, + ); + return JSON.stringify({ + type: 'debug', + messages: [ + `${desc ?? ''}\n`, //TODO time consumption + // request + `${config.method.toUpperCase()} ${config.url}\n`, + ...transformHeaders(config.headers), + config?.data ? `\n${config.data}\n` : '', + '\n', + // response + `${resp.status} ${resp.statusText}\n`, + ...transformHeaders(resp.headers, true), + `${resp?.data ? `\n${JSON.stringify(resp.data)}` : ''}\n`, + ], + }); +}; diff --git a/libs/backend-api7/tsconfig.json b/libs/backend-api7/tsconfig.json new file mode 100644 index 00000000..ba4f9fec --- /dev/null +++ b/libs/backend-api7/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/backend-api7/tsconfig.lib.json b/libs/backend-api7/tsconfig.lib.json new file mode 100644 index 00000000..e365efe6 --- /dev/null +++ b/libs/backend-api7/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": [ + "node" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "include": [ + "src/**/*.ts" + ] +} diff --git a/libs/backend-api7/tsconfig.spec.json b/libs/backend-api7/tsconfig.spec.json new file mode 100644 index 00000000..e4523c2a --- /dev/null +++ b/libs/backend-api7/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts", + "e2e/**/*.e2e-spec.ts" + ] +} diff --git a/libs/backend-apisix/.eslintrc.json b/libs/backend-apisix/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/libs/backend-apisix/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/backend-apisix/README.md b/libs/backend-apisix/README.md new file mode 100644 index 00000000..3cfa8a03 --- /dev/null +++ b/libs/backend-apisix/README.md @@ -0,0 +1,52 @@ +# Apache APISIX Backend for ADC + +**This backend is experimental and still has many unresolved issues.** + +## Supported Features + +| Features | Supported | +| ------------- | --------- | +| Dump to ADC | ✅ | +| Sync from ADC | ✅ | + +## Known Issues/Limitations + +### ID-based upstream references are not supported + +You cannot reference an external upstream in Route/StreamRoute/Service using `upstream_id`. + +Basically ADC doesn't support this use case and you should use an upstream inline in the resource; you should use the YAML anchor syntax when you want to share the resource across multiple resources. + +### Plugin templates (plugin_configs) are not supported + +ADC does not support plugin templates, you should use a service to do this, the configuration on the service will be applied to all the routes it contains. If you wish to share the configuration across multiple services, you can use the YAML anchor syntax. + +### Some resources will always be considered "changed" + +When you run diff and sync, you will find that some resources are always considered "changed", which is to be expected, although we don't expect it to happen. + +The reason for this comes from the ultimate flexibility that Apache APISIX provides, such as the fact that you can configure the `nodes` field in upstream in two different ways. + +Under the hood, it uses a JSON Schema and some custom Lua logic to check your configuration, and in the process, some unprovided field values will be populated with default values. This actually changes the configurations stored in etcd, and when we read them again using the List API, they have been modified. + +And some JSON Schema may also change when APISIX releases a new version. + +Unless we have a built-in table of default values for core resources and plugins for each version, there's no easy way to avoid this problem. + +However, we believe this issue can be resolved when APISIX use cases are more standardized. + +### Consumer Group not supported yet + +This resource is not supported at this time, they will be excluded by the dump and sync processes. It may be provided when we do have a need for it. + +### The output of dump is not same which Apache APISIX + +ADC will do some normalization when exporting resources from APISIX, for example: + +- Ensure that all referenced upstream is inlined into the resource that references it +- Ensure that the plugin config is inlined into the route that references it +- Ensure that routes and stream routes belong to a service + +Aside from the formal changes, all of your configuration items will be retained as much as possible. + +To be clear, guarantees of absolute consistency in the form of resources are not on our roadmap. ADC will convert your resources in its own way, using the fields documented in the ADC documentation that can be correctly mapped to resources in APISIX. diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/http.yaml b/libs/backend-apisix/e2e/assets/apisix_conf/http.yaml new file mode 100644 index 00000000..8bc6bcbc --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/http.yaml @@ -0,0 +1,22 @@ +apisix: + node_listen: 9080 + enable_ipv6: false + enable_control: true + control: + ip: "0.0.0.0" + port: 9092 +deployment: + admin: + allow_admin: + - 0.0.0.0/0 + admin_key: + - name: "admin" + key: edd1c9f034335f136f87ad84b625c8f1 + role: admin + etcd: + host: + - "http://etcd:2379" + prefix: "/apisix" + timeout: 30 +nginx_config: + worker_processes: 1 diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls.yaml b/libs/backend-apisix/e2e/assets/apisix_conf/mtls.yaml new file mode 100644 index 00000000..c2fa7dc7 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls.yaml @@ -0,0 +1,27 @@ +apisix: + node_listen: 9080 + enable_ipv6: false + enable_control: true + control: + ip: "0.0.0.0" + port: 9092 +deployment: + admin: + allow_admin: + - 0.0.0.0/0 + admin_key: + - name: "admin" + key: edd1c9f034335f136f87ad84b625c8f1 + role: admin + https_admin: true + admin_api_mtls: + admin_ssl_cert: "/mtls/server.cer" + admin_ssl_cert_key: "/mtls/server.key" + admin_ssl_ca_cert: "/mtls/ca.cer" + etcd: + host: + - "http://etcd:2379" + prefix: "/apisix" + timeout: 30 +nginx_config: + worker_processes: 1 diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.cer b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.cer new file mode 100755 index 00000000..70a23a31 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.cer @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqzCCAZMCFHJ1qdQ562AtZhnNb2g5RP964zFtMA0GCSqGSIb3DQEBCwUAMBEx +DzANBgNVBAMMBlJPT1RDQTAgFw0yNDA2MDYwNzExMzhaGA8yMTI0MDUxMzA3MTEz +OFowETEPMA0GA1UEAwwGUk9PVENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAhWEhjd+3TF6tuGdEL1GD9VgaeEemXSYpjI2iaYs2MqtS6ge0QmUYmjgS +hikCkA+hOIwhLFB8Cy23BcsEIQFDGs53HThOBt+harW4+M1bz+rQb+DQNhS4LmqI +VbriMxR5ReLn76rJnc1SXVgjyuLQQrVDPESGpnqhvkPIDVR1xcjklE3iNVH7Dy7o +XH/xRO0h1RqW4JmBrW3z9IjeP80oTjZFpa/Vse3V64CBsR5VEB6Y0qs97PlTTsoM +u7/AIcvb6y4t+qMksKT3HUBsu+QGkRpbjFCcd3zacezRk2FcRU1njTuQaxwBQNBw +cQ25nMO9vbo1uKjrTen+68oQp0wR7QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAY +LYET3babzbqhw8OtrhdyON7GT1DqBeB+2PS1s+9glUCuk2Ov4lFwdXDklxVhYpFt +XNVlGqbYhI0ypTF/us2jKgrJGpusJt+xQ+03so1ysp49TfptfZPF3vaJsdzePn2V +yCK2Yj0O8JZcHWQEPQR+qbP6l3k+WkAQ2iCJhbXzafemNmixRxS/DpgYK6pe7XhF +emlTdRF4J0YMJPfkpaoSNCpp1gIYt+xbnDxOA+72sOJJN1BH2w5oAA+JLYFkAZSC +pMG8eWGt/+9PcF2LFN+D1vW7K8nshmsoqS6OrjFWEP7C68Mjj6XFfL0vzODWntph +1wIUTc09QrjQzMnTwxpw +-----END CERTIFICATE----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.csr b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.csr new file mode 100755 index 00000000..1586ce87 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVjCCAT4CAQAwETEPMA0GA1UEAwwGUk9PVENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAhWEhjd+3TF6tuGdEL1GD9VgaeEemXSYpjI2iaYs2MqtS +6ge0QmUYmjgShikCkA+hOIwhLFB8Cy23BcsEIQFDGs53HThOBt+harW4+M1bz+rQ +b+DQNhS4LmqIVbriMxR5ReLn76rJnc1SXVgjyuLQQrVDPESGpnqhvkPIDVR1xcjk +lE3iNVH7Dy7oXH/xRO0h1RqW4JmBrW3z9IjeP80oTjZFpa/Vse3V64CBsR5VEB6Y +0qs97PlTTsoMu7/AIcvb6y4t+qMksKT3HUBsu+QGkRpbjFCcd3zacezRk2FcRU1n +jTuQaxwBQNBwcQ25nMO9vbo1uKjrTen+68oQp0wR7QIDAQABoAAwDQYJKoZIhvcN +AQELBQADggEBABXzWtLjaZuxW/WweDb+HCwb5bYBrYfPqqjby1acjl68eYlo0ZFM +kZYPKHmxp+NVbtGORAXvFDmNt9Kfguk0IfZse9n1jsfTozpHM6hGTQJGjPIxA9Sh +l91PNPCODnFy/bg9J9BEQ6HKK7+pUn3cY6BA3JWQ9hJyV1kP8Dir/zhoinhs7aUK ++f+FS7YNWwezqQacBFkZKybomikxkhREWMyWaRk+aflDvGRvGLZ8Wa0ykruyzJV7 +3Hpl+d3RSP7xq+r0ETBsJQ4HYwjRc1glJv7+BVyxZoIbpuPK0oyKafezHSThn+Ro +tRIWwZAjRGHvqpcortiiNR3sL8cXMFCUw1w= +-----END CERTIFICATE REQUEST----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.key b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.key new file mode 100755 index 00000000..e0d13983 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCFYSGN37dMXq24 +Z0QvUYP1WBp4R6ZdJimMjaJpizYyq1LqB7RCZRiaOBKGKQKQD6E4jCEsUHwLLbcF +ywQhAUMazncdOE4G36Fqtbj4zVvP6tBv4NA2FLguaohVuuIzFHlF4ufvqsmdzVJd +WCPK4tBCtUM8RIameqG+Q8gNVHXFyOSUTeI1UfsPLuhcf/FE7SHVGpbgmYGtbfP0 +iN4/zShONkWlr9Wx7dXrgIGxHlUQHpjSqz3s+VNOygy7v8Ahy9vrLi36oySwpPcd +QGy75AaRGluMUJx3fNpx7NGTYVxFTWeNO5BrHAFA0HBxDbmcw729ujW4qOtN6f7r +yhCnTBHtAgMBAAECggEACyvir2s3WJOuMZL2AI/vdrC4QtkezjrV4bpeiere5r6N +ICH2D1ZACca8MnV5NqTAfafVrXcDn2dm/CVlb/hBl8YG7wZvbp7zelMZ2Oj7vCia +6GFS6dIGx93PaSgmgwhqIe2Kkfs4ONng1gWoYHlqinPRMQPnp/D/NBxMQ2WOxGUw +tPo0NMwj7pKxNkXvACfbx3699DDnF9sLia2ZOn71MY2A7FHMN744eY6lOvzvJ/KT +m1Y0QPaiRUzJmY8w+jXTG/3Pcg/SPu+MiObgzeHkHBzNPLovFaMj1CJTJcuih/Od +4epcAliFtbHyrb/N1sl6X6z53bVEWOWXaGWC76efdwKBgQC5mqleNKzd24kEubp7 +DIvA7WuRmgKji7BHjBRE7ZRr85Fq+JkEH58S/4oY8uXpFC523s+t6CzVn7tgVGvV +ed6LcFeon6MUeEvNqpYow9ku/MbzPn+dOrasS9yEzI7ROoIpuHgdXd2s/Y7ukC4Z +gvVv4SGO3j1TeBO2pfUV6uXRjwKBgQC3964lPEUpot1L7R62fXTyJ+7wfoGibIps +jRcqV+EeZJt9LzBtthrMGqYOINfGWXVThjjfMX2H5Ig1CuUE5t2TLgW0jPWuE0/x +09KM77dclr9PVA3OKPNZoA1NTq/kVUJQeA9+vYfzitY5XR28Ak0qav6rQHjZ37Yi +jy5ARa5uwwKBgB79AZYm+U25X7EYSVhOCe9WNIWEzzf7FJ19d8ziVcuISRkxFGsp +1GdZnvb3Zwd5RSC3prkEcKfiGWjF75Me29cwFJKkxJegVheqiZOYz2QW9CicoLXh +nao6qEDL3nR0blME55kPmPlPBFQ0Yl4EDXJ2hiHSXS7Yd4IhR0A3jdNlAoGBAIcs +C978K+1t56BUOE7qW7VaNiyrJ2FK02LzQGQycgy866rNs43JUmNJ6V1UMHdjX9vh +MYR9frkM6C/hM2mooIH2PObu883WDtWnSHuZ32a+tQ76ubITUMs32M5G1OK26qTp +sqjzZiNCjilUC/cK0dwrBbibBLFBuTgncFp1WLe1AoGActRr0ocNGwchOLFcTwJ2 +hTVHlK0F7eoOgt+hBb7lPSW3jRJFxmJq65BUPTnYU2j+o34kHQnIt1reb5s5Lstu +e2h4xLO4vE8WMDp1WYwV6BgcEsVkbxAX5J/hsDzRPnGq49oVEz1uIup7uGZq9Bgr +xJ0fTr/SVy7uXF3T+zLZlAk= +-----END PRIVATE KEY----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.cer b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.cer new file mode 100755 index 00000000..a7198822 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.cer @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqzCCAZMCFGtUTWJsSz7Li0uWX+lN/rHcDmg4MA0GCSqGSIb3DQEBCwUAMBEx +DzANBgNVBAMMBlJPT1RDQTAgFw0yNDA2MDYwNzExMzhaGA8yMTI0MDUxMzA3MTEz +OFowETEPMA0GA1UEAwwGQ0xJRU5UMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAtRnzgCX/VsqbTqbDuVlzq2E30O01lzBMUpT6JAOao8nDs0euovUNh2QM +6CdElhNjeF3yWtVHgl4kAWRMbvdr9LarqiiVvHk6B5eO+tIhT8Nw7fuTbjpAtzjk +5Zk4yMjiy4voNzLKY6LuhCO2KtSF7Uo6krBb8RtSgW1kElkYrqBoX6q8Z0PmH+5w +jyjRpsOFVGYDVfIjiJyy0dAu1ifwSFvfgvikGLzkFg+QnUTtiGZ6jDSqHBgN+U4B +WDkIClzHlT3jJKZW/KSDMc68q8NTNTN2gKDU5Lo0wvQpNtmczEFVRImn6KXFn0fP +Ok4AI+NmSWwtAfs+2dm3gl/lHUoakQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB/ +RgRCBM21IqfhyCmIf0E0KzTAuRFSo4wgk0MTXddl0w8XDOdLAP4T1IXwPYKkwAtA +6pEhNpYep6kAy32k8NcbirlLp/URzwI7C8Z8+3l1xyl8GV0ivDfE1WPVWk9uwDwn +qJfuqi+BZF+9P0wtzCvov5bXlbaK0Gf65S12dKKw5GRERFU52YcQXUexd+Gnrr9x +Xgoa7qJ+7MzVB8QBx7Jsdbks7fFa371S/450cp4vYTYVF0MAOPOxzXg1BBtp8I+/ +TryUg/G/u/7dnals+GmucCgJ4AqNFLqOPsVwpZBXErdqJBDgxFSRQUGJ1DRHR9lP +8cx9TgbbU7wYLWP+rjbL +-----END CERTIFICATE----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.csr b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.csr new file mode 100755 index 00000000..034bc7a7 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVjCCAT4CAQAwETEPMA0GA1UEAwwGQ0xJRU5UMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtRnzgCX/VsqbTqbDuVlzq2E30O01lzBMUpT6JAOao8nD +s0euovUNh2QM6CdElhNjeF3yWtVHgl4kAWRMbvdr9LarqiiVvHk6B5eO+tIhT8Nw +7fuTbjpAtzjk5Zk4yMjiy4voNzLKY6LuhCO2KtSF7Uo6krBb8RtSgW1kElkYrqBo +X6q8Z0PmH+5wjyjRpsOFVGYDVfIjiJyy0dAu1ifwSFvfgvikGLzkFg+QnUTtiGZ6 +jDSqHBgN+U4BWDkIClzHlT3jJKZW/KSDMc68q8NTNTN2gKDU5Lo0wvQpNtmczEFV +RImn6KXFn0fPOk4AI+NmSWwtAfs+2dm3gl/lHUoakQIDAQABoAAwDQYJKoZIhvcN +AQELBQADggEBAFWS8ymjbYN2dQU4KwlCvXwVqWgCLhP8T9TsUQwigmmbeX9ylv+j +Y8UkTitNmOSI+KxSFHP85rRS28pWDUU7FERljUxQSZcBZX6Ibgj9VaVZnIX6sGi+ +rzIWuaZEe5303MiGjxzlFdISfIGJP1zdsmpSg94EvuMLT7Wx329wU9XpY8/QXzZx +pDvsNlpEXkRErTjuxJrP8izdCM59ANXQ8FO5ADQOrtOcJzg3T9CN4deQ6erSF5lV +8NBcxPJM91pdXCwwWZsANMr1Hzb5nJYI25e5vn1DiFunrhtUDI6OCuz+TAkDwqID +W/37OxZ9SO22kDKP4KN7Lgax1i+Wym2t3P8= +-----END CERTIFICATE REQUEST----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.key b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.key new file mode 100755 index 00000000..8395a0b2 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1GfOAJf9WyptO +psO5WXOrYTfQ7TWXMExSlPokA5qjycOzR66i9Q2HZAzoJ0SWE2N4XfJa1UeCXiQB +ZExu92v0tquqKJW8eToHl4760iFPw3Dt+5NuOkC3OOTlmTjIyOLLi+g3Mspjou6E +I7Yq1IXtSjqSsFvxG1KBbWQSWRiuoGhfqrxnQ+Yf7nCPKNGmw4VUZgNV8iOInLLR +0C7WJ/BIW9+C+KQYvOQWD5CdRO2IZnqMNKocGA35TgFYOQgKXMeVPeMkplb8pIMx +zryrw1M1M3aAoNTkujTC9Ck22ZzMQVVEiafopcWfR886TgAj42ZJbC0B+z7Z2beC +X+UdShqRAgMBAAECggEABPODboC7cSRDaDxTdA0PxkL3a3Ecyrghkg1sJFFr6Cfz +P4LJcb+Y4dd4qd0u+RppGQiagGS92oDX46jaFRuTGkLnQRTweRFoZn48hLt7eSqK ++xqqbnRNwiSeT2+nt4eUecOmsuGi2mQBOOAgEh4y1ii2Hr4PGXGpiQvUVVMVw2pn +8ult6mu701EiuvoG4nQ+tHwa0d4x6oY+PHx6CNIcw9wbAwG1NJRVzjU6Nr3LZ2GT +IjfkRbf/VNey/gnQ3lQkAZL1BUMOYoPwyUwtyRfX9Q4YDEV67hj6eQ3k4S9eQdVA +F6dkD+U7J4VPNer1G0EIQgLFuxl6MWpW0IebilpIxQKBgQDCxw2aKKq5s0xS6QEt +gO1ixl6YWFfSkNvPn5Q5M3kVSR7PYdTnELyJHjcBlVN0hZQSOitcj+PLyru0loXf +L7y8Dvbk6WLUBObfGK5Ya1Mkz20luv2TCjV/VhC23uFqGWruFakJTaqsXBta1xQb +hV1pmET2S23v3VRChdyTKIo9TQKBgQDuBm6m5D4ydCPOQ8hu9usmoTOhbkJe3fD/ +9AQ8CAiIxnagBr9WQvfVuqd0tdvy7ncJFjPz+9LzusPm2fHlXBIMfnXFlc0UzJ3q +N34ttIsI8UltHdw8hTK5EGUCh/DZHyMjgj+jPmPteGaJbfQhPYaXtR0bOkFBkkB8 +ZiVeUhLAVQKBgQCJYb7tWfu5SYbu/9vNzZ8iSUqVZ9Jh+bygwCza/7xK0C1EFvwF +Ep/4kvA3VKzthf4/sPm+qtsAdflZUcYQ+unDN0bbhOYpJ1/0QcNPKDwXvZp9v1t1 +qAC91OWrJp+Vp2HtlieJIVfdhIXi18sd8j5F6ZnSordjNRhmZ2aCAVkoDQKBgH4E +R8bx5f/+PwqwFkixT3PnOpeH8XmaVUKdZTSHKEWJlTpJ+DjqQZ4VMRz4P1eRatIT +wDO3KktBoP/yeT/uC9wzg5S1J5uQbTaYZKQ8BrQYUsCDY5tjBaS3ClNZt2isNIVT +Ku/5Uxxhdx/hZ7PFiCF/kMZehDf7/0odJkGWgDj1AoGBAIya8y+vrRJk0XeSlpFv +99tPPg9hQJThsc3JM+1XBXT0XkbiJAR4lKihuiJBm+CpsbAkLqfnz/EdE3YYC3UH +XYxu11VjQnEN1uhXMCqB35LHmnRHuiFxOaes/w889CGF2Ns4mvsLOq48QER0f3jH +1+Q6NOuao6xu5N9jypPt3ZIR +-----END PRIVATE KEY----- diff --git a/test/mtls/certs/renew.sh b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/generate-mtls.sh similarity index 58% rename from test/mtls/certs/renew.sh rename to libs/backend-apisix/e2e/assets/apisix_conf/mtls/generate-mtls.sh index 2357a668..939cf217 100755 --- a/test/mtls/certs/renew.sh +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/generate-mtls.sh @@ -1,22 +1,18 @@ #!/bin/bash +# For ROOT CA openssl genrsa -out ca.key 2048 openssl req -new -sha256 -key ca.key -out ca.csr -subj "/CN=ROOTCA" -openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.cert +openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.cer -# Server certs +# For server certificate openssl genrsa -out server.key 2048 -openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=127.0.0.1" -openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cert -CAkey ca.key -CAserial ca.srl -CAcreateserial -in server.csr -out server.cert +openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=localhost" +openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cer -CAkey ca.key -CAserial ca.srl -CAcreateserial -in server.csr -out server.cer -# Client certs +# For client certificate openssl genrsa -out client.key 2048 openssl req -new -sha256 -key client.key -out client.csr -subj "/CN=CLIENT" -openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cert -CAkey ca.key -CAserial ca.srl -CAcreateserial -in client.csr -out client.cert +openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cer -CAkey ca.key -CAserial ca.srl -CAcreateserial -in client.csr -out client.cer -# Concat CA -cat ca.cert > ca.ca -cat ca.key >> ca.ca - -# Remove unused files -rm -f ca.csr ca.srl client.csr server.csr +chmod -R 777 . diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.cer b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.cer new file mode 100755 index 00000000..5b5d3d24 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.cer @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrjCCAZYCFDvgCOozSi7reDVf+3IBVoi1pxg1MA0GCSqGSIb3DQEBCwUAMBEx +DzANBgNVBAMMBlJPT1RDQTAgFw0yNDA2MDYwNzExMzhaGA8yMTI0MDUxMzA3MTEz +OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA8TTjrcxP1n+/Rh7f7S0WIG589Ne+toBi2MXO1maJ3/6iZGGfhAlu +ofOg59OpEfoz6WgCOiVdwQvPAEHs6XXw+mS5B3oEIB9/WMrU6P6Ew/zYNjVyxhT0 +dGQg1ug7SnqOdpieGRk7f+2qRDXt+j7TybeEGZ81lUXrDLqPnkC5sqJAQCbj1X2s +tEWMquROH56kp99lL/PF1swVwNa+pmoKnc+S3vhcMK2PvkfGKQ/HJAoVMvyF9rwR +Mr9cuEmXm0irqIMd/cy7y4Sfvk5snPfKzJ40HUl0tHkmxkQV2SvSO9soJH/4XmIm +YRDN2HvA2zZfGTxz4mzbnIMcI+xieleFKQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB +AQBQVw/bIaFZx8JbdNqRwGy0ryZT7U/D5fdqKel1kWwjdTFMldtH1jX4LMuBR/LE +Iscsv2pXypGjuSPmPtHl9+fcg31d5VFFpg+SvLkifyP7iPZoe/cr5erK/ytoJD35 +64APQuYzz3+BzgLLHhOyv5wAr+Bb9cSweXGyyKHPB+zVp8LthYMOnmlIxXrKCnmi +skRtEmI3RPhMF/Vrkm44mkYB19P7cgzawuvxn3ooYHBjI0pLsoxRXyj5QJurJGsq +6Qkt2omsqNCZuz2r6m8jHXdtGMKjQm+KUww5ntDo68r2xHpftCMPgX5EKeUxvlAD +BT6Rk9y/p4Gz2jsFsJLfKjwu +-----END CERTIFICATE----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.csr b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.csr new file mode 100755 index 00000000..701dee4f --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA8TTjrcxP1n+/Rh7f7S0WIG589Ne+toBi2MXO1maJ +3/6iZGGfhAluofOg59OpEfoz6WgCOiVdwQvPAEHs6XXw+mS5B3oEIB9/WMrU6P6E +w/zYNjVyxhT0dGQg1ug7SnqOdpieGRk7f+2qRDXt+j7TybeEGZ81lUXrDLqPnkC5 +sqJAQCbj1X2stEWMquROH56kp99lL/PF1swVwNa+pmoKnc+S3vhcMK2PvkfGKQ/H +JAoVMvyF9rwRMr9cuEmXm0irqIMd/cy7y4Sfvk5snPfKzJ40HUl0tHkmxkQV2SvS +O9soJH/4XmImYRDN2HvA2zZfGTxz4mzbnIMcI+xieleFKQIDAQABoAAwDQYJKoZI +hvcNAQELBQADggEBABtrExq53/Cq/gcowGU+eIlamO+JH5vpZDL1l80PxQ3qE2R2 +UZwN8ocldAvcvYs0YPcBGOkNhBdqrcHaHx28el1Xu3NbUwg+YK47sbj/zcOsQEYd +MvCXtmd1r7MGyvfrsqir3fAFHry5ppIlGj47/sr39/Nv+rNGFU3lYBkmukhvOLpz +JhC6sUwmjN+nn68feYrAOtr7EnCf/D0Y711IWmdw18D/KxCm2Cl8DHVH60Tzdj+2 +0vXm9sKrN+Xzg/l7849wEUIXTCQROHdQCLuJ8bkk3T5RsnTqePUJzwnnC0V6HfMN +cJNVmV1zuf5w/gxEGEilAmytlYbPYCSJPNTrSC4= +-----END CERTIFICATE REQUEST----- diff --git a/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.key b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.key new file mode 100755 index 00000000..032a3674 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/apisix_conf/mtls/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxNOOtzE/Wf79G +Ht/tLRYgbnz01762gGLYxc7WZonf/qJkYZ+ECW6h86Dn06kR+jPpaAI6JV3BC88A +QezpdfD6ZLkHegQgH39YytTo/oTD/Ng2NXLGFPR0ZCDW6DtKeo52mJ4ZGTt/7apE +Ne36PtPJt4QZnzWVResMuo+eQLmyokBAJuPVfay0RYyq5E4fnqSn32Uv88XWzBXA +1r6magqdz5Le+FwwrY++R8YpD8ckChUy/IX2vBEyv1y4SZebSKuogx39zLvLhJ++ +Tmyc98rMnjQdSXS0eSbGRBXZK9I72ygkf/heYiZhEM3Ye8DbNl8ZPHPibNucgxwj +7GJ6V4UpAgMBAAECggEAALjT+PhfFs5xaqAmCHDgRj7pFdcy0Btd8GCAiCjCFI8P +umAxGN/pr7IUfbfRo5Gi/SRdZAIqSoJ4Cfv93bcHfnW1uL2B7Kdbe2RkCGyBRjro +BMWFL0LvIQ1kVxsBLm25JnFk1mtlFQgc6zEEIix0+CoZ9Z+sio1X25plWqe+m+Jv +SkfRQQ1GtQenjzc+WF+TZT0gP33c0eQwbY0AZSSE/FJtmczfRe19+GkZfHZfukIg +raLIp3HZFq7eDeZrO/lCsDdiwl8H/dA1gtMC1bwku+qVES4YtskL1c46x7Id8FdL +m1nWjUf/21NGEJns+OoTA3lZ2KlpCMHJKr/PGSHrWQKBgQD7PDGnIW9ir45Yf6Y9 +9fNVUNZLM8ecvKaMicesPsA/GmmSBGJFiY7QmyCPc2/eI1PXFNHCVlAoykaz07Nr +Y+nWf+QD5QciDSdCos9MYnbpa/whVnIlLeGCXJKM09kkaDQ4vN6ReEjpm96n3+xX +R9X99SXiUtKmnPKw4mLnwOlUzQKBgQD1yAEnjwp4mXM5/kJVZLcjpYaeEqX9fOF5 +5O9jIKbMzsRIVNuX5ItpyTvLLr0GaRWXYoMga1kov4eTOojygD2PlpSxk+8a7N2z +oTtGZ8WmypQswImVR9Ris/6jADzAzeiwWxZT0gV7bBE7O1iBfgAevWLtw+cqH1Y6 +RbNwXFERzQKBgQCK1GV/vJsnhml7f/ZmcN3pPEVewxtAAoNqT8y14usrM7Y4yRFg +6bWwkrh4bMrZjt4KkWekIzwifjx5rLeN1WVncb6XZFz/tRMH4J360MJzFIf8CCAF +aYgfGHanOX3Zf3e0DrJS4owwA0ETtUqNpJWcw3YOzcO37Cy0EDWlaVXG1QKBgQCk +HY3vzUrPpp9TXR0MCjlj2xZdnNQrxGSG0UCr71SRs4tLRSZwcVJKK+36SVY83pRl +RomKb0PUurebrt1dGBaDN6hIPyDM2NddJ879vzMyoVh53YLBJHqEAe6JBxKKJ7Q1 +dk0dYUL52/pRk9oQdYM9A3b4jvRfoxcfyAT+hRY5DQKBgHtMa6mn0vxUFd77INOP +DGG3Ostf6MwA7tS/TnVCnFNZoZnUb00y3fq3rOqBWA75Aqf3pCSl8kk1dHPeh1GU +zbGEtWFo20wendCJGim+vVs4GSp0RUV9rkT/eB3D4lPjLzVQA8AlGew9T2ZMqomo +jv5ta8/TiGiqpVLP62L2TKt2 +-----END PRIVATE KEY----- diff --git a/libs/backend-apisix/e2e/assets/docker-compose.yaml b/libs/backend-apisix/e2e/assets/docker-compose.yaml new file mode 100644 index 00000000..457520a6 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/docker-compose.yaml @@ -0,0 +1,49 @@ +services: + apisix_http: + image: apache/apisix:${BACKEND_APISIX_VERSION:-3.9.0-debian} + restart: always + volumes: + - ./apisix_conf/http.yaml:/usr/local/apisix/conf/config.yaml:ro + depends_on: + - etcd + ports: + - "19180:9180/tcp" + - "19080:9080/tcp" + networks: + apisix: + + apisix_mtls: + image: apache/apisix:${BACKEND_APISIX_VERSION:-3.9.0-debian} + restart: always + volumes: + - ./apisix_conf/mtls.yaml:/usr/local/apisix/conf/config.yaml:ro + - ./apisix_conf/mtls:/mtls:ro + depends_on: + - etcd + ports: + - "29180:9180/tcp" + - "29080:9080/tcp" + networks: + apisix: + + etcd: + image: bitnami/etcd:3.5 + restart: always + volumes: + - etcd_data:/bitnami/etcd + environment: + ALLOW_NONE_AUTHENTICATION: "yes" + ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379" + ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379" + ports: + - "2379:2379/tcp" + networks: + apisix: + +networks: + apisix: + driver: bridge + +volumes: + etcd_data: + driver: local diff --git a/libs/backend-apisix/e2e/assets/test-ssl.cer b/libs/backend-apisix/e2e/assets/test-ssl.cer new file mode 100644 index 00000000..3213d2ac --- /dev/null +++ b/libs/backend-apisix/e2e/assets/test-ssl.cer @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpzCCAY8CFFCd8b84WQ0G61xWBmnzrOPXLtIYMA0GCSqGSIb3DQEBCwUAMA8x +DTALBgNVBAMMBHRlc3QwIBcNMjQwNTMwMDgxODQ4WhgPMjEyNDA1MDYwODE4NDha +MA8xDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCBSSfjbqcLgvn+vIB65/lp0BTsQyIZ0G+wr1cMkN1WRBapSaKurtHDDrOnOUCS +rbEeRy7A5T6DrJZl5aoxhilaN9P/tNsIjqXA3MRSMWMkE+PJtR5Pab4SrVWY8Tty +fT0F8RnyMHNWOLtjUbxqNhE5yFUKczfi55bvVqj6xiYqW1oAluUuGBUnTfoNAFrz +0urt6RRp/wphJu03++HKswTXfKcdbDxPMaLxfkTl7ot+tvFLmeUbTHQLv3ZK7H+m +knwVHmLfPreCp/mGIuFXsQNaLPqH58/qk1t5kT1YzvXiLEIRZJGyp6ONMMEqGEpx +g9+n83FRoLDGNZLGnzwm7L/5AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAACTC46X +uuYETXnJSP+rX3HwGxCJrdFvpL0KMfMAakNptXxcY4em7Yzery2ewp3+9fb9dyqn +Jpg0AWFKEhy6knxZjdXld6uinu1wbAbi7X8+AdqCylti9YtkuU7o5RETmaaAuedE +14m69u8fdrI0W8ep6tWn0IbZqIBh+ODYCmwzOIzC54cpAmxBytpfxW7ncMLO2vTY +MIB6Tb2GaIJPvkwQtyYoopRUSQIOJa4hJtUy0vunrSXxRNEDlubSRZJnUdHlE6xq +HEUKi6JG2XnVgtle4qgz4nWlB/FY/nUTXKT63qIbYOahviD2Ov7d2PSUdB745nO7 +kTbbHbxQ9PGEfNo= +-----END CERTIFICATE----- diff --git a/libs/backend-apisix/e2e/assets/test-ssl.csr b/libs/backend-apisix/e2e/assets/test-ssl.csr new file mode 100644 index 00000000..d1439c93 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/test-ssl.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVDCCATwCAQAwDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAIFJJ+NupwuC+f68gHrn+WnQFOxDIhnQb7CvVwyQ3VZEFqlJ +oq6u0cMOs6c5QJKtsR5HLsDlPoOslmXlqjGGKVo30/+02wiOpcDcxFIxYyQT48m1 +Hk9pvhKtVZjxO3J9PQXxGfIwc1Y4u2NRvGo2ETnIVQpzN+Lnlu9WqPrGJipbWgCW +5S4YFSdN+g0AWvPS6u3pFGn/CmEm7Tf74cqzBNd8px1sPE8xovF+ROXui3628UuZ +5RtMdAu/dkrsf6aSfBUeYt8+t4Kn+YYi4VexA1os+ofnz+qTW3mRPVjO9eIsQhFk +kbKno40wwSoYSnGD36fzcVGgsMY1ksafPCbsv/kCAwEAAaAAMA0GCSqGSIb3DQEB +CwUAA4IBAQBa5QxWUf/RgSEidEH6PI552XUPyql1MSxodA6LZyuDM5gu/a8AJWun +1/HvCZyW3eqbGbEPWR5M/zz0Hd8lKVF48Okq58rOLm0HwWKM62TizAAK/z7jD3AA +nwG/Sy6hSspsptz5w6thV6PPLA118QmKhl3m4AgcgGevhYRbVvNFP8/RZ8J/CENq +wH/qy69C2XA7JTWhMETNXv34/IgpqqsvBUNZtXjoHyKwVaRZialVx/oVEkpZx62M +qwQv7iE8i09cjsG3PwVZNCv3OsPIrgzWYZRNUtwo/K9etCxHqKYbId6ZmRxz47Xy +cR/iz4n6XzWMlrf7wQwGZ4pWCnGDl5Li +-----END CERTIFICATE REQUEST----- diff --git a/libs/backend-apisix/e2e/assets/test-ssl.key b/libs/backend-apisix/e2e/assets/test-ssl.key new file mode 100644 index 00000000..4c8e75a9 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/test-ssl.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCBSSfjbqcLgvn+ +vIB65/lp0BTsQyIZ0G+wr1cMkN1WRBapSaKurtHDDrOnOUCSrbEeRy7A5T6DrJZl +5aoxhilaN9P/tNsIjqXA3MRSMWMkE+PJtR5Pab4SrVWY8TtyfT0F8RnyMHNWOLtj +UbxqNhE5yFUKczfi55bvVqj6xiYqW1oAluUuGBUnTfoNAFrz0urt6RRp/wphJu03 +++HKswTXfKcdbDxPMaLxfkTl7ot+tvFLmeUbTHQLv3ZK7H+mknwVHmLfPreCp/mG +IuFXsQNaLPqH58/qk1t5kT1YzvXiLEIRZJGyp6ONMMEqGEpxg9+n83FRoLDGNZLG +nzwm7L/5AgMBAAECggEAAgMJDgc+T5d++gC54/MbO378B7xW3Vou9HKL9agnBxMA +6SsRtDqBk/oQAzYdBoz9gDzbOz2IihZwkH/KRaP3nbfX82SQaG7keLEpUO8sVsfB +FmHIQVWvp5I5Xz/BVzncpAeA7xHWQuZvbBK3LMdQMP55WBj4e7g9TF1TjW0nosgi +UhtPkxAvgVU+yVGrIWVnwDtaEZ3FjWbIlehsm3b6vCbbbnI7ov0hJocAw6DLLIMv +roLrPGb+jDLQcfVbEsXb1m0cv3njiKTSnX46Jv9Es7OadQILMSks5WKEGU4QESHm +ZsBPp2J6S4ZAPZqa+K9UU1GLcEu2ctZatJJt5WpGfQKBgQC1DL4np5h6r41KA2+F +cYaRTXHVemw4axCDUrwhgpE+vJgNSjSJlzV2YBH43yFGN71p/iTYgMmtuqXc+nnh +frLsnw/VRmRmTH9jFL6jJsenPM0VWa2I80G41GwyQCbkSFC9yswPnNfqpI/yEvwl +WWGVfwZdVhxR+NDmgmYYiFxyLwKBgQC2zpOvM+GA58h6mbH2IBFt6xkyWm3JF63w +psGH5XdZAsdSsn9gV4iQiI8ntUq8ypBQ4YAUBu7YK4csIAZJ1Xaqg06w6L3ezEwn +d0wMlAmvSfdWw+hXhYqW1rQVCWVxLVigy2d78F0B9GvPdP17WHpXjWEtC5IbwtIW +NxICCPquVwKBgGRbs+x3UmZveXpVfTQTQey5aShUvDhp4LVq3y4UGj8Ue5SiVY0P +bGs5xbyWsvMhYrWmUvZQLXtHwzlC/tyoRrYzVat7YZvzO4nIrgPyMynY4jW7iB4p ++XxjA7a3KNfR3i/WglcFMgq3PbOKIorxiM49o2GJohkuO14LWlyu+c+xAoGARgyo +qNVPjw7CUsB8YhztsNoKwLHIKeuJ+pJbzJBQiGo5KOY/SxKfwQqjGIsuxWM1o9iV +NJdnhKxBUtY+sqI6o6JHUCEPSMWuVN0w5g0AHQxR3AycBpWkgqJpRTphhdnjgVIQ +KHz1lt8rRMbGrzI7RztkxlcNuNeRm3Ok0eTQ8mcCgYAwZZtkr5gAikg1wb9L8f/p +scaVa/bnBFbGjO+MO4lUwh4UH2w7Ge6zUzbEeNcPypZabh/xBPFitCqgLb2kxfDx ++Xai4H/dzKAgS7uOeHUf5d5nFG+Nf/DmTxF/tQuQzXG3Ar7I8YoDf19XUYU/XVsE +HHotW/xILpazJVPj0NhX6w== +-----END PRIVATE KEY----- diff --git a/libs/backend-apisix/e2e/assets/testdata/mixed-1-clean.json b/libs/backend-apisix/e2e/assets/testdata/mixed-1-clean.json new file mode 100644 index 00000000..52208877 --- /dev/null +++ b/libs/backend-apisix/e2e/assets/testdata/mixed-1-clean.json @@ -0,0 +1,72 @@ +[ + { + "resourceType": "route", + "type": "delete", + "resourceId": "9764db8eeb0f13c2a9762161c418504d74ac64a2", + "resourceName": "route1.2", + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "delete", + "resourceId": "801b75f6eb1432c889cd15d3cffb2fa28b7bf95c", + "resourceName": "route1.1", + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "delete", + "resourceId": "12e10811f0bf2d39998936bf81e33314ad7ff6ae", + "resourceName": "route2.2", + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "route", + "type": "delete", + "resourceId": "143e485dfbcace6afb3d40cd26a20cb6f3d8a469", + "resourceName": "route2.1", + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "service", + "type": "delete", + "resourceId": "6e899c1108b88e75d4887b85f9a62c26d9571739", + "resourceName": "service1" + }, + { + "resourceType": "service", + "type": "delete", + "resourceId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a", + "resourceName": "service2" + }, + { + "resourceType": "consumer", + "type": "delete", + "resourceId": "tom", + "resourceName": "tom" + }, + { + "resourceType": "ssl", + "type": "delete", + "resourceId": "5f543afdb6ba8aeef955c6c951d3bd70c1de8361", + "resourceName": "test.com" + }, + { + "resourceType": "global_rule", + "type": "delete", + "resourceId": "prometheus", + "resourceName": "prometheus" + }, + { + "resourceType": "plugin_metadata", + "type": "delete", + "resourceId": "tcp-logger", + "resourceName": "tcp-logger" + }, + { + "resourceType": "plugin_metadata", + "type": "delete", + "resourceId": "http-logger", + "resourceName": "http-logger" + } +] diff --git a/libs/backend-apisix/e2e/assets/testdata/mixed-1.json b/libs/backend-apisix/e2e/assets/testdata/mixed-1.json new file mode 100644 index 00000000..870dce4d --- /dev/null +++ b/libs/backend-apisix/e2e/assets/testdata/mixed-1.json @@ -0,0 +1,307 @@ +[ + { + "resourceType": "ssl", + "type": "create", + "resourceId": "5f543afdb6ba8aeef955c6c951d3bd70c1de8361", + "resourceName": "test.com", + "newValue": { + "type": "server", + "snis": ["test.com"], + "certificates": [ + { + "certificate": "-----BEGIN CERTIFICATE-----\nMIICrTCCAZUCFCcH5+jEDUhpTxEQo/pZYC91e2aYMA0GCSqGSIb3DQEBCwUAMBEx\nDzANBgNVBAMMBlJPT1RDQTAgFw0yNDAxMTgwNjAzMDNaGA8yMTIzMTIyNTA2MDMw\nM1owEzERMA8GA1UEAwwIdGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCVkfufMRK2bckdpQ/aRfcaTxmjsv5Mb+sJdhb0QuEuXp/VgN3yzFM0\nzCmAeBZwNKpU3HZDv0tnkTx7OARYpj5Bw1ole0EfPVPKBRjlLE56tabzyd4vdLV2\nbk7jYH+H8NjGZNEYLm9MdWiB4Ulyc0+XFA0ZL5WWKOi+oSQVUibT8QK0CENFKNLP\nQjEXlbyujzRS3u6r99EEEy8+3psBA2EELq8GAjEp+jilWggBhUEpLQxCHhHeNevR\nkg5iEvhOhEVKtr5xvgolg5Wvz7GmDulIW9MCu0dIXim52H/spPwgi3yRraY1XjxU\nREyj5tcY7n7LBESkx/ODXEyCkICIPpo9AgMBAAEwDQYJKoZIhvcNAQELBQADggEB\nADBU5XvbnjaF4rpQoqdzgK6BuRvD/Ih/rh+xc+G9mm+qaHx0g3TdTqyhCvSg6aRW\njDq4Z0NILdb6wmJcunua1jjmOQMXER5y34Xfn21+dzjLN2Bl+/vZ/HyXlCjxkppG\nZAsd1H0/jmXqN1zddIThxOccmRcDEP+9GT3hba50sijFbO30Zx+ORJCoT8he6Kyw\nKdOs/yyukafoAtlpoPR+ao/kumto6w/rLfFlEsehU0dMGNgPVSxxVNtBSdxPTUBk\nD6mfqB4f//2DuAmiO+l5RmPUmumqzcYlpd+oAdy3OSnNEHbgxishZr/GI3s6DmUh\n16bgI69aQ5F+MnN3trvaufc=\n-----END CERTIFICATE-----\n", + "key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVkfufMRK2bckd\npQ/aRfcaTxmjsv5Mb+sJdhb0QuEuXp/VgN3yzFM0zCmAeBZwNKpU3HZDv0tnkTx7\nOARYpj5Bw1ole0EfPVPKBRjlLE56tabzyd4vdLV2bk7jYH+H8NjGZNEYLm9MdWiB\n4Ulyc0+XFA0ZL5WWKOi+oSQVUibT8QK0CENFKNLPQjEXlbyujzRS3u6r99EEEy8+\n3psBA2EELq8GAjEp+jilWggBhUEpLQxCHhHeNevRkg5iEvhOhEVKtr5xvgolg5Wv\nz7GmDulIW9MCu0dIXim52H/spPwgi3yRraY1XjxUREyj5tcY7n7LBESkx/ODXEyC\nkICIPpo9AgMBAAECggEADKrg+zIOSioRKvQUXCGp1G4Xl1AtDm86IkKAni5d4O39\nrnjsE0iv0VxrNWi2ScmEFYzYbVNxwPNBgpQTdiiaRDqV02/Va55njsgQvDQc73Kc\nEbCqoy1Iwx+Dield07cXvPHD7b7dCUY2VC2u6UUP3BVEKLppel67m42NP0sGY9O4\nALutC42zU8BFLRnt0koSfcTdEcwFPFDQ0N8H2wI/Z7Y65QniilUqD4AS/SH8MesO\n4pjEXhZz9ok7WgduK2LVukGvt1QedTsRh8qwHm3PgT/pF2+35eeT0nYTz2VscPBI\ngazEtLQFLhtXZ4aH3MUTsXVkQLEe7XeILHrsnCGbBwKBgQC7q8LzHFdVHc46qqiB\nppMfGEYlV4LCoWbHfEl+nPznKCtcjrN8unoN6nyGq+lt2bwUqZdnpvYSqydPMujH\nCAWnRIX+7H4ELIRKV62oVibd2ovfubMGIzCGUTObl8Vn2m/ovtMwZn9mvPCY4hve\n8TY80apdZae+Ew0Bfn2ZNDo+vwKBgQDMBviZdbE78tONwfKbfDeLVN62/TC5mQad\nIzz2G5AbolTKq6qByJlO7ZfcjdsLJTpeKfbzU2pHMpWNnTjN0mHgyBFuvLgoaN1n\n2qXnOPyv8Xir/N7eHZ2K9lvGrSPY4vszzLTabKqVYiufJYjb0okH0AWg8+4FxIHb\n+gms0eWiAwKBgEu3U7MUByQfH1pKCiws0YSlHX/pW6c8ySPIwDomCl8UtNHl/QJg\nlefRaCZJa6dXRmurtJssIHGNvhFU/9d1JBrFKa6dKYZzk3gPAdA92fZ+OxqraFAc\nmHJIhqLKy+lHlwj3HGuVnucLaaK07vu2o+RLzwlZfyDPvNqSdwf9q1YvAoGAdoia\nR7XbuUNzawlB5Nl+/6DYH6Hre/iOoh6F3UnYKGXgMzsWvX4Iq5VXxBhaKRiA15Iz\n2dwdg628u2CbTuCqYsh5cEeOClQaRar+9d3i2GlGvU0VQiAclk5YtY2DpQ8B+G82\npyu40z8MrtJEt8mSOQq/KmptX8Zx15ZlppTvf3cCgYBYxCWn49saCr0FnhIheenc\njlExhQ3ebcwJBSuzNKd2y94zUrQScVpxg27ZAXUfJpppcoY73elvYnczRe3JNaVY\n1b+66bQSAevT5GE8n2C3olLtNmZMvT41Pra0s3St0YnyPtH8acgeHw8Vy5/0/MhZ\nm7/qElKtlWlmUQO1CLdZhQ==\n-----END PRIVATE KEY-----\n" + } + ] + } + }, + { + "resourceType": "service", + "type": "create", + "resourceId": "6e899c1108b88e75d4887b85f9a62c26d9571739", + "resourceName": "service1", + "newValue": { + "name": "service1", + "description": "service1 description", + "upstream": { + "name": "default", + "scheme": "http", + "type": "roundrobin", + "hash_on": "vars", + "nodes": [ + { + "host": "host", + "port": 1100, + "weight": 1100, + "priority": 0 + } + ], + "retry_timeout": 0, + "pass_host": "pass", + "checks": { + "active": { + "type": "tcp", + "timeout": 1, + "concurrency": 10, + "http_path": "/", + "https_verify_certificate": true, + "healthy": { + "interval": 1, + "http_statuses": [200, 302], + "successes": 2 + }, + "unhealthy": { + "interval": 1, + "http_statuses": [429, 404, 500, 501, 502, 503, 504, 505], + "http_failures": 5, + "tcp_failures": 2, + "timeouts": 3 + } + } + } + }, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + }, + "routes": [ + { + "uris": ["/anything"], + "name": "route1.1", + "description": "", + "methods": ["GET"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + { + "uris": ["/anything"], + "name": "route1.2", + "description": "", + "methods": ["POST"], + "enable_websocket": false + } + ] + } + }, + { + "resourceType": "service", + "type": "create", + "resourceId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a", + "resourceName": "service2", + "newValue": { + "name": "service2", + "description": "service2 description", + "upstream": { + "name": "default", + "scheme": "http", + "type": "roundrobin", + "hash_on": "vars", + "nodes": [ + { + "host": "host", + "port": 1100, + "weight": 1100, + "priority": 0 + } + ], + "retry_timeout": 0, + "pass_host": "pass" + }, + "routes": [ + { + "uris": ["/getSomething"], + "name": "route2.1", + "description": "", + "methods": ["GET", "POST"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + { + "uris": ["/postSomething"], + "name": "route2.2", + "description": "", + "methods": ["POST", "PUT"], + "enable_websocket": false + } + ] + } + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "801b75f6eb1432c889cd15d3cffb2fa28b7bf95c", + "resourceName": "route1.1", + "newValue": { + "uris": ["/anything"], + "name": "route1.1", + "description": "", + "methods": ["GET"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "9764db8eeb0f13c2a9762161c418504d74ac64a2", + "resourceName": "route1.2", + "newValue": { + "uris": ["/anything"], + "name": "route1.2", + "description": "", + "methods": ["POST"], + "enable_websocket": false + }, + "parentId": "6e899c1108b88e75d4887b85f9a62c26d9571739" + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "143e485dfbcace6afb3d40cd26a20cb6f3d8a469", + "resourceName": "route2.1", + "newValue": { + "uris": ["/getSomething"], + "name": "route2.1", + "description": "", + "methods": ["GET", "POST"], + "enable_websocket": false, + "plugins": { + "limit-count": { + "_meta": { + "disable": false + }, + "allow_degradation": false, + "count": 2, + "key": "$consumer_name $remote_addr", + "key_type": "var_combination", + "policy": "local", + "rejected_code": 503, + "show_limit_quota_header": true, + "time_window": 60 + } + } + }, + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "route", + "type": "create", + "resourceId": "12e10811f0bf2d39998936bf81e33314ad7ff6ae", + "resourceName": "route2.2", + "newValue": { + "uris": ["/postSomething"], + "name": "route2.2", + "description": "", + "methods": ["POST", "PUT"], + "enable_websocket": false + }, + "parentId": "45d48919804fadc2db9d0180a65fe0ee0a8ddf9a" + }, + { + "resourceType": "consumer", + "type": "create", + "resourceId": "tom", + "resourceName": "tom", + "newValue": { + "username": "tom", + "description": "", + "plugins": { + "key-auth": { + "key": "tom" + } + } + } + }, + { + "resourceType": "global_rule", + "type": "create", + "resourceId": "prometheus", + "resourceName": "prometheus", + "newValue": { + "_meta": { + "disable": false + }, + "prefer_name": false + } + }, + { + "resourceType": "plugin_metadata", + "type": "create", + "resourceId": "http-logger", + "resourceName": "http-logger", + "newValue": { + "log_format": { + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr", + "host": "$host" + } + } + }, + { + "resourceType": "plugin_metadata", + "type": "create", + "resourceId": "tcp-logger", + "resourceName": "tcp-logger", + "newValue": { + "log_format": { + "@timestamp": "$time_iso8601", + "client_ip": "$remote_addr", + "host": "$host" + } + } + } +] diff --git a/libs/backend-apisix/e2e/ping.e2e-spec.ts b/libs/backend-apisix/e2e/ping.e2e-spec.ts new file mode 100644 index 00000000..ab07bff9 --- /dev/null +++ b/libs/backend-apisix/e2e/ping.e2e-spec.ts @@ -0,0 +1,62 @@ +import { join } from 'path'; + +import { BackendAPISIX } from '../src'; +import { server, token } from './support/constants'; + +describe('Ping', () => { + it('should success (http)', async () => { + const backend = new BackendAPISIX({ + server, + token, + }); + await backend.ping(); + }); + + it('should success (mTLS)', async () => { + const backend = new BackendAPISIX({ + server: 'https://localhost:29180', + token, + tlsClientCertFile: join(__dirname, 'assets/apisix_conf/mtls/client.cer'), + tlsClientKeyFile: join(__dirname, 'assets/apisix_conf/mtls/client.key'), + caCertFile: join(__dirname, 'assets/apisix_conf/mtls/ca.cer'), + }); + await backend.ping(); + }); + + it('should failed (invalid server)', async () => { + const backend = new BackendAPISIX({ + server: 'http://0.0.0.0', + token: '', + }); + await expect(backend.ping()).rejects.toThrow( + 'connect ECONNREFUSED 0.0.0.0:80', + ); + }); + + it('should failed (self-signed certificate)', async () => { + const backend = new BackendAPISIX({ + server: 'https://localhost:29180', + token, + }); + await expect(backend.ping()).rejects.toThrow( + 'unable to verify the first certificate', + ); + }); + + it('should failed (miss client certificates)', async () => { + const backend = new BackendAPISIX({ + server: 'https://localhost:29180', + token, + caCertFile: join(__dirname, 'assets/apisix_conf/mtls/ca.cer'), + }); + + try { + await backend.ping(); + } catch (err) { + expect(err.toString()).toContain('Request failed with status code 400'); + expect(err.response.data).toContain( + 'No required SSL certificate was sent', + ); + } + }); +}); diff --git a/libs/backend-apisix/e2e/support/constants.ts b/libs/backend-apisix/e2e/support/constants.ts new file mode 100644 index 00000000..9c27e656 --- /dev/null +++ b/libs/backend-apisix/e2e/support/constants.ts @@ -0,0 +1,2 @@ +export const server = 'http://localhost:19180'; +export const token = 'edd1c9f034335f136f87ad84b625c8f1'; diff --git a/libs/backend-apisix/e2e/support/global-setup.ts b/libs/backend-apisix/e2e/support/global-setup.ts new file mode 100644 index 00000000..9223ddb3 --- /dev/null +++ b/libs/backend-apisix/e2e/support/global-setup.ts @@ -0,0 +1,3 @@ +export default async () => { + //empty +}; diff --git a/libs/backend-apisix/e2e/support/global-teardown.ts b/libs/backend-apisix/e2e/support/global-teardown.ts new file mode 100644 index 00000000..9223ddb3 --- /dev/null +++ b/libs/backend-apisix/e2e/support/global-teardown.ts @@ -0,0 +1,3 @@ +export default async () => { + //empty +}; diff --git a/libs/backend-apisix/e2e/support/utils.ts b/libs/backend-apisix/e2e/support/utils.ts new file mode 100644 index 00000000..83100439 --- /dev/null +++ b/libs/backend-apisix/e2e/support/utils.ts @@ -0,0 +1,83 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Listr, SilentRenderer } from 'listr2'; + +import { BackendAPISIX } from '../../src'; + +export const runTask = async (tasks: Listr, ctx = {}) => { + //@ts-expect-error just ignore + tasks.renderer = new SilentRenderer(); + await tasks.run(ctx); + return tasks.ctx.local; +}; + +export const syncEvents = async ( + backend: BackendAPISIX, + events: Array = [], +) => { + return runTask(await backend.sync(), { diff: events }); +}; + +export const dumpConfiguration = async (backend: BackendAPISIX) => { + const ctx = { remote: {} }; + await runTask(await backend.dump(), ctx); + return ctx.remote; +}; + +export const getDefaultValue = async (backend: BackendAPISIX) => { + const ctx = { defaultValue: {} }; + await runTask(new Listr(backend.getResourceDefaultValueTask()), ctx); + return ctx.defaultValue; +}; + +export const createEvent = ( + resourceType: ADCSDK.ResourceType, + resourceName: string, + resource: object, + parentName?: string, +): ADCSDK.Event => ({ + type: ADCSDK.EventType.CREATE, + resourceType, + resourceName, + resourceId: + resourceType === ADCSDK.ResourceType.CONSUMER || + resourceType === ADCSDK.ResourceType.GLOBAL_RULE || + resourceType === ADCSDK.ResourceType.PLUGIN_METADATA + ? resourceName + : resourceType === ADCSDK.ResourceType.SSL + ? ADCSDK.utils.generateId((resource as ADCSDK.SSL).snis.join(',')) + : ADCSDK.utils.generateId( + parentName ? `${parentName}.${resourceName}` : resourceName, + ), + newValue: resource, + parentId: parentName ? ADCSDK.utils.generateId(parentName) : undefined, +}); + +export const updateEvent = ( + resourceType: ADCSDK.ResourceType, + resourceName: string, + resource: object, + parentName?: string, +): ADCSDK.Event => { + const event = createEvent(resourceType, resourceName, resource, parentName); + event.type = ADCSDK.EventType.UPDATE; + return event; +}; + +export const deleteEvent = ( + resourceType: ADCSDK.ResourceType, + resourceName: string, + parentName?: string, +): ADCSDK.Event => ({ + type: ADCSDK.EventType.DELETE, + resourceType, + resourceName, + resourceId: + resourceType === ADCSDK.ResourceType.CONSUMER || + resourceType === ADCSDK.ResourceType.GLOBAL_RULE || + resourceType === ADCSDK.ResourceType.PLUGIN_METADATA + ? resourceName + : ADCSDK.utils.generateId( + parentName ? `${parentName}.${resourceName}` : resourceName, + ), + parentId: parentName ? ADCSDK.utils.generateId(parentName) : undefined, +}); diff --git a/libs/backend-apisix/e2e/sync-and-dump-1.e2e-spec.ts b/libs/backend-apisix/e2e/sync-and-dump-1.e2e-spec.ts new file mode 100644 index 00000000..2e729d5b --- /dev/null +++ b/libs/backend-apisix/e2e/sync-and-dump-1.e2e-spec.ts @@ -0,0 +1,424 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { unset } from 'lodash'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { BackendAPISIX } from '../src'; +import { server, token } from './support/constants'; +import { + createEvent, + deleteEvent, + dumpConfiguration, + syncEvents, + updateEvent, +} from './support/utils'; + +describe('Sync and Dump - 1', () => { + let backend: BackendAPISIX; + + beforeAll(() => { + backend = new BackendAPISIX({ + server, + token, + tlsSkipVerify: true, + gatewayGroup: 'default', + }); + }); + + describe('Sync and dump empty service', () => { + const upstream = { + scheme: 'https', + nodes: [ + { + host: 'httpbin.org', + port: 443, + weight: 100, + }, + ], + }; + const service1Name = 'service1'; + const service1 = { + name: service1Name, + upstream: structuredClone(upstream), + } as ADCSDK.Service; + const service2Name = 'service2'; + const service2 = { + name: service2Name, + upstream: structuredClone(upstream), + } as ADCSDK.Service; + + it('Create services', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SERVICE, service1Name, service1), + createEvent(ADCSDK.ResourceType.SERVICE, service2Name, service2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(2); + expect(result.services[0]).toMatchObject(service2); + expect(result.services[1]).toMatchObject(service1); + }); + + it('Update service1', async () => { + service1.description = 'desc'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.SERVICE, service1Name, service1), + ]); + }); + + it('Dump again (service1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services[1]).toMatchObject(service1); + }); + + it('Delete service1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SERVICE, service1Name), + ])); + + it('Dump again (service1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service2); + }); + + it('Delete service2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SERVICE, service2Name), + ])); + + it('Dump again (service2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(0); + }); + }); + + describe('Sync and dump service with routes', () => { + const serviceName = 'test'; + const service = { + name: serviceName, + upstream: { + scheme: 'https', + nodes: [ + { + host: 'httpbin.org', + port: 443, + weight: 100, + }, + ], + }, + } as ADCSDK.Service; + const route1Name = 'route1'; + const route1 = { + name: route1Name, + uris: ['/route1'], + } as ADCSDK.Route; + const route2Name = 'route2'; + const route2 = { + name: route2Name, + uris: ['/route2'], + plugins: { + 'key-auth': {}, + }, + } as ADCSDK.Route; + + it('Create resources', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SERVICE, serviceName, service), + createEvent(ADCSDK.ResourceType.ROUTE, route1Name, route1, serviceName), + createEvent(ADCSDK.ResourceType.ROUTE, route2Name, route2, serviceName), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service); + expect(result.services[0].routes).toHaveLength(2); + expect(result.services[0].routes[0]).toMatchObject(route1); + expect(result.services[0].routes[1]).toMatchObject(route2); + }); + + it('Delete route1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.ROUTE, route1Name, serviceName), + ])); + + it('Dump again (check remain route2)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(1); + expect(result.services[0]).toMatchObject(service); + expect(result.services[0].routes).toHaveLength(1); + expect(result.services[0].routes[0]).toMatchObject(route2); + }); + + it('Delete service', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.ROUTE, route2Name, serviceName), + deleteEvent(ADCSDK.ResourceType.SERVICE, serviceName), + ])); + + it('Dump again (service should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.services).toHaveLength(0); + }); + }); + + describe('Sync and dump consumers', () => { + const consumer1Name = 'consumer1'; + const consumer1 = { + username: consumer1Name, + plugins: { + 'key-auth': { + key: consumer1Name, + }, + }, + } as ADCSDK.Consumer; + const consumer2Name = 'consumer2'; + const consumer2 = { + username: consumer2Name, + plugins: { + 'key-auth': { + key: consumer2Name, + }, + }, + } as ADCSDK.Consumer; + + it('Create consumers', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.CONSUMER, consumer1Name, consumer1), + createEvent(ADCSDK.ResourceType.CONSUMER, consumer2Name, consumer2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers).toHaveLength(2); + expect(result.consumers[0]).toMatchObject(consumer1); + expect(result.consumers[1]).toMatchObject(consumer2); + }); + + it('Update consumer1', async () => { + consumer1.description = 'desc'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.CONSUMER, consumer1Name, consumer1), + ]); + }); + + it('Dump again (consumer1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers[0]).toMatchObject(consumer1); + }); + + it('Delete consumer1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.CONSUMER, consumer1Name), + ])); + + it('Dump again (consumer1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers).toHaveLength(1); + expect(result.consumers[0]).toMatchObject(consumer2); + }); + + it('Delete consumer2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.CONSUMER, consumer2Name), + ])); + + it('Dump again (consumer2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.consumers).toHaveLength(0); + }); + }); + + describe('Sync and dump ssls', () => { + const certificate = { + certificate: readFileSync( + join(__dirname, 'assets/test-ssl.cer'), + ).toString('utf-8'), + key: readFileSync(join(__dirname, 'assets/test-ssl.key')).toString( + 'utf-8', + ), + }; + const ssl1SNIs = ['ssl1-1.com', 'ssl1-2.com']; + const ssl1 = { + snis: ssl1SNIs, + certificates: [certificate], + } as ADCSDK.SSL; + const ssl2SNIs = ['ssl2-1.com', 'ssl2-2.com']; + const ssl2 = { + snis: ssl2SNIs, + certificates: [certificate], + } as ADCSDK.SSL; + const sslName = (snis: Array) => snis.join(','); + + const ssl1test = structuredClone(ssl1); + const ssl2test = structuredClone(ssl2); + unset(ssl1test, 'certificates.0.key'); + unset(ssl2test, 'certificates.0.key'); + + it('Create ssls', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.SSL, sslName(ssl1SNIs), ssl1), + createEvent(ADCSDK.ResourceType.SSL, sslName(ssl2SNIs), ssl2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.ssls).toHaveLength(2); + expect(result.ssls[0]).toMatchObject(ssl2test); + expect(result.ssls[1]).toMatchObject(ssl1test); + }); + + it('Update ssl1', async () => { + ssl1.labels = { test: 'test' }; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.SSL, sslName(ssl1SNIs), ssl1), + ]); + }); + + it('Dump again (ssl1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + const testCase = structuredClone(ssl1); + unset(testCase, 'certificates.0.key'); + expect(result.ssls[1]).toMatchObject(testCase); + }); + + it('Delete ssl1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SSL, sslName(ssl1SNIs)), + ])); + + it('Dump again (ssl1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.ssls).toHaveLength(1); + expect(result.ssls[0]).toMatchObject(ssl2test); + }); + + it('Delete ssl2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.SSL, sslName(ssl2SNIs)), + ])); + + it('Dump again (ssl2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.ssls).toHaveLength(0); + }); + }); + + describe('Sync and dump global rules', () => { + const plugin1Name = 'prometheus'; + const plugin1 = { + prefer_name: true, + } as ADCSDK.GlobalRule; + const plugin2Name = 'file-logger'; + const plugin2 = { + path: 'logs/file.log', + } as ADCSDK.GlobalRule; + + it('Create global rules', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin1Name, plugin1), + createEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin2Name, plugin2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.global_rules)).toHaveLength(2); + expect(result.global_rules[plugin1Name]).toMatchObject(plugin1); + expect(result.global_rules[plugin2Name]).toMatchObject(plugin2); + }); + + it('Update plugin1', async () => { + plugin1.test = 'test'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin1Name, plugin1), + ]); + }); + + it('Dump again (plugin1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.global_rules[plugin1Name]).toMatchObject(plugin1); + }); + + it('Delete plugin1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin1Name), + ])); + + it('Dump again (plugin1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.global_rules)).toHaveLength(1); + expect(result.global_rules[plugin1Name]).toBeUndefined(); + expect(result.global_rules[plugin2Name]).toMatchObject(plugin2); + }); + + it('Delete plugin2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.GLOBAL_RULE, plugin2Name), + ])); + + it('Dump again (plugin2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.global_rules)).toHaveLength(0); + }); + }); + + describe('Sync and dump plugin metadata', () => { + const plugin1Name = 'http-logger'; + const plugin1 = { + log_format: { test: 'test', test1: 'test1' }, + } as ADCSDK.PluginMetadata; + const plugin2Name = 'tcp-logger'; + const plugin2 = { + log_format: { test: 'test', test1: 'test1' }, + } as ADCSDK.PluginMetadata; + + it('Create plugin metadata', async () => + syncEvents(backend, [ + createEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin1Name, plugin1), + createEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin2Name, plugin2), + ])); + + it('Dump', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.plugin_metadata)).toHaveLength(2); + expect(result.plugin_metadata[plugin1Name]).toMatchObject(plugin1); + expect(result.plugin_metadata[plugin2Name]).toMatchObject(plugin2); + }); + + it('Update plugin1', async () => { + plugin1.test = 'test'; + await syncEvents(backend, [ + updateEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin1Name, plugin1), + ]); + }); + + it('Dump again (plugin1 updated)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(result.plugin_metadata[plugin1Name]).toMatchObject(plugin1); + }); + + it('Delete plugin1', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin1Name), + ])); + + it('Dump again (plugin1 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.plugin_metadata)).toHaveLength(1); + expect(result.plugin_metadata[plugin1Name]).toBeUndefined(); + expect(result.plugin_metadata[plugin2Name]).toMatchObject(plugin2); + }); + + it('Delete plugin2', async () => + syncEvents(backend, [ + deleteEvent(ADCSDK.ResourceType.PLUGIN_METADATA, plugin2Name), + ])); + + it('Dump again (plugin2 should not exist)', async () => { + const result = (await dumpConfiguration(backend)) as ADCSDK.Configuration; + expect(Object.keys(result.plugin_metadata)).toHaveLength(0); + }); + }); +}); diff --git a/libs/backend-apisix/jest.config.e2e.ts b/libs/backend-apisix/jest.config.e2e.ts new file mode 100644 index 00000000..b2ab4756 --- /dev/null +++ b/libs/backend-apisix/jest.config.e2e.ts @@ -0,0 +1,19 @@ +/* eslint-disable */ +export default { + displayName: 'backend-apisix-e2e', + preset: '../../jest.preset.js', + globalSetup: '/e2e/support/global-setup.ts', + globalTeardown: '/e2e/support/global-teardown.ts', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/backend-apisix/e2e', + testMatch: ['**/?(*.)+(e2e-spec).[jt]s?(x)'], +}; diff --git a/libs/backend-apisix/jest.config.ts b/libs/backend-apisix/jest.config.ts new file mode 100644 index 00000000..d3a9c791 --- /dev/null +++ b/libs/backend-apisix/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'backend-apisix', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/backend-apisix', +}; diff --git a/libs/backend-apisix/project.json b/libs/backend-apisix/project.json new file mode 100644 index 00000000..a57b20f8 --- /dev/null +++ b/libs/backend-apisix/project.json @@ -0,0 +1,34 @@ +{ + "name": "backend-apisix", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/backend-apisix/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + "outputs": [ + "{options.outputFile}" + ] + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}" + ], + "options": { + "jestConfig": "libs/backend-apisix/jest.config.ts" + } + }, + "e2e": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{e2eProjectRoot}" + ], + "options": { + "jestConfig": "libs/backend-apisix/jest.config.e2e.ts", + "passWithNoTests": true + } + } + }, + "tags": [] +} diff --git a/libs/backend-apisix/src/fetcher.ts b/libs/backend-apisix/src/fetcher.ts new file mode 100644 index 00000000..3edadc93 --- /dev/null +++ b/libs/backend-apisix/src/fetcher.ts @@ -0,0 +1,188 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Axios } from 'axios'; +import { ListrTask } from 'listr2'; + +import { ToADC } from './transformer'; +import * as typing from './typing'; +import { buildReqAndRespDebugOutput, resourceTypeToAPIName } from './utils'; + +type FetchTask = ListrTask<{ + remote: ADCSDK.Configuration; + apisixResources?: typing.Resources; +}>; + +export class Fetcher { + constructor(private readonly client: Axios) {} + + public fetch(): Array { + return [ + // Initialize context + { + task: (ctx) => { + ctx.apisixResources = {}; + }, + }, + ...this.allTask(), + { + title: 'Reorganize resources', + task: (_, task) => task.newListr(this.toADC()), + }, + ]; + } + + private allTask(): Array { + return Object.values(ADCSDK.ResourceType) + .map((resourceType): [ADCSDK.ResourceType, string] => [ + resourceType, + resourceTypeToAPIName(resourceType), + ]) + .map( + ([resourceType, apiName]): FetchTask => ({ + title: `Fetch ${apiName}`, + task: async (ctx, task) => { + const resp = await this.client.get<{ + list: Array<{ + key: string; + value: any; + createdIndex: number; + modifiedIndex: number; + }>; + total: number; + }>(`/apisix/admin/${apiName}`, { + validateStatus: () => true, + }); + task.output = buildReqAndRespDebugOutput(resp, `Get ${apiName}`); + if ( + resourceType === ADCSDK.ResourceType.STREAM_ROUTE && + resp.status === 400 + ) + return; + + // resourceType === ADCSDK.ResourceType.GLOBAL_RULE || + if (resourceType === ADCSDK.ResourceType.PLUGIN_METADATA) { + ctx.apisixResources[ADCSDK.ResourceType.PLUGIN_METADATA] = + Object.fromEntries( + resp.data?.list.map((item) => [ + item.key.split('/').pop(), + ADCSDK.utils.recursiveOmitUndefined(item.value), + ]), + ); + } else if (resourceType === ADCSDK.ResourceType.GLOBAL_RULE) { + ctx.apisixResources[ADCSDK.ResourceType.GLOBAL_RULE] = + Object.fromEntries( + resp.data?.list.map((item) => { + const pluginName = item.key.split('/').pop(); + return [ + pluginName, + ADCSDK.utils.recursiveOmitUndefined( + item.value?.plugins?.[pluginName] ?? {}, + ), + ]; + }), + ); + } else { + ctx.apisixResources[resourceType] = resp.data?.list.map( + (item) => item.value, + ); + } + }, + }), + ); + } + + private toADC(): Array { + const toADC = new ToADC(); + return [ + { + title: 'Move plugin templates to route', + task: (ctx) => { + const resources = ctx.apisixResources; + const pluginConfigIdMap = Object.fromEntries( + resources?.plugin_config?.map((item) => [item.id, item]), + ); + resources.route = resources?.route?.map((item) => { + if (item.plugin_config_id) + item.plugins = pluginConfigIdMap[item.plugin_config_id].plugins; + return item; + }); + }, + }, + { + title: 'Move upstreams to service or route', + task: (ctx) => { + const resources = ctx.apisixResources; + const upstreamIdMap = Object.fromEntries( + resources?.upstream?.map((item) => [ + item.id, + toADC.transformUpstream(item), + ]), + ); + resources.route = resources?.route?.map((item) => { + if (item.upstream_id) + item.upstream = upstreamIdMap[item.upstream_id]; + return item; + }); + resources.service = resources?.service?.map((item) => { + if (item.upstream_id) + item.upstream = upstreamIdMap[item.upstream_id]; + return item; + }); + }, + }, + { + title: 'Move routes and stream_routes to service', + task: (ctx) => { + const resources = ctx.apisixResources; + const serviceIdMap = Object.fromEntries( + resources?.service?.map((item) => [ + item.id, + toADC.transformService(item), + ]), + ); + resources?.route?.forEach((item) => { + const route = toADC.transformRoute(item); + if (item.service_id) { + if (!serviceIdMap[item.service_id]) return; //TODO error report + if (!serviceIdMap[item.service_id].routes) + serviceIdMap[item.service_id].routes = []; + serviceIdMap[item.service_id].routes.push(route); + } + }); + resources?.stream_route?.forEach((item) => { + const route = toADC.transformStreamRoute(item); + if (item.service_id) { + if (!serviceIdMap[item.service_id]) return; //TODO error report + if (!serviceIdMap[item.service_id].stream_routes) + serviceIdMap[item.service_id].stream_routes = []; + serviceIdMap[item.service_id].stream_routes.push(route); + } + }); + ctx.remote.services = Object.values(serviceIdMap).map((item) => + ADCSDK.utils.recursiveOmitUndefined({ + ...item, + routes: item?.routes?.length > 0 ? item.routes : undefined, + stream_routes: + item?.stream_routes?.length > 0 + ? item.stream_routes + : undefined, + }), + ); + }, + }, + { + task: (ctx) => { + const resources = ctx.apisixResources; + ctx.remote = { + ...ctx.remote, + ssls: resources?.ssl?.map(toADC.transformSSL), + consumers: resources?.consumer?.map((item) => + toADC.transformConsumer(item, true), + ), + global_rules: resources[ADCSDK.ResourceType.GLOBAL_RULE], + plugin_metadata: resources[ADCSDK.ResourceType.PLUGIN_METADATA], + }; + }, + }, + ]; + } +} diff --git a/libs/backend-apisix/src/index.ts b/libs/backend-apisix/src/index.ts new file mode 100644 index 00000000..ba8f124a --- /dev/null +++ b/libs/backend-apisix/src/index.ts @@ -0,0 +1,86 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import axios, { Axios, CreateAxiosDefaults } from 'axios'; +import { Listr, ListrTask } from 'listr2'; +import { readFileSync } from 'node:fs'; +import { AgentOptions, Agent as httpsAgent } from 'node:https'; + +import { Fetcher } from './fetcher'; +import { Operator } from './operator'; + +export class BackendAPISIX implements ADCSDK.Backend { + private readonly client: Axios; + private static logScope = ['APISIX']; + + constructor(private readonly opts: ADCSDK.BackendOptions) { + const config: CreateAxiosDefaults = { + baseURL: `${opts.server}`, + headers: { + 'Content-Type': 'application/json', + 'X-API-KEY': opts.token, + }, + }; + + if (opts.server.startsWith('https')) { + const agentConfig: AgentOptions = { + rejectUnauthorized: !opts?.tlsSkipVerify, + }; + + if (opts?.caCertFile) { + agentConfig.ca = readFileSync(opts.caCertFile); + } + if (opts?.tlsClientCertFile) { + agentConfig.cert = readFileSync(opts.tlsClientCertFile); + agentConfig.key = readFileSync(opts.tlsClientKeyFile); + } + + config.httpsAgent = new httpsAgent(agentConfig); + } + + if (opts.timeout) config.timeout = opts.timeout; + + this.client = axios.create(config); + } + + public async ping(): Promise { + await this.client.get(`/apisix/admin/routes`); + } + + public getResourceDefaultValueTask(): Array { + return []; + } + + public async dump(): Promise> { + const fetcher = new Fetcher(this.client); + return new Listr( + [...this.getResourceDefaultValueTask(), ...fetcher.fetch()], + { + rendererOptions: { scope: BackendAPISIX.logScope }, + }, + ); + } + + public async sync(): Promise { + const operator = new Operator(this.client); + return new Listr( + [ + ...this.getResourceDefaultValueTask(), + { + task: (ctx, task) => + task.newListr( + ctx.diff.map((event: ADCSDK.Event) => + event.type === ADCSDK.EventType.DELETE + ? operator.deleteResource(event) + : operator.updateResource(event), + ), + ), + }, + ], + { + rendererOptions: { scope: BackendAPISIX.logScope }, + }, + ); + } + + supportValidate?: () => Promise; + supportStreamRoute?: () => Promise; +} diff --git a/libs/backend-apisix/src/operator.ts b/libs/backend-apisix/src/operator.ts new file mode 100644 index 00000000..6025f480 --- /dev/null +++ b/libs/backend-apisix/src/operator.ts @@ -0,0 +1,107 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Axios } from 'axios'; +import { ListrTask } from 'listr2'; + +import { FromADC } from './transformer'; +import * as typing from './typing'; +import { + buildReqAndRespDebugOutput, + capitalizeFirstLetter, + resourceTypeToAPIName, +} from './utils'; + +export interface OperateContext { + diff: Array; + gatewayGroupId: string; + needPublishServices: Record; +} +type OperateTask = ListrTask; + +export class Operator { + constructor(private readonly client: Axios) {} + + public updateResource(event: ADCSDK.Event): OperateTask { + return { + title: this.generateTaskName(event), + task: async (ctx, task) => { + const resp = await this.client.put( + `/apisix/admin/${resourceTypeToAPIName(event.resourceType)}/${event.resourceId}`, + this.fromADC(event), + { + validateStatus: () => true, + }, + ); + task.output = buildReqAndRespDebugOutput(resp); + + if (resp.data?.error_msg) throw new Error(resp.data.error_msg); + // [200, 201].includes(resp.status); + }, + }; + } + + public deleteResource(event: ADCSDK.Event): OperateTask { + return { + title: this.generateTaskName(event), + task: async (ctx, task) => { + const resp = await this.client.delete( + `/apisix/admin/${resourceTypeToAPIName(event.resourceType)}/${event.resourceId}`, + { + validateStatus: () => true, + }, + ); + task.output = buildReqAndRespDebugOutput(resp); + + // If the resource does not exist, it is not an error for the delete operation + if (resp.status === 404) return; + if (resp.data?.error_msg) throw new Error(resp.data.error_msg); + if (resp.data?.deleted <= 0) + throw new Error( + `Unexpected number of deletions of resources: ${resp.data?.deleted}`, + ); + // [200, 404].includes(resp.status); + }, + }; + } + + private generateTaskName(event: ADCSDK.Event) { + return `${capitalizeFirstLetter( + event.type, + )} ${event.resourceType}: "${event.resourceName}"`; + } + + private fromADC(event: ADCSDK.Event) { + const fromADC = new FromADC(); + switch (event.resourceType) { + case ADCSDK.ResourceType.CONSUMER: + return fromADC.transformConsumer(event.newValue as ADCSDK.Consumer); + case ADCSDK.ResourceType.CONSUMER_GROUP: + return fromADC.transformConsumerGroup( + event.newValue as ADCSDK.ConsumerGroup, + )[0]; + case ADCSDK.ResourceType.GLOBAL_RULE: + return { + plugins: { + [event.resourceId]: event.newValue, + }, + }; + case ADCSDK.ResourceType.PLUGIN_METADATA: + return event.newValue; + case ADCSDK.ResourceType.ROUTE: { + const route = fromADC.transformRoute(event.newValue as ADCSDK.Route); + if (event.parentId) route.service_id = event.parentId; + return route; + } + case ADCSDK.ResourceType.SERVICE: + return fromADC.transformService(event.newValue as ADCSDK.Service)[0]; + case ADCSDK.ResourceType.SSL: + return fromADC.transformSSL(event.newValue as ADCSDK.SSL); + case ADCSDK.ResourceType.STREAM_ROUTE: { + const route = fromADC.transformStreamRoute( + event.newValue as ADCSDK.StreamRoute, + ); + if (event.parentId) route.service_id = event.parentId; + return route; + } + } + } +} diff --git a/libs/backend-apisix/src/transformer.ts b/libs/backend-apisix/src/transformer.ts new file mode 100644 index 00000000..6b2faa3a --- /dev/null +++ b/libs/backend-apisix/src/transformer.ts @@ -0,0 +1,359 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { isEmpty, unset } from 'lodash'; + +import * as typing from './typing'; + +export class ToADC { + public transformRoute(route: typing.Route): ADCSDK.Route { + return ADCSDK.utils.recursiveOmitUndefined({ + name: route.name ?? route.id, + description: route.desc, + labels: route.labels, + + uris: route.uri ? [route.uri] : route.uris, + hosts: route.host ? [route.host] : route.hosts, + priority: route.priority, + timeout: route.timeout, + vars: route.vars, + methods: route.methods, + enable_websocket: route.enable_websocket, + remote_addrs: route.remote_addr + ? [route.remote_addr] + : route.remote_addrs, + plugins: route.plugins, + plugin_config_id: route.plugin_config_id, + filter_func: route.filter_func, + + metadata: { id: route.id }, + } as ADCSDK.Route); + } + + public transformService(service: typing.Service): ADCSDK.Service { + return ADCSDK.utils.recursiveOmitUndefined({ + name: service.name ?? service.id, + description: service.desc, + labels: service.labels, + + upstream: service.upstream, + plugins: service.plugins, + + metadata: { id: service.id }, + } as ADCSDK.Service); + } + + public transformConsumer( + consumer: typing.Consumer, + removeGroupId = false, + ): ADCSDK.Consumer { + return ADCSDK.utils.recursiveOmitUndefined({ + username: consumer.username, + description: consumer.desc, + labels: consumer.labels, + + plugins: consumer.plugins, + group_id: !removeGroupId ? consumer.group_id : undefined, + }); + } + + public transformSSL(ssl: typing.SSL): ADCSDK.SSL { + const certificates: Array = [ + { + certificate: ssl.cert, + key: ssl.key, + }, + ...(ssl.certs?.map((cert, idx) => ({ + certificate: cert, + key: ssl.keys[idx], + })) ?? []), + ]; + + return ADCSDK.utils.recursiveOmitUndefined({ + labels: ssl.labels, + + type: ssl.type, + snis: ssl.sni ? [ssl.sni] : ssl.snis, + certificates: certificates, + client: ssl.client + ? { + ca: ssl.client.ca, + depth: ssl.client.depth, + skip_mtls_uri_regex: ssl.client.skip_mtls_uri_regex, + } + : undefined, + ssl_protocols: ssl.ssl_protocols, + }); + } + + public transformConsumerGroup( + consumerGroup: typing.ConsumerGroup, + consumers?: Array, + ): ADCSDK.ConsumerGroup { + const adcConsumerGroup: ADCSDK.ConsumerGroup = + ADCSDK.utils.recursiveOmitUndefined({ + name: (consumerGroup.labels?.ADC_NAME as string) ?? consumerGroup.id, + description: consumerGroup.desc, + labels: consumerGroup.labels, + plugins: consumerGroup.plugins, + + consumers: consumers + .filter( + (consumer) => + consumer.group_id && consumer.group_id === consumerGroup.id, + ) + .map((consumer) => this.transformConsumer(consumer, true)), + }); + + unset(adcConsumerGroup, 'labels.ADC_NAME'); + if (isEmpty(adcConsumerGroup.labels)) unset(adcConsumerGroup, 'labels'); + + return adcConsumerGroup; + } + + public transformGlobalRule(globalRule: typing.GlobalRule): ADCSDK.GlobalRule { + return ADCSDK.utils.recursiveOmitUndefined({ + name: globalRule.id, + plugins: globalRule.plugins, + }); + } + + public transformStreamRoute( + streamRoute: typing.StreamRoute, + ): ADCSDK.StreamRoute { + return ADCSDK.utils.recursiveOmitUndefined({ + name: streamRoute.name ?? streamRoute.id, + description: streamRoute.desc, + labels: streamRoute.labels, + + remote_addr: streamRoute.remote_addr, + server_addr: streamRoute.server_addr, + server_port: streamRoute.server_port, + sni: streamRoute.sni, + + metadata: { id: streamRoute.id }, + } as ADCSDK.StreamRoute); + } + + public transformUpstream(upstream: typing.Upstream): ADCSDK.Upstream { + const defaultPortMap: Record = { + http: 80, + https: 443, + grpc: 80, + grpcs: 443, + }; + const nodes = Array.isArray(upstream.nodes) + ? upstream.nodes + : Object.keys(upstream.nodes).map((node) => { + const hostport = node.split(':'); + return { + host: hostport[0], + port: + hostport.length === 2 + ? parseInt(hostport[1]) + : defaultPortMap[upstream.scheme] + ? defaultPortMap[upstream.scheme] + : 80, + weight: upstream.nodes[node], + }; + }); + return ADCSDK.utils.recursiveOmitUndefined({ + name: upstream.name ?? upstream.id, + description: upstream.desc, + labels: upstream.labels, + + type: upstream.type, + hash_on: upstream.hash_on, + key: upstream.key, + checks: upstream.checks, + nodes, + scheme: upstream.scheme, + retries: upstream.retries, + retry_timeout: upstream.retry_timeout, + timeout: upstream.timeout, + tls: upstream.tls + ? { + client_cert_id: upstream.tls.client_cert_id, + cert: upstream.tls.client_cert, + key: upstream.tls.client_key, + verify: upstream.tls.verify, + } + : undefined, + keepalive_pool: upstream.keepalive_pool + ? { + size: upstream.keepalive_pool.size, + idle_timeout: upstream.keepalive_pool.idle_timeout, + requests: upstream.keepalive_pool.requests, + } + : undefined, + pass_host: upstream.pass_host, + upstream_host: upstream.upstream_host, + + service_name: upstream.service_name, + discovery_type: upstream.discovery_type, + discovery_args: upstream.discovery_args, + }); + } +} + +export class FromADC { + private static transformLabels( + labels?: ADCSDK.Labels, + ): Record { + if (!labels) return undefined; + return Object.entries(labels).reduce((pv, [key, value]) => { + pv[key] = typeof value === 'string' ? value : JSON.stringify(value); + return pv; + }, {}); + } + + public transformRoute(route: ADCSDK.Route): typing.Route { + return ADCSDK.utils.recursiveOmitUndefined({ + ...route, + id: undefined, + labels: FromADC.transformLabels(route.labels), + status: 1, + + desc: route.description, + description: undefined, + }); + } + + public transformService( + service: ADCSDK.Service, + ): [typing.Service, Array, Array] { + const serviceId = ADCSDK.utils.generateId(service.name); + const routes: Array = + service.routes + ?.map(this.transformRoute) + .map((route) => ({ ...route, service_id: serviceId })) ?? []; + const streamRoutes: Array = + service.stream_routes + ?.map(this.transformStreamRoute) + .map((route) => ({ ...route, service_id: serviceId })) ?? []; + return [ + ADCSDK.utils.recursiveOmitUndefined({ + ...service, + id: undefined, + labels: FromADC.transformLabels(service.labels), + routes: undefined, + stream_routes: undefined, + + desc: service.description, + description: undefined, + }), + routes, + streamRoutes, + ]; + } + + public transformConsumer(consumer: ADCSDK.Consumer): typing.Consumer { + return ADCSDK.utils.recursiveOmitUndefined({ + ...consumer, + id: undefined, + labels: FromADC.transformLabels(consumer.labels), + + desc: consumer.description, + description: undefined, + }); + } + + public transformSSL(ssl: ADCSDK.SSL): typing.SSL { + return ADCSDK.utils.recursiveOmitUndefined({ + ...ssl, + id: undefined, + labels: FromADC.transformLabels(ssl.labels), + status: 1, + certificates: undefined, + + cert: ssl.certificates[0].certificate, + key: ssl.certificates[0].key, + ...(ssl.certificates.length > 1 + ? { + certs: ssl.certificates + .slice(1) + .map((certificate) => certificate.certificate), + keys: ssl.certificates + .slice(1) + .map((certificate) => certificate.key), + } + : {}), + }); + } + + public transformConsumerGroup( + consumerGroup: ADCSDK.ConsumerGroup, + ): [typing.ConsumerGroup, Array] { + const consumerGroupId = ADCSDK.utils.generateId(consumerGroup.name); + const consumers: Array = consumerGroup.consumers + ?.map(this.transformConsumer) + .map((consumer) => ({ ...consumer, group_id: consumerGroupId })); + + return [ + ADCSDK.utils.recursiveOmitUndefined({ + ...consumerGroup, + id: undefined, + labels: { + ...FromADC.transformLabels(consumerGroup.labels), + ADC_NAME: consumerGroup.name, + }, + name: undefined, + consumers: undefined, + }), + consumers, + ]; + } + + public transformGlobalRules( + globalRules: Record, + ): Array { + return Object.entries(globalRules).reduce>( + (pv, [key, value]) => { + pv.push( + ADCSDK.utils.recursiveOmitUndefined({ + id: undefined, + plugins: { + [key]: value as ADCSDK.Plugin, + }, + }), + ); + return pv; + }, + [], + ); + } + + public transformPluginMetadatas( + pluginMetadatas: Record, + ): Array { + return Object.entries(pluginMetadatas).reduce>( + (pv, [key, value]) => { + pv.push( + ADCSDK.utils.recursiveOmitUndefined({ + id: undefined, + ...value, + __plugin_name: key, + }), + ); + return pv; + }, + [], + ); + } + + public transformStreamRoute( + streamRoute: ADCSDK.StreamRoute, + ): typing.StreamRoute { + return ADCSDK.utils.recursiveOmitUndefined({ + ...streamRoute, + id: undefined, + labels: FromADC.transformLabels(streamRoute.labels), + }); + } + + public transformUpstream(upstream: ADCSDK.Upstream): typing.Upstream { + return ADCSDK.utils.recursiveOmitUndefined({ + ...upstream, + id: undefined, + labels: FromADC.transformLabels(upstream.labels), + }); + } +} diff --git a/libs/backend-apisix/src/typing.ts b/libs/backend-apisix/src/typing.ts new file mode 100644 index 00000000..b9927d75 --- /dev/null +++ b/libs/backend-apisix/src/typing.ts @@ -0,0 +1,178 @@ +import { + GlobalRule as ADCGlobalRule, + PluginMetadata as ADCPluginMetadata, + Upstream as ADCUpstream, + Expr, + Labels, + Plugins, + ResourceType, + UpstreamBalancer, + UpstreamHealthCheck, + UpstreamNode, + UpstreamPassHost, + UpstreamScheme, + UpstreamTimeout, +} from '@api7/adc-sdk'; + +export interface Route { + id: string; + name?: string; + desc?: string; + labels?: Record; + + // matcher + uri?: string; + uris?: Array; + host?: string; + hosts?: Array; + methods?: Array; + remote_addr?: string; + remote_addrs?: Array; + vars?: Expr; + filter_func?: string; + + // upstream and policies + script?: string; + script_id?: string; + plugins?: Plugins; + plugin_config_id?: string; + upstream?: ADCUpstream; + upstream_id?: string; + service_id?: string; + timeout?: UpstreamTimeout; + + // misc + enable_websocket?: boolean; + priority?: number; + status?: number; +} +export interface Service { + id: string; + name?: string; + desc?: string; + labels?: Labels; + + hosts?: Array; + upstream?: ADCUpstream; + upstream_id?: string; + plugins?: Plugins; + script?: string; + enable_websocket?: boolean; +} +export interface Consumer { + username: string; + desc?: string; + labels?: Labels; + + group_id?: string; + plugins?: Plugins; +} +export interface SSL { + id: string; + labels?: Labels; + + type?: 'server' | 'client'; + sni?: string; + snis?: Array; + cert?: string; + certs?: Array; + key?: string; + keys?: Array; + client?: { + ca: string; + depth: number; + skip_mtls_uri_regex?: Array; + }; + ssl_protocols?: Array; + + status: number; +} +export interface PluginConfig { + id: string; + name?: string; + desc?: string; + labels?: Labels; + + plugins: Plugins; +} +export interface ConsumerGroup { + id: string; + desc?: string; + labels?: Labels; + + plugins: Plugins; +} +export type GlobalRule = ADCGlobalRule; +export type PluginMetadata = ADCPluginMetadata; +export interface StreamRoute { + id: string; + desc?: string; + labels?: Labels; + name: string; + + remote_addr?: string; + server_addr?: string; + server_port?: number; + sni?: string; + upstream?: ADCUpstream; + upstream_id?: string; + service_id?: string; + + plugins?: Plugins; + protocol?: { + name: string; + superior_id?: string; + conf?: object; + logger?: Array<{ + name?: string; + filter?: Array; + conf: object; + }>; + }; +} +export interface Upstream { + id: string; + name?: string; + desc?: string; + labels?: Labels; + + nodes?: Array | Record; + scheme?: UpstreamScheme; + type?: UpstreamBalancer; + hash_on?: string; + key?: string; + checks?: UpstreamHealthCheck; + + discovery_type?: string; + service_name?: string; + discovery_args?: Record; + + pass_host?: UpstreamPassHost; + upstream_host?: string; + retries?: number; + retry_timeout?: number; + timeout?: UpstreamTimeout; + tls?: { + client_cert_id?: string; + client_cert?: string; + client_key?: string; + verify: boolean; + }; + keepalive_pool?: { + size: number; + idle_timeout: number; + requests: number; + }; +} +export interface Resources { + [ResourceType.ROUTE]?: Array; + [ResourceType.SERVICE]?: Array; + [ResourceType.CONSUMER]?: Array; + [ResourceType.SSL]?: Array; + [ResourceType.GLOBAL_RULE]?: Record; + [ResourceType.PLUGIN_CONFIG]?: Array; + [ResourceType.CONSUMER_GROUP]?: Array; + [ResourceType.PLUGIN_METADATA]?: Record; + [ResourceType.STREAM_ROUTE]?: Array; + [ResourceType.UPSTREAM]?: Array; +} diff --git a/libs/backend-apisix/src/utils.ts b/libs/backend-apisix/src/utils.ts new file mode 100644 index 00000000..f8ae229d --- /dev/null +++ b/libs/backend-apisix/src/utils.ts @@ -0,0 +1,43 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { AxiosResponse } from 'axios'; + +export const resourceTypeToAPIName = (resourceType: ADCSDK.ResourceType) => + resourceType !== ADCSDK.ResourceType.PLUGIN_METADATA + ? `${resourceType}s` + : resourceType; + +export const capitalizeFirstLetter = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1); + +export const buildReqAndRespDebugOutput = ( + resp: AxiosResponse, + desc?: string, +) => { + const config = resp.config; + + // NodeJS will not keep the response header in Xxx-Xxx format, correct it + const normalizeHeaderKey = (key: string) => + key.split('-').map(capitalizeFirstLetter).join('-'); + + // Transforms HTTP headers to a single line of text formatting + const transformHeaders = (headers: object, normalizeKey = false) => + Object.entries(headers).map( + ([key, value]) => + `${normalizeKey ? normalizeHeaderKey(key) : key}: ${key !== 'X-API-KEY' ? value : '*****'}\n`, + ); + return JSON.stringify({ + type: 'debug', + messages: [ + `${desc ?? ''}\n`, //TODO time consumption + // request + `${config.method.toUpperCase()} ${config.url}\n`, + ...transformHeaders(config.headers), + config?.data ? `\n${config.data}\n` : '', + '\n', + // response + `${resp.status} ${resp.statusText}\n`, + ...transformHeaders(resp.headers, true), + `${resp?.data ? `\n${JSON.stringify(resp.data)}` : ''}\n`, + ], + }); +}; diff --git a/libs/backend-apisix/tsconfig.json b/libs/backend-apisix/tsconfig.json new file mode 100644 index 00000000..ba4f9fec --- /dev/null +++ b/libs/backend-apisix/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/backend-apisix/tsconfig.lib.json b/libs/backend-apisix/tsconfig.lib.json new file mode 100644 index 00000000..3f06e802 --- /dev/null +++ b/libs/backend-apisix/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/libs/backend-apisix/tsconfig.spec.json b/libs/backend-apisix/tsconfig.spec.json new file mode 100644 index 00000000..e4523c2a --- /dev/null +++ b/libs/backend-apisix/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts", + "e2e/**/*.e2e-spec.ts" + ] +} diff --git a/libs/converter-openapi/.eslintrc.json b/libs/converter-openapi/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/libs/converter-openapi/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/converter-openapi/README.md b/libs/converter-openapi/README.md new file mode 100644 index 00000000..0dbfe74a --- /dev/null +++ b/libs/converter-openapi/README.md @@ -0,0 +1,306 @@ +# OpenAPI Converter + +A library for implementing the OpenAPI specification and ADC profile conversion. + +| Direction | Supported | +| -------------- | --------- | +| OpenAPI to ADC | ✅ | +| ADC to OpenAPI | ❎ | + +## Supported Extension Fields + +| Supported Levels | +| ----------------------------------------------------------------------------------------- | +| Root Level: on the root of the OAS document | +| Path Level: on each path object | +| Operation Level: on each HTTP method object for each path | +| Server Level: on each item in the servers field, supports Root, Path and Operation levels | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldLevelDescription
x-adc-nameRoot LevelOverride the name of the generated main service
Operation LevelOverride the name of the generated route
x-adc-labelsRoot LevelAdd labels field to the specified level. It supports string and string array formats.
Path Level
Operation Level
x-adc-pluginsRoot LevelAdd plugis field to the specified level. It is an object that contains one or more plugins.
Path LevelPlugin objects at the Path level and Operation level will cause the service to be split, i.e. the sub-level containing the plugin will be included in a new service.
Operation Level
x-adc-plugin-[plugin-name]Root LevelIt will be consistent with x-adc-plugins. However, those configured using this format will override plugins of the same name in x-adc-plugins.
Path Level
Operation Level
x-adc-service-defaultsRoot LevelIt supports setting/overriding parameters in the service at various levels. This field on sub-levels will cause the service to be split.
Path Level
Operation Level
x-adc-upstream-defaultsRoot LevelIt supports setting/overriding parameters in the upstream at various levels. This field on sub-levels will cause the service to be split.
Path Level
Operation Level
x-adc-upstream-node-defaultsRoot Level - Server LevelIt supports setting/overriding parameters in the upstream's node at various levels. The servers field on sub-levels will cause the service to be split.
+ +```yaml +servers: + - url: "https://httpbin.org" + x-adc-upstream-node-defaults: + priority: 100 + - url: "http://httpbin.org" + x-adc-upstream-node-defaults: + priority: 100 +``` + +
Path Level - Server Level
Operation Level - Server Level
x-adc-route-defaultsRoot LevelIt supports setting/overriding parameters in the route at various levels. This field on sub-levels will cause the service to be split.
Path Level
Operation Level
+ +## Extension Fields processing logic + +### `x-adc-plugins` and `x-adc-plugin-[plugin-name]` + +> They can be set at all three main levels, root, path, and operation. + +#### Set both `x-adc-plugins` and `x-adc-plugin-[plugin-name]` in same level + +1. For plugins with non-same names, their configurations will be merged. + + + + + + + + + + +
InputOutput
+ +```yaml +x-adc-plugins: + test-plugin1: + key: value +x-adc-plugin-test-plugin2: + key: value +``` + + + +```yaml +plugins: + test-plugin1: + key: value + test-plugin2: + key: value +``` + +
+ +1. For plugins with same names, the configurations in `x-adc-plugin-[plugin-name]` will override the one in `x-adc-plugins`. + + + + + + + + + + +
InputOutput
+ +```yaml +x-adc-plugins: + test-plugin1: + key: value +x-adc-plugin-test-plugin1: + key1: value1 +``` + + + +```yaml +plugins: + test-plugin1: + key1: value1 +``` + +
+ +#### Set both `x-adc-plugins` or `x-adc-plugin-[plugin-name]` in multiple levels + +- Plugin configurations at the root level will be mapped to the exported service. + +- Both the path level and the operation level will be mapped to the routes included in this service. + +The difference is: + +- The plugins on the path level will be included on all the routes corresponding to the method for that path. + + + + + + + + + + +
InputOutput
+ +```yaml +... +paths: + /anything: + x-adc-plugin-test-plugin: + key: value + get: ... + post: ... +``` + + + +```yaml +... +services: + - name: demo + routes: + - name: demo_anything_get + uris: [/anything] + methods: [GET] + plugins: + test-plugin: + key: value + - name: demo_anything_post + uris: [/anything] + methods: [POST] + plugins: + test-plugin: + key: value +``` + +
+ +- The plugin with the same name at the operation level as at the path level will override the one at the path. + + + + + + + + + + +
InputOutput
+ +```yaml +... +paths: + /anything: + x-adc-plugin-test-plugin: + key: value + get: ... + x-adc-plugin-test-plugin: + key1: value1 + x-adc-plugin-test-plugin2: + key: value + post: ... +``` + + + +```yaml +... +services: + - name: demo + routes: + - name: demo_anything_get + uris: [/anything] + methods: [GET] + plugins: + test-plugin: + key1: value1 + test-plugin2: + key: value + - name: demo_anything_post + uris: [/anything] + methods: [POST] + plugins: + test-plugin: + key: value +``` + +
diff --git a/libs/converter-openapi/jest.config.ts b/libs/converter-openapi/jest.config.ts new file mode 100644 index 00000000..b9e9d01c --- /dev/null +++ b/libs/converter-openapi/jest.config.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +export default { + displayName: 'converter-openapi', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/converter-openapi', + testMatch: ['**/test/?(*.)+(spec).ts?(x)'], +}; diff --git a/libs/converter-openapi/project.json b/libs/converter-openapi/project.json new file mode 100644 index 00000000..e1b1d0cc --- /dev/null +++ b/libs/converter-openapi/project.json @@ -0,0 +1,16 @@ +{ + "name": "converter-openapi", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/converter-openapi/src", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/converter-openapi/jest.config.ts" + } + } + } +} diff --git a/libs/converter-openapi/src/extension.ts b/libs/converter-openapi/src/extension.ts new file mode 100644 index 00000000..ceb11f0c --- /dev/null +++ b/libs/converter-openapi/src/extension.ts @@ -0,0 +1,24 @@ +import { isEmpty } from 'lodash'; + +export enum ExtKey { + NAME = 'x-adc-name', + LABELS = 'x-adc-labels', + PLUGINS = 'x-adc-plugins', + PLUGIN_PREFIX = 'x-adc-plugin-', + SERVICE_DEFAULTS = 'x-adc-service-defaults', + UPSTREAM_DEFAULTS = 'x-adc-upstream-defaults', + UPSTREAM_NODE_DEFAULTS = 'x-adc-upstream-node-defaults', + ROUTE_DEFAULTS = 'x-adc-route-defaults', +} + +export const parseExtPlugins = (context: object) => { + const separatePlugins = Object.fromEntries( + Object.entries(context) + .filter( + ([key]) => key.startsWith(ExtKey.PLUGIN_PREFIX), // x-adc-plugin-acl + ) + .map(([key, plugin]) => [key.replace(ExtKey.PLUGIN_PREFIX, ''), plugin]), + ); + const plugins = { ...context[ExtKey.PLUGINS], ...separatePlugins }; + return !isEmpty(plugins) ? plugins : undefined; +}; diff --git a/libs/converter-openapi/src/index.ts b/libs/converter-openapi/src/index.ts new file mode 100644 index 00000000..b10526ed --- /dev/null +++ b/libs/converter-openapi/src/index.ts @@ -0,0 +1,154 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { Listr } from 'listr2'; +import { isEmpty } from 'lodash'; +import { OpenAPIV3 } from 'openapi-types'; +import slugify from 'slugify'; + +import { ExtKey, parseExtPlugins } from './extension'; +import { parseSeprateService, parseUpstream } from './parser'; +import { schema } from './schema'; + +const pathVariableRegex = /{[^}]+}/g; + +export class OpenAPIConverter implements ADCSDK.Converter { + public toADC( + oas: OpenAPIV3.Document, + ): Listr<{ local: ADCSDK.Configuration }> { + return new Listr<{ local: ADCSDK.Configuration }>([ + { + title: 'Validate OpenAPI document', + task: () => { + const res = schema.safeParse(oas); + if (!res.success) throw new Error(res.error.toString()); //TODO optimize it + }, + }, + { + title: 'Generate main service', + task: (ctx) => { + ctx.local.services = [ + { + name: oas[ExtKey.NAME] + ? oas[ExtKey.NAME] + : oas.info.title + ? oas.info.title + : 'Untitled service', + description: oas.info.description, + labels: oas[ExtKey.LABELS], + plugins: parseExtPlugins(oas), + routes: [], + }, + ]; + + parseUpstream( + ctx.local.services[0], + oas.servers, + oas[ExtKey.UPSTREAM_DEFAULTS], + ); + + ctx.local.services[0] = { + ...ctx.local.services[0], + ...(oas[ExtKey.SERVICE_DEFAULTS] ?? {}), + }; + }, + }, + { + title: 'Generate routes', + task: (ctx, task) => { + const mainService = ctx.local.services[0]; + const services: Array = []; + Object.entries(oas.paths).forEach(([path, operations]) => { + const httpMethods = Object.values(OpenAPIV3.HttpMethods); + + const separateService = parseSeprateService( + mainService, + operations, + [mainService.name, path].map((item) => slugify(item)).join('_'), + ); + const pathPlugins = parseExtPlugins(operations); + const routes = Object.entries(operations) + .filter(([key]) => + httpMethods.includes(key as OpenAPIV3.HttpMethods), + ) + .map( + ([method, operation]: [ + OpenAPIV3.HttpMethods, + OpenAPIV3.OperationObject, + ]) => { + const separateService = parseSeprateService( + mainService, + operation, + [oas.info.title, path, method] + .map((item) => slugify(item)) + .join('_'), + operations[ExtKey.SERVICE_DEFAULTS], + { + ...oas[ExtKey.UPSTREAM_DEFAULTS], + ...operations[ExtKey.UPSTREAM_DEFAULTS], + }, + ); + + const plugins = { + ...pathPlugins, + ...parseExtPlugins(operation), + }; + const route = ADCSDK.utils.recursiveOmitUndefined({ + name: operation[ExtKey.NAME] + ? operation[ExtKey.NAME] + : operation.operationId + ? operation.operationId + : [oas.info.title, path, method] + .map((item) => slugify(item)) + .join('_'), + description: operation.summary ?? operation.description, + labels: operation[ExtKey.LABELS], + methods: [method.toUpperCase()], + uris: [ + path.replaceAll(pathVariableRegex, (match) => { + return `:${match.slice(1, match.length - 1)}`; + }), + ], + plugins: !isEmpty(plugins) ? plugins : undefined, + ...structuredClone(oas[ExtKey.ROUTE_DEFAULTS]), + ...structuredClone(operations[ExtKey.ROUTE_DEFAULTS]), + ...structuredClone(operation[ExtKey.ROUTE_DEFAULTS]), + } as ADCSDK.Route); + + if (separateService) { + separateService.routes = [route]; + services.push(separateService); + task.output = this.buildDebugOutput([ + `${method.toUpperCase()} "${path}" contains the service or upstream defaults, so it will be included to the separate service`, + ]); + } else { + return route; + } + }, + ) + .filter((item) => !!item); + if (separateService) { + if (routes?.length <= 0) return; + separateService.routes = routes; + services.push(separateService); + task.output = this.buildDebugOutput([ + `Path "${path}" contains the service or upstream defaults, so it will be included to the separate service`, + ]); + } else { + mainService.routes.push(...routes); + } + }); + ctx.local.services = ctx.local.services + .concat(...services) + .filter((service) => service?.routes?.length > 0) + .map((service) => ADCSDK.utils.recursiveOmitUndefined(service)); + }, + }, + ]); + } + + private buildDebugOutput(messages: Array) { + return JSON.stringify({ + type: 'debug', + messages, + }); + } +} diff --git a/libs/converter-openapi/src/parser.ts b/libs/converter-openapi/src/parser.ts new file mode 100644 index 00000000..11b02358 --- /dev/null +++ b/libs/converter-openapi/src/parser.ts @@ -0,0 +1,90 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { OpenAPIV3 } from 'openapi-types'; + +import { ExtKey, parseExtPlugins } from './extension'; + +export const parseUpstream = ( + service: ADCSDK.Service, + oasServers: Array, + upstreamDefaults?: object, +) => { + const getPort = (protocol: string): number => + protocol === 'http:' ? 80 : 443; + const defaultProtocol = oasServers[0] + ? new URL(oasServers[0].url).protocol + : 'https:'; + let defaultPathPrefix: string; + const nodes: Array = oasServers.map((server, idx) => { + if (server.variables) + Object.entries(server.variables).forEach(([name, svar]) => { + server.url = server.url.replaceAll(`{${name}}`, svar.default); + }); + + const url = new URL(server.url); + if (idx === 0) defaultPathPrefix = url.pathname; + + return { + host: url.hostname, + port: url.port ? parseInt(url.port) : getPort(url.protocol), + weight: 100, + ...server[ExtKey.UPSTREAM_NODE_DEFAULTS], + } as ADCSDK.UpstreamNode; + }); + + if (defaultPathPrefix) service.path_prefix = defaultPathPrefix; + service.upstream = { + scheme: defaultProtocol.slice( + 0, + defaultProtocol.length - 1, + ) as ADCSDK.UpstreamScheme, + nodes, + timeout: { + connect: 60, + send: 60, + read: 60, + }, + pass_host: 'pass', + ...upstreamDefaults, + }; +}; + +export const parseSeprateService = ( + mainService: ADCSDK.Service, + context: OpenAPIV3.PathItemObject | OpenAPIV3.OperationObject, + name: string, + pathServiceDefaults?: object, // Defaults used for operation level to merge path level + pathUpstreamDefaults?: object, // Defaults used for operation level to merge path level +): ADCSDK.Service | undefined => { + let separateService: ADCSDK.Service = undefined; + if ( + context[ExtKey.SERVICE_DEFAULTS] || + context[ExtKey.UPSTREAM_DEFAULTS] || + context.servers + ) { + separateService = { + ...structuredClone(mainService), // Inherit all attributes from the main service + name, + }; + + // Override upstream of path level + if (context.servers) + parseUpstream( + separateService, + context.servers, + context[ExtKey.UPSTREAM_DEFAULTS], + ); + + // Override service and upstream defaults of path level + separateService = { + ...separateService, + upstream: { + ...separateService.upstream, + ...(pathUpstreamDefaults ?? {}), + ...(context[ExtKey.UPSTREAM_DEFAULTS] ?? {}), + }, + ...(pathServiceDefaults ?? {}), + ...(context[ExtKey.SERVICE_DEFAULTS] ?? {}), + }; + } + return separateService; +}; diff --git a/libs/converter-openapi/src/schema.ts b/libs/converter-openapi/src/schema.ts new file mode 100644 index 00000000..25360740 --- /dev/null +++ b/libs/converter-openapi/src/schema.ts @@ -0,0 +1,71 @@ +import { z } from 'zod'; + +import { ExtKey } from './extension'; + +const xNameSchema = z.optional(z.string().min(1)); +const xLabelsSchmea = z.optional( + z.record(z.string(), z.union([z.string(), z.array(z.string())])), +); +const xPluginsSchema = z.optional(z.record(z.string(), z.any())); +const xDefaults = z.optional(z.record(z.string(), z.any())); +const pathOperationSchema = z.optional( + z.object({ + [ExtKey.NAME]: xNameSchema, + [ExtKey.LABELS]: xLabelsSchmea, + [ExtKey.PLUGINS]: xPluginsSchema, + // [ExtKey.PLUGIN_PREFIX]: ignore + [ExtKey.SERVICE_DEFAULTS]: xDefaults, + [ExtKey.UPSTREAM_DEFAULTS]: xDefaults, + [ExtKey.ROUTE_DEFAULTS]: xDefaults, + operationId: z.optional(z.string()), + summary: z.optional(z.string()), + description: z.optional(z.string()), + }), +); +export const schema = z.object({ + info: z.object({ + title: z.string(), + description: z.optional(z.string()), + }), + [ExtKey.NAME]: xNameSchema, + [ExtKey.LABELS]: xLabelsSchmea, + [ExtKey.PLUGINS]: xPluginsSchema, + // [ExtKey.PLUGIN_PREFIX]: ignore + [ExtKey.SERVICE_DEFAULTS]: xDefaults, + [ExtKey.UPSTREAM_DEFAULTS]: xDefaults, + [ExtKey.ROUTE_DEFAULTS]: xDefaults, + servers: z + .array( + z.object({ + url: z.string().regex(/https?:\/\//g), + variables: z.optional( + z.record( + z.string(), + z.object({ + default: z.string(), + }), + ), + ), + [ExtKey.UPSTREAM_NODE_DEFAULTS]: xDefaults, + }), + ) + .min(1), + paths: z.record( + z.string(), + z.object({ + [ExtKey.PLUGINS]: xPluginsSchema, + // [ExtKey.PLUGIN_PREFIX]: ignore + [ExtKey.SERVICE_DEFAULTS]: xDefaults, + [ExtKey.UPSTREAM_DEFAULTS]: xDefaults, + [ExtKey.ROUTE_DEFAULTS]: xDefaults, + get: pathOperationSchema, + put: pathOperationSchema, + post: pathOperationSchema, + delete: pathOperationSchema, + options: pathOperationSchema, + head: pathOperationSchema, + patch: pathOperationSchema, + trace: pathOperationSchema, + }), + ), +}); diff --git a/libs/converter-openapi/test/assets/basic-1.yaml b/libs/converter-openapi/test/assets/basic-1.yaml new file mode 100644 index 00000000..8d6e5ebd --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-1.yaml @@ -0,0 +1,47 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +paths: + /anything: + get: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/basic-2.yaml b/libs/converter-openapi/test/assets/basic-2.yaml new file mode 100644 index 00000000..fe5f8c4d --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-2.yaml @@ -0,0 +1,61 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +paths: + /absolute-redirect/{n}: + get: + tags: + - Redirects + summary: Absolutely 302 Redirects n times. + parameters: + - name: "n" + in: path + required: true + schema: {} + responses: + "302": + description: A redirection. + content: {} + /anything: + get: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/basic-3.yaml b/libs/converter-openapi/test/assets/basic-3.yaml new file mode 100644 index 00000000..0755b4b0 --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-3.yaml @@ -0,0 +1,54 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ + - url: https://httpbin.net/ + - url: http://httpbin.com/ + - url: http://httpbin.com:8080 + - url: http://httpbin.us/{test} + variables: + test: + default: "testValue" +paths: + /anything: + get: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/basic-4.yaml b/libs/converter-openapi/test/assets/basic-4.yaml new file mode 100644 index 00000000..3f68fd55 --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-4.yaml @@ -0,0 +1,53 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: http://httpbin.us/{test1}/{test2} + variables: + test1: + default: "test1Value" + test2: + default: "test2Value" + - url: https://httpbin.org/testPath +paths: + /anything: + get: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/basic-5.yaml b/libs/converter-openapi/test/assets/basic-5.yaml new file mode 100644 index 00000000..280e4924 --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-5.yaml @@ -0,0 +1,65 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org +paths: + /absolute-redirect/{n}: + get: + tags: + - Redirects + summary: Absolutely 302 Redirects n times. + parameters: + - name: "n" + in: path + required: true + schema: {} + responses: + "302": + description: A redirection. + content: {} + /anything: + servers: + - url: https://httpbin.net + get: + servers: + - url: https://httpbin.com + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/basic-6.yaml b/libs/converter-openapi/test/assets/basic-6.yaml new file mode 100644 index 00000000..08d1eb2e --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-6.yaml @@ -0,0 +1,51 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org +paths: + /anything: + servers: + - url: https://httpbin.net + get: + servers: + - url: https://httpbin.com + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/basic-7.yaml b/libs/converter-openapi/test/assets/basic-7.yaml new file mode 100644 index 00000000..fbbd4f55 --- /dev/null +++ b/libs/converter-openapi/test/assets/basic-7.yaml @@ -0,0 +1,24 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org +paths: + /anything: + get: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + operationId: "Anything_PUT" + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-1.yaml b/libs/converter-openapi/test/assets/extension-1.yaml new file mode 100644 index 00000000..5bb967db --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-1.yaml @@ -0,0 +1,17 @@ +info: + title: httpbin.org + description: httpbin.org description +x-adc-name: override service name +servers: + - url: https://httpbin.org/ +paths: + /anything: + get: + x-adc-name: override route name + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-10.yaml b/libs/converter-openapi/test/assets/extension-10.yaml new file mode 100644 index 00000000..d09b7827 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-10.yaml @@ -0,0 +1,41 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +x-adc-service-defaults: + test1: test1 + test2: test2 + test3: test3 +x-adc-upstream-defaults: + test1: test1 + test2: test2 + test3: test3 +paths: + /anything: + x-adc-service-defaults: + test2: test2-override + x-adc-upstream-defaults: + test2: test2-override + get: + x-adc-service-defaults: + test3: test3-override + x-adc-upstream-defaults: + test3: test3-override + servers: + - url: https://httpbin.org/ + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-11.yaml b/libs/converter-openapi/test/assets/extension-11.yaml new file mode 100644 index 00000000..e7775d39 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-11.yaml @@ -0,0 +1,91 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +x-adc-route-defaults: + test1: test1 + test2: test2 + test3: test3 +x-adc-service-defaults: + test1: test1 + test2: test2 + test3: test3 +x-adc-upstream-defaults: + test1: test1 + test2: test2 + test3: test3 +paths: + /anything: + x-adc-route-defaults: + test2: test2-override + x-adc-service-defaults: + test2: test2-override + x-adc-upstream-defaults: + test2: test2-override + get: + x-adc-route-defaults: + test3: test3-override + x-adc-service-defaults: + test3: test3-override + x-adc-upstream-defaults: + test3: test3-override + servers: + - url: https://httpbin.org/ + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + x-adc-route-defaults: + test3: test3-override + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + post: + x-adc-service-defaults: + test3: test3-override + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + delete: + x-adc-upstream-defaults: + test3: test3-override + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + patch: + x-adc-service-defaults: + test3: test3-override + x-adc-upstream-defaults: + test3: test3-override + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + options: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-12.yaml b/libs/converter-openapi/test/assets/extension-12.yaml new file mode 100644 index 00000000..01c61746 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-12.yaml @@ -0,0 +1,36 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ + x-adc-upstream-node-defaults: + test1: test1 + test2: test2 + test3: test3 + - url: https://httpbin.com/ + x-adc-upstream-node-defaults: + test4: test4 +paths: + /anything: + get: + servers: + - url: https://httpbin.org/ + x-adc-upstream-node-defaults: + test1: test1-override + test2: test2-override + test3: test3-override + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-2.yaml b/libs/converter-openapi/test/assets/extension-2.yaml new file mode 100644 index 00000000..3a4cf9af --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-2.yaml @@ -0,0 +1,17 @@ +info: + title: httpbin.org + description: httpbin.org description +x-adc-name: "" +servers: + - url: https://httpbin.org/ +paths: + /anything: + get: + x-adc-name: "" + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-3.yaml b/libs/converter-openapi/test/assets/extension-3.yaml new file mode 100644 index 00000000..33b20305 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-3.yaml @@ -0,0 +1,20 @@ +info: + title: httpbin.org + description: httpbin.org description +x-adc-labels: + test1: test1 +servers: + - url: https://httpbin.org/ +paths: + /anything: + get: + x-adc-labels: + test2: test2 + test3: [test3, test4] + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-4.yaml b/libs/converter-openapi/test/assets/extension-4.yaml new file mode 100644 index 00000000..b8c43b30 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-4.yaml @@ -0,0 +1,18 @@ +info: + title: httpbin.org + description: httpbin.org description +x-adc-labels: + test1: + test2: test +servers: + - url: https://httpbin.org/ +paths: + /anything: + get: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-5.yaml b/libs/converter-openapi/test/assets/extension-5.yaml new file mode 100644 index 00000000..9152ccc6 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-5.yaml @@ -0,0 +1,33 @@ +info: + title: httpbin.org + description: httpbin.org description +x-adc-plugins: + test1: + test1-key: test1-value + test2: + test2-key: test2-value +x-adc-plugin-test2: + test2-key: test3-value-override +x-adc-plugin-test3: + test3-key: test3-value +servers: + - url: https://httpbin.org/ +paths: + /anything: + get: + x-adc-plugins: + test1: + test1-key: test1-value + test2: + test2-key: test2-value + x-adc-plugin-test2: + test2-key: test3-value-override + x-adc-plugin-test3: + test3-key: test3-value + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-6.yaml b/libs/converter-openapi/test/assets/extension-6.yaml new file mode 100644 index 00000000..6693d805 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-6.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.0 +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +x-adc-plugins: + root1: + root1-key: value +x-adc-plugin-root2: + root2-key: value +paths: + /anything: + x-adc-plugins: + path1: + path1-key: value + x-adc-plugin-path2: + path2-key: value + get: + x-adc-plugins: + method1: + method1-key: value + path2: + path2-key: value-override + x-adc-plugin-method2: + method2-key: value + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-7.yaml b/libs/converter-openapi/test/assets/extension-7.yaml new file mode 100644 index 00000000..0e791e81 --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-7.yaml @@ -0,0 +1,33 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +x-adc-route-defaults: + test1: test1 + test2: test2 + test3: test3 +paths: + /anything: + x-adc-route-defaults: + test2: test2-override + get: + x-adc-route-defaults: + test3: test3-override + servers: + - url: https://httpbin.org/ + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-8.yaml b/libs/converter-openapi/test/assets/extension-8.yaml new file mode 100644 index 00000000..1c24045b --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-8.yaml @@ -0,0 +1,33 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +x-adc-service-defaults: + test1: test1 + test2: test2 + test3: test3 +paths: + /anything: + x-adc-service-defaults: + test2: test2-override + get: + x-adc-service-defaults: + test3: test3-override + servers: + - url: https://httpbin.org/ + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/assets/extension-9.yaml b/libs/converter-openapi/test/assets/extension-9.yaml new file mode 100644 index 00000000..92b7456c --- /dev/null +++ b/libs/converter-openapi/test/assets/extension-9.yaml @@ -0,0 +1,33 @@ +info: + title: httpbin.org + description: httpbin.org description +servers: + - url: https://httpbin.org/ +x-adc-upstream-defaults: + test1: test1 + test2: test2 + test3: test3 +paths: + /anything: + x-adc-upstream-defaults: + test2: test2-override + get: + x-adc-upstream-defaults: + test3: test3-override + servers: + - url: https://httpbin.org/ + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} + put: + tags: + - Anything + summary: Returns anything passed in request data. + responses: + "200": + description: Anything passed in request + content: {} diff --git a/libs/converter-openapi/test/basic.spec.ts b/libs/converter-openapi/test/basic.spec.ts new file mode 100644 index 00000000..e7e9445f --- /dev/null +++ b/libs/converter-openapi/test/basic.spec.ts @@ -0,0 +1,422 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { OpenAPIV3 } from 'openapi-types'; +import { parse } from 'yaml'; + +import { OpenAPIConverter } from '../src'; +import { loadAsset, runTask } from './utils'; + +describe('Basic', () => { + it('case 1 (single path)', async () => { + const oas = parse(loadAsset('basic-1.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 2 (multiple paths)', async () => { + const oas = parse(loadAsset('basic-2.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Absolutely 302 Redirects n times.', + methods: ['GET'], + name: 'httpbin.org_absolute-redirectn_get', + uris: ['/absolute-redirect/:n'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 3 (multiple servers)', async () => { + const oas = parse(loadAsset('basic-3.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [ + { host: 'httpbin.org', port: 443, weight: 100 }, + { host: 'httpbin.net', port: 443, weight: 100 }, + { host: 'httpbin.com', port: 80, weight: 100 }, + { host: 'httpbin.com', port: 8080, weight: 100 }, + { host: 'httpbin.us', port: 80, weight: 100 }, // Path that is not the first node will be ignored + ], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 4 (server variables)', async () => { + const oas = parse(loadAsset('basic-4.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/test1Value/test2Value', // The path prefix follows only the first server, use default values for variables + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [ + { host: 'httpbin.us', port: 80, weight: 100 }, + { host: 'httpbin.org', port: 443, weight: 100 }, + ], + pass_host: 'pass', + scheme: 'http', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 5 (servers in path/operation)', async () => { + const oas = parse(loadAsset('basic-5.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Absolutely 302 Redirects n times.', + methods: ['GET'], + name: 'httpbin.org_absolute-redirectn_get', + uris: ['/absolute-redirect/:n'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.com', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.net', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 6 (route-less main service)', async () => { + const oas = parse(loadAsset('basic-6.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.com', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.net', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 7 (route name)', async () => { + const oas = parse(loadAsset('basic-7.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', // Use generate name + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'Anything_PUT', // Use operationId if that is exist + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); +}); diff --git a/libs/converter-openapi/test/extension.spec.ts b/libs/converter-openapi/test/extension.spec.ts new file mode 100644 index 00000000..6f97e847 --- /dev/null +++ b/libs/converter-openapi/test/extension.spec.ts @@ -0,0 +1,632 @@ +import { parse } from 'yaml'; + +import { OpenAPIConverter } from '../src'; +import { loadAsset, runTask } from './utils'; + +describe('Extension', () => { + it('case 1 (override resource name)', async () => { + const oas = parse(loadAsset('extension-1.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'override service name', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'override route name', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 2 (override resource name by empty string)', async () => { + const oas = parse(loadAsset('extension-2.yaml')); + await expect(runTask(new OpenAPIConverter().toADC(oas))).rejects.toThrow(); + }); + + it('case 3 (add labels)', async () => { + const oas = parse(loadAsset('extension-3.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + labels: { test1: 'test1' }, + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + labels: { test2: 'test2', test3: ['test3', 'test4'] }, + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 4 (incorrect labels format)', async () => { + const oas = parse(loadAsset('extension-4.yaml')); + await expect(runTask(new OpenAPIConverter().toADC(oas))).rejects.toThrow(); + }); + + it('case 5 (add plugins)', async () => { + const oas = parse(loadAsset('extension-5.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + plugins: { + test1: { 'test1-key': 'test1-value' }, + test2: { 'test2-key': 'test3-value-override' }, // Individual plugins will override the plugin configuration in the list + test3: { 'test3-key': 'test3-value' }, + }, + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + plugins: { + test1: { 'test1-key': 'test1-value' }, + test2: { 'test2-key': 'test3-value-override' }, // Dito + test3: { 'test3-key': 'test3-value' }, + }, + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 6 (add plugins at all levels)', async () => { + const oas = parse(loadAsset('extension-6.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + plugins: { + root1: { 'root1-key': 'value' }, + root2: { 'root2-key': 'value' }, + }, + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + plugins: { + method1: { 'method1-key': 'value' }, + method2: { 'method2-key': 'value' }, + path1: { 'path1-key': 'value' }, + path2: { 'path2-key': 'value-override' }, + }, + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + plugins: { + path1: { 'path1-key': 'value' }, + path2: { 'path2-key': 'value' }, + }, + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 7 (route defaults)', async () => { + const oas = parse(loadAsset('extension-7.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + test1: 'test1', + test2: 'test2-override', // Override by path level x-adc-route-defaults + test3: 'test3', // Keep due to no specific x-adc-route-defaults in the PUT method + uris: ['/anything'], + }, + ], + upstream: { + nodes: [ + { + host: 'httpbin.org', + port: 443, + weight: 100, + }, + ], + pass_host: 'pass', + scheme: 'https', + timeout: { + connect: 60, + read: 60, + send: 60, + }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + test1: 'test1', + test2: 'test2-override', // Override by path level x-adc-route-defaults + test3: 'test3-override', // Override by opertaion level x-adc-route-defaults + uris: ['/anything'], + }, + ], + upstream: { + nodes: [ + { + host: 'httpbin.org', + port: 443, + weight: 100, + }, + ], + pass_host: 'pass', + scheme: 'https', + timeout: { + connect: 60, + read: 60, + send: 60, + }, + }, + }, + ], + }); + }); + + it('case 8 (service defaults)', async () => { + const oas = parse(loadAsset('extension-8.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 9 (upstream defaults)', async () => { + const oas = parse(loadAsset('extension-9.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 10 (service and upstream defaults)', async () => { + const oas = parse(loadAsset('extension-10.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 11 (route, service and upstream defaults)', async () => { + const oas = parse(loadAsset('extension-11.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_post', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['POST'], + name: 'httpbin.org_anything_post', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_delete', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['DELETE'], + name: 'httpbin.org_anything_delete', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_patch', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PATCH'], + name: 'httpbin.org_anything_patch', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + test1: 'test1', + test2: 'test2-override', + test3: 'test3-override', + uris: ['/anything'], + }, + { + description: 'Returns anything passed in request data.', + methods: ['OPTIONS'], + name: 'httpbin.org_anything_options', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + uris: ['/anything'], + }, + ], + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + upstream: { + nodes: [{ host: 'httpbin.org', port: 443, weight: 100 }], + pass_host: 'pass', + scheme: 'https', + test1: 'test1', + test2: 'test2-override', + test3: 'test3', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); + + it('case 12 (upstream node defaults)', async () => { + const oas = parse(loadAsset('extension-12.yaml')); + const config = await runTask(new OpenAPIConverter().toADC(oas)); + + expect(config).toEqual({ + services: [ + { + description: 'httpbin.org description', + name: 'httpbin.org', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['PUT'], + name: 'httpbin.org_anything_put', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [ + { + host: 'httpbin.org', + port: 443, + test1: 'test1', + test2: 'test2', + test3: 'test3', + weight: 100, + }, + { host: 'httpbin.com', port: 443, test4: 'test4', weight: 100 }, + ], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + { + description: 'httpbin.org description', + name: 'httpbin.org_anything_get', + path_prefix: '/', + routes: [ + { + description: 'Returns anything passed in request data.', + methods: ['GET'], + name: 'httpbin.org_anything_get', + uris: ['/anything'], + }, + ], + upstream: { + nodes: [ + { + host: 'httpbin.org', + port: 443, + test1: 'test1-override', + test2: 'test2-override', + test3: 'test3-override', + weight: 100, + }, + ], + pass_host: 'pass', + scheme: 'https', + timeout: { connect: 60, read: 60, send: 60 }, + }, + }, + ], + }); + }); +}); diff --git a/libs/converter-openapi/test/utils.ts b/libs/converter-openapi/test/utils.ts new file mode 100644 index 00000000..b494604c --- /dev/null +++ b/libs/converter-openapi/test/utils.ts @@ -0,0 +1,13 @@ +import { Listr, SilentRenderer } from 'listr2'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +export const loadAsset = (fileName: string) => + readFileSync(join(__dirname, `assets/${fileName}`)).toString('utf-8'); + +export const runTask = async (tasks: Listr) => { + //@ts-expect-error just ignore + tasks.renderer = new SilentRenderer(); + await tasks.run({ local: {} }); + return tasks.ctx.local; +}; diff --git a/libs/converter-openapi/tsconfig.json b/libs/converter-openapi/tsconfig.json new file mode 100644 index 00000000..19b9eece --- /dev/null +++ b/libs/converter-openapi/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs" + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/converter-openapi/tsconfig.lib.json b/libs/converter-openapi/tsconfig.lib.json new file mode 100644 index 00000000..840de3d5 --- /dev/null +++ b/libs/converter-openapi/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": [ + "node" + ], + "lib": [ + "ESNext" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "include": [ + "src/**/*.ts" + ] +} diff --git a/libs/converter-openapi/tsconfig.spec.json b/libs/converter-openapi/tsconfig.spec.json new file mode 100644 index 00000000..3a831592 --- /dev/null +++ b/libs/converter-openapi/tsconfig.spec.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": [ + "jest", + "node" + ], + "lib": [ + "ESNext" + ], + "esModuleInterop": true + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts", + "test/**/*.spec.ts" + ] +} diff --git a/libs/sdk/.eslintrc.json b/libs/sdk/.eslintrc.json new file mode 100644 index 00000000..adbe7ae2 --- /dev/null +++ b/libs/sdk/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": "error" + } + } + ] +} diff --git a/libs/sdk/README.md b/libs/sdk/README.md new file mode 100644 index 00000000..4a5ca436 --- /dev/null +++ b/libs/sdk/README.md @@ -0,0 +1,11 @@ +# sdk + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build sdk` to build the library. + +## Running unit tests + +Run `nx test sdk` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/sdk/jest.config.ts b/libs/sdk/jest.config.ts new file mode 100644 index 00000000..a9085bb8 --- /dev/null +++ b/libs/sdk/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'sdk', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/libs/sdk', +}; diff --git a/libs/sdk/package.json b/libs/sdk/package.json new file mode 100644 index 00000000..cecd170c --- /dev/null +++ b/libs/sdk/package.json @@ -0,0 +1,4 @@ +{ + "name": "@api7/adc-sdk", + "version": "0.0.1" +} diff --git a/libs/sdk/project.json b/libs/sdk/project.json new file mode 100644 index 00000000..df0b0545 --- /dev/null +++ b/libs/sdk/project.json @@ -0,0 +1,31 @@ +{ + "name": "sdk", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/sdk/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/sdk", + "tsConfig": "libs/sdk/tsconfig.lib.json", + "packageJson": "libs/sdk/package.json", + "main": "libs/sdk/src/index.ts", + "assets": ["libs/sdk/*.md"] + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/sdk/jest.config.ts" + } + } + }, + "tags": [] +} diff --git a/libs/sdk/src/backend/index.ts b/libs/sdk/src/backend/index.ts new file mode 100644 index 00000000..c246ae29 --- /dev/null +++ b/libs/sdk/src/backend/index.ts @@ -0,0 +1,25 @@ +import { Listr } from 'listr2'; + +import * as ADCSDK from '..'; + +export interface BackendOptions { + server: string; + token: string; + gatewayGroup?: string; + timeout?: number; + caCertFile?: string; + tlsClientCertFile?: string; + tlsClientKeyFile?: string; + tlsSkipVerify?: boolean; + verbose?: number; +} + +export interface Backend { + ping: () => Promise; + + dump: () => Promise>; + sync: () => Promise; + + supportValidate?: () => Promise; + supportStreamRoute?: () => Promise; +} diff --git a/libs/sdk/src/converter/index.ts b/libs/sdk/src/converter/index.ts new file mode 100644 index 00000000..c3687e72 --- /dev/null +++ b/libs/sdk/src/converter/index.ts @@ -0,0 +1,9 @@ +import { Listr } from 'listr2'; + +import { Configuration } from '../core'; + +export interface Converter { + toADC: (input: unknown) => Listr<{ + local: Configuration; + }>; +} diff --git a/libs/sdk/src/core/differ.ts b/libs/sdk/src/core/differ.ts new file mode 100644 index 00000000..fb36e0e8 --- /dev/null +++ b/libs/sdk/src/core/differ.ts @@ -0,0 +1,36 @@ +import { Diff } from 'deep-diff'; + +import { Plugin, Resource, ResourceType } from '.'; + +export enum EventType { + CREATE = 'create', + DELETE = 'delete', + UPDATE = 'update', + + // Internal use only, the backend does not need to handle such event type + ONLY_SUB_EVENTS = 'only_sub_events', +} + +export type Event = { + type: EventType; + resourceId: string; + resourceName: string; + diff?: Array>; + + resourceType: ResourceType; + oldValue?: Resource; + newValue?: Resource; + + // for nested events + parentId?: string; + subEvents?: Array; +}; + +export type ResourceName = string; +export type ResourceId = string; +export type ResourceDefaultValue = Partial>; +export type PluginDefaultValue = Record; +export type DefaultValue = { + core?: ResourceDefaultValue; + plugins?: PluginDefaultValue; +}; diff --git a/libs/sdk/src/core/index.ts b/libs/sdk/src/core/index.ts new file mode 100644 index 00000000..93282c63 --- /dev/null +++ b/libs/sdk/src/core/index.ts @@ -0,0 +1,248 @@ +export * from './differ'; +export * from './resource'; + +export type Labels = Record>; +export type Plugin = Record; +export type Plugins = Record; +export type Expr = Array>; + +export interface Route { + name: string; + description?: string; + labels?: Labels; + + hosts?: Array; + uris: Array; + priority?: number; + timeout?: UpstreamTimeout; + vars?: Expr; + methods?: Array; + enable_websocket?: boolean; + remote_addrs?: Array; + plugins?: Plugins; + plugin_config_id?: string; + filter_func?: string; + service_id?: string; + + metadata?: ResourceMetadata; +} + +export interface Service { + name: string; + description?: string; + labels?: Labels; + + upstream?: Upstream; + plugins?: Plugins; + path_prefix?: string; + strip_path_prefix?: boolean; + hosts?: Array; + + routes?: Array; + stream_routes?: Array; + + metadata?: ResourceMetadata; +} + +export type UpstreamBalancer = 'roundrobin' | 'chash' | 'least_conn' | 'ewma'; +export type UpstreamScheme = + | 'grpc' + | 'grpcs' + | 'http' + | 'https' + | 'tcp' + | 'tls' + | 'udp' + | 'kafka'; +export type UpstreamPassHost = 'pass' | 'node' | 'rewrite'; +export interface UpstreamNode { + host: string; + port: number; + weight: number; + priority?: number; + metadata?: { [key: string]: unknown }; +} +export interface UpstreamTimeout { + connect: number; + send: number; + read: number; +} +export interface UpstreamClientTLS { + cert: string; + key: string; + client_cert_id: string; + verify: boolean; +} +export interface UpstreamKeepalivePool { + size: number; + idle_timeout: number; + requests: number; +} +export interface UpstreamHealthCheck { + active: UpstreamHealthCheckActive; + passive: UpstreamHealthCheckPassive; +} +export interface UpstreamHealthCheckActive { + type?: string; + timeout?: number; + concurrency?: number; + host: string; + port: number; + http_path: string; + https_verify_cert: boolean; + http_request_headers: Array; + healthy: UpstreamHealthCheckActiveHealthy; + unhealthy: UpstreamHealthCheckActiveUnhealthy; +} +export interface UpstreamHealthCheckPassive { + type: string; + healthy: UpstreamHealthCheckPassiveHealthy; + unhealthy: UpstreamHealthCheckPassiveUnhealthy; +} +export interface UpstreamHealthCheckPassiveHealthy { + http_statuses: Array; + successes: number; +} +export interface UpstreamHealthCheckPassiveUnhealthy { + http_statuses: Array; + http_failures: number; + tcp_failures: number; + timeouts: number; +} + +export type UpstreamHealthCheckActiveHealthy = { + interval: number; +} & UpstreamHealthCheckPassiveHealthy; +export type UpstreamHealthCheckActiveUnhealthy = { + interval: number; +} & UpstreamHealthCheckPassiveUnhealthy; + +export interface Upstream { + name?: string; + description?: string; + labels?: Labels; + + type?: UpstreamBalancer; + hash_on?: string; + key?: string; + checks?: UpstreamHealthCheck; + nodes?: Array; + scheme?: UpstreamScheme; + retries?: number; + retry_timeout?: number; + timeout?: UpstreamTimeout; + tls?: UpstreamClientTLS; + keepalive_pool?: UpstreamKeepalivePool; + pass_host?: UpstreamPassHost; + upstream_host?: string; + + service_name?: string; + discovery_type?: string; + discovery_args?: Record; + + metadata?: ResourceMetadata; +} + +export type SSLType = 'server' | 'client'; +export interface SSLClientMTLS { + ca: string; + depth: number; + skip_mtls_uri_regex?: Array; +} +export interface SSLCertificate { + certificate: string; + key: string; +} +export interface SSL { + labels?: Labels; + + type?: SSLType; + snis: Array; + certificates: Array; + client?: SSLClientMTLS; + ssl_protocols?: Array; + + metadata?: ResourceMetadata; +} + +export interface PluginConfig { + name: string; + description?: string; + labels?: Labels; + + plugins: Plugins; + + metadata?: ResourceMetadata; +} + +export type GlobalRule = Record; + +export type PluginMetadata = Record; + +export interface Consumer { + username: string; + description?: string; + labels?: Labels; + + plugins?: Plugins; +} + +export interface ConsumerGroup { + name: string; + description?: string; + labels?: Labels; + + plugins: Plugins; + + consumers?: Array; + + metadata?: ResourceMetadata; +} + +export interface StreamRoute { + name: string; + description?: string; + labels?: Labels; + + plugins?: Plugins; + + remote_addr?: string; + server_addr?: string; + server_port?: number; + sni?: string; + + metadata?: ResourceMetadata; +} + +export interface ResourceMetadata { + readonly id: string; +} + +export interface Configuration { + services?: Array; + ssls?: Array; + consumers?: Array; + + // object format resources + global_rules?: Record; + plugin_metadata?: Record; + + // APISIX only resources + routes?: Array; + stream_routes?: Array; + /* consumer_groups?: Array; + plugin_configs?: Array; + upstreams?: Array; */ +} + +export type Resource = + | Route + | SSL + | GlobalRule + | PluginConfig + | PluginMetadata + | Consumer + | ConsumerGroup + | StreamRoute + | Service + | Upstream; diff --git a/libs/sdk/src/core/resource.ts b/libs/sdk/src/core/resource.ts new file mode 100644 index 00000000..646b4d18 --- /dev/null +++ b/libs/sdk/src/core/resource.ts @@ -0,0 +1,12 @@ +export enum ResourceType { + ROUTE = 'route', + SERVICE = 'service', + UPSTREAM = 'upstream', + SSL = 'ssl', + GLOBAL_RULE = 'global_rule', + PLUGIN_CONFIG = 'plugin_config', + PLUGIN_METADATA = 'plugin_metadata', + CONSUMER = 'consumer', + CONSUMER_GROUP = 'consumer_group', + STREAM_ROUTE = 'stream_route', +} diff --git a/libs/sdk/src/index.ts b/libs/sdk/src/index.ts new file mode 100644 index 00000000..36c4a044 --- /dev/null +++ b/libs/sdk/src/index.ts @@ -0,0 +1,4 @@ +export * from './backend'; +export * from './converter'; +export * from './core'; +export * from './utils'; diff --git a/libs/sdk/src/utils.spec.ts b/libs/sdk/src/utils.spec.ts new file mode 100644 index 00000000..b2b9eee3 --- /dev/null +++ b/libs/sdk/src/utils.spec.ts @@ -0,0 +1,21 @@ +import { recursiveOmitUndefined } from './utils'; + +describe('SDK utils', () => { + it('recursiveOmitUndefined', () => { + expect( + recursiveOmitUndefined({ + test: 'test', + removed: undefined, + test2: { + test3: 'test', + removed: undefined, + }, + test5: ['test', undefined], + }), + ).toEqual({ + test: 'test', + test2: { test3: 'test' }, + test5: ['test', undefined], + }); + }); +}); diff --git a/libs/sdk/src/utils.ts b/libs/sdk/src/utils.ts new file mode 100644 index 00000000..f8095016 --- /dev/null +++ b/libs/sdk/src/utils.ts @@ -0,0 +1,45 @@ +import { isUndefined, mapValues, pickBy } from 'lodash'; +import { createHash } from 'node:crypto'; +import { Signale } from 'signale'; + +const generateId = (name: string): string => { + return createHash('sha1').update(name).digest('hex'); +}; + +const recursiveOmitUndefined = (obj: T) => { + return typeof obj === 'object' && !Array.isArray(obj) + ? (pickBy( + mapValues(obj, recursiveOmitUndefined), + (value) => !isUndefined(value), + ) as T) + : obj; +}; + +const getLogger = (scope: Array = ['ADC']) => Logger.getLogger(scope); + +class Logger { + private static instance: Signale; + public static getLogger(scope: Array = ['ADC']): Signale { + if (!Logger.instance) + Logger.instance = new Signale({ + config: { + displayTimestamp: true, + }, + }); + + return new Proxy(Logger.instance.scope(...scope), { + get: (obj, prop) => { + /* if (prop === 'start') + return () => console.log('NO DEBUG MODE, IGNORE LOG'); */ + + return obj[prop]; + }, + }); + } +} + +export const utils = { + generateId, + recursiveOmitUndefined, + getLogger, +}; diff --git a/libs/sdk/tsconfig.json b/libs/sdk/tsconfig.json new file mode 100644 index 00000000..19b9eece --- /dev/null +++ b/libs/sdk/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs" + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/sdk/tsconfig.lib.json b/libs/sdk/tsconfig.lib.json new file mode 100644 index 00000000..01e881b7 --- /dev/null +++ b/libs/sdk/tsconfig.lib.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": [ + "node" + ], + "esModuleInterop": true + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "include": [ + "src/**/*.ts" + ] +} diff --git a/libs/sdk/tsconfig.spec.json b/libs/sdk/tsconfig.spec.json new file mode 100644 index 00000000..9b2a121d --- /dev/null +++ b/libs/sdk/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/main.go b/main.go deleted file mode 100644 index f2a9a1d6..00000000 --- a/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "github.com/api7/adc/cmd" - -func main() { - cmd.Execute() -} diff --git a/nx.json b/nx.json new file mode 100644 index 00000000..88816ee8 --- /dev/null +++ b/nx.json @@ -0,0 +1,55 @@ +{ + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "targetDefaults": { + "build": { + "cache": true, + "dependsOn": ["^build"], + "inputs": ["production", "^production"] + }, + "lint": { + "cache": true, + "inputs": [ + "default", + "{workspaceRoot}/.eslintrc.json", + "{workspaceRoot}/.eslintignore", + "{workspaceRoot}/eslint.config.js" + ] + }, + "@nx/jest:jest": { + "cache": true, + "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], + "options": { + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + }, + "namedInputs": { + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "production": [ + "default", + "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", + "!{projectRoot}/tsconfig.spec.json", + "!{projectRoot}/jest.config.[jt]s", + "!{projectRoot}/src/test-setup.[jt]s", + "!{projectRoot}/test-setup.[jt]s", + "!{projectRoot}/.eslintrc.json", + "!{projectRoot}/eslint.config.js" + ], + "sharedGlobals": [] + }, + "plugins": [ + { + "plugin": "@nx/eslint/plugin", + "options": { + "targetName": "eslint:lint", + "extensions": ["ts", "tsx", "js", "jsx", "html", "vue"] + } + } + ] +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..2223f047 --- /dev/null +++ b/package.json @@ -0,0 +1,71 @@ +{ + "name": "@api7/adc", + "version": "0.0.0", + "license": "MIT", + "scripts": {}, + "private": true, + "devDependencies": { + "@nx/esbuild": "18.3.4", + "@nx/eslint": "18.3.4", + "@nx/eslint-plugin": "18.3.4", + "@nx/jest": "18.3.4", + "@nx/js": "18.3.4", + "@nx/node": "18.3.4", + "@nx/webpack": "18.3.4", + "@nx/workspace": "18.3.4", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.13", + "@svgr/webpack": "^8.1.0", + "@swc-node/register": "~1.8.0", + "@swc/core": "~1.3.107", + "@swc/helpers": "~0.5.11", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/deep-diff": "^1.0.5", + "@types/jest": "^29.5.12", + "@types/js-yaml": "^4.0.9", + "@types/json-schema": "^7.0.15", + "@types/lodash": "^4.17.4", + "@types/node": "18.16.9", + "@types/pluralize": "^0.0.33", + "@types/qs": "^6.9.15", + "@types/signale": "^1.4.7", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "esbuild": "^0.19.12", + "eslint": "~8.48.0", + "eslint-config-prettier": "^9.1.0", + "jest": "^29.7.0", + "jest-environment-node": "^29.7.0", + "nx": "18.3.4", + "openapi-types": "^12.1.3", + "prettier": "^3.2.5", + "qs": "^6.12.1", + "ts-jest": "^29.1.3", + "ts-node": "^10.9.2", + "typescript": "^5.4.5", + "url-loader": "^4.1.1", + "webpack": "^5.91.0", + "zod-to-json-schema": "^3.23.0" + }, + "dependencies": { + "@readme/openapi-parser": "^2.5.1", + "axios": "^1.7.2", + "chalk": "^4.1.2", + "commander": "^12.1.0", + "deep-diff": "^1.0.2", + "dotenv": "^16.4.5", + "immer": "^10.1.1", + "json-schema": "^0.4.0", + "listr2": "^8.2.1", + "lodash": "^4.17.21", + "parse-duration": "^1.1.0", + "pluralize": "^8.0.0", + "reflect-metadata": "^0.1.14", + "rxjs": "^7.8.1", + "signale": "^1.4.0", + "slugify": "^1.6.6", + "tslib": "^2.6.2", + "winston": "^3.13.0", + "yaml": "^2.4.2", + "zod": "^3.23.8" + } +} diff --git a/pkg/api/apisix/apisix.go b/pkg/api/apisix/apisix.go deleted file mode 100644 index 1fb6b6b7..00000000 --- a/pkg/api/apisix/apisix.go +++ /dev/null @@ -1,72 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type Cluster interface { - Route() Route - Service() Service - Consumer() Consumer - SSL() SSL - GlobalRule() GlobalRule - PluginConfig() PluginConfig - ConsumerGroup() ConsumerGroup - PluginMetadata() PluginMetadata - StreamRoute() StreamRoute - Upstream() Upstream - Ping() error - SupportValidate() (bool, error) - SupportStreamRoute() (bool, error) -} - -type ResourceClient[T any] interface { - Get(ctx context.Context, name string) (*T, error) - List(ctx context.Context) ([]*T, error) - Create(ctx context.Context, ups *T) (*T, error) - Delete(ctx context.Context, name string) error - Update(ctx context.Context, ups *T) (*T, error) - Validate(ctx context.Context, resource *T) error -} - -type Route interface { - ResourceClient[types.Route] -} - -type Service interface { - ResourceClient[types.Service] -} - -type Consumer interface { - ResourceClient[types.Consumer] -} - -type SSL interface { - ResourceClient[types.SSL] -} - -type GlobalRule interface { - ResourceClient[types.GlobalRule] -} - -type PluginConfig interface { - ResourceClient[types.PluginConfig] -} - -type ConsumerGroup interface { - ResourceClient[types.ConsumerGroup] -} - -type PluginMetadata interface { - ResourceClient[types.PluginMetadata] -} - -type StreamRoute interface { - ResourceClient[types.StreamRoute] -} - -type Upstream interface { - ResourceClient[types.Upstream] -} diff --git a/pkg/api/apisix/client.go b/pkg/api/apisix/client.go deleted file mode 100644 index 03cb1a64..00000000 --- a/pkg/api/apisix/client.go +++ /dev/null @@ -1,284 +0,0 @@ -package apisix - -import ( - "bytes" - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strings" - "time" - - "github.com/fatih/color" - "go.uber.org/multierr" -) - -var ( - ErrNotFound = fmt.Errorf("not found") - ErrStillInUse = errors.New("still in use") // We should use force mode - ErrFunctionDisabled = errors.New("function disabled") -) - -type Client struct { - baseURL string - adminKey string - - cli *http.Client -} - -func newClient(baseURL, adminKey string) *Client { - return &Client{ - baseURL: baseURL, - adminKey: adminKey, - cli: &http.Client{ - Timeout: 5 * time.Second, - }, - } -} - -func newClientWithCertificates(baseURL, adminKey string, host string, insecure bool, ca *x509.CertPool, certs []tls.Certificate) *Client { - return &Client{ - baseURL: baseURL, - adminKey: adminKey, - cli: &http.Client{ - Timeout: 5 * time.Second, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecure, - ServerName: host, - RootCAs: ca, - Certificates: certs, - }, - }, - }, - } -} - -func (c *Client) setAdminKey(req *http.Request) { - if c.adminKey != "" { - req.Header.Set("X-API-Key", c.adminKey) - } -} - -func (c *Client) do(req *http.Request) (*http.Response, error) { - c.setAdminKey(req) - return c.cli.Do(req) -} - -func (c *Client) getResource(ctx context.Context, url string) (*item, error) { - var res getResponse - err := makeGetRequest(c, ctx, url, &res) - if err != nil { - return nil, err - } - return &res, nil -} - -func (c *Client) listResource(ctx context.Context, url string) (items, error) { - var res listResponse - - err := makeGetRequest(c, ctx, url, &res) - if err != nil { - return nil, err - } - return res.List, nil -} - -func (c *Client) createResource(ctx context.Context, url string, body []byte) (*item, error) { - var cr createResponse - err := makePutRequest(c, ctx, url, body, &cr) - if err != nil { - return nil, err - } - return &cr, nil -} - -func (c *Client) updateResource(ctx context.Context, url string, body []byte) (*item, error) { - var ur updateResponse - - err := makePutRequest(c, ctx, url, body, &ur) - if err != nil { - return nil, err - } - return &ur, nil -} - -func (c *Client) deleteResource(ctx context.Context, url string) error { - req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) - if err != nil { - return err - } - resp, err := c.do(req) - if err != nil { - return err - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound { - return handleErrorResponse(resp) - } - return nil -} - -func readBody(r io.ReadCloser) (string, error) { - defer r.Close() - - data, err := io.ReadAll(r) - if err != nil { - return "", err - } - return string(data), nil -} - -// getSchema returns the schema of APISIX object. -// -//nolint:unused -func (c *Client) getSchema(ctx context.Context, url string) (string, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return "", err - } - resp, err := c.do(req) - if err != nil { - return "", err - } - - defer resp.Body.Close() - body, err := readBody(resp.Body) - if err != nil { - err = multierr.Append(err, fmt.Errorf("read body failed")) - return "", err - } - - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNotFound { - return "", ErrNotFound - } else { - err = multierr.Append(err, fmt.Errorf("unexpected status code %d", resp.StatusCode)) - err = multierr.Append(err, fmt.Errorf("error message: %s", body)) - } - return "", err - } - - return body, nil -} - -// getList returns a list of string. -// -//nolint:unused -func (c *Client) getList(ctx context.Context, url string) ([]string, error) { - var listResp map[string]interface{} - err := makeGetRequest(c, ctx, url, &listResp) - if err != nil { - return nil, err - } - res := make([]string, 0, len(listResp)) - - for name := range listResp { - res = append(res, name) - } - return res, nil -} - -func (c *Client) validate(ctx context.Context, url string, resource interface{}) error { - jsonData, err := json.Marshal(resource) - if err != nil { - return err - } - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonData)) - if err != nil { - return err - } - resp, err := c.do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return handleErrorResponse(resp) - } - return nil -} - -func isFunctionDisabled(msg string) bool { - return strings.Contains(msg, "is disabled") -} - -func makeGetRequest[T any](c *Client, ctx context.Context, url string, result *T) error { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return err - } - resp, err := c.do(req) - if err != nil { - return err - } - - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNotFound { - return ErrNotFound - } - return handleErrorResponse(resp) - } - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(result); err != nil { - return err - } - - return nil -} - -func makePutRequest[T any](c *Client, ctx context.Context, url string, body []byte, result *T) error { - req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewReader(body)) - if err != nil { - return err - } - resp, err := c.do(req) - if err != nil { - return err - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { - return handleErrorResponse(resp) - } - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(result); err != nil { - return err - } - - return nil -} - -func handleErrorResponse(resp *http.Response) error { - respData := &struct { - ErrMsg string `json:"error_msg"` - }{} - - body, err := readBody(resp.Body) - if err != nil { - err = multierr.Append(err, fmt.Errorf("read body failed")) - return err - } - err = json.Unmarshal([]byte(body), respData) - if err != nil { - color.Red("unmarshal response failed:") - color.Red(body) - return err - } - - errMsg := errors.New(respData.ErrMsg) - if isFunctionDisabled(errMsg.Error()) { - return errMsg - } - return multierr.Append(fmt.Errorf("unexpected status code %d", resp.StatusCode), errMsg) -} diff --git a/pkg/api/apisix/cluster.go b/pkg/api/apisix/cluster.go deleted file mode 100644 index f13d905b..00000000 --- a/pkg/api/apisix/cluster.go +++ /dev/null @@ -1,192 +0,0 @@ -package apisix - -import ( - "context" - "crypto/tls" - "crypto/x509" - "errors" - "net/url" - "os" - "strings" - - "github.com/fatih/color" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/config" -) - -type cluster struct { - baseURL string - adminKey string - - cli *Client - - route Route - service Service - consumer Consumer - ssl SSL - globalRule GlobalRule - pluginConfig PluginConfig - consumerGroup ConsumerGroup - pluginMetadata PluginMetadata - streamRoute StreamRoute - upstream Upstream -} - -func NewCluster(ctx context.Context, conf config.ClientConfig) (Cluster, error) { - c := &cluster{ - baseURL: conf.Server, - adminKey: conf.Token, - } - - var cli *Client - if conf.CAPath != "" && conf.Certificate != "" && conf.CertificateKey != "" { - rootCA, err := os.ReadFile(conf.CAPath) - if err != nil { - color.Red("Failed to read CA file: %v", err) - return nil, err - } - - caCertPool := x509.NewCertPool() - ok := caCertPool.AppendCertsFromPEM(rootCA) - if !ok { - color.Red("Failed to parse CA certificate") - return nil, errors.New("failed to parse CA certificate") - } - - cert, err := os.ReadFile(conf.Certificate) - if err != nil { - color.Red("Failed to read certificate file: %v", err) - return nil, err - } - key, err := os.ReadFile(conf.CertificateKey) - if err != nil { - color.Red("Failed to read certificate key file: %v", err) - return nil, err - } - keyPair, err := tls.X509KeyPair(cert, key) - if err != nil { - color.Red("Failed to parse x509 key pair: %v", err) - return nil, err - } - - u, err := url.Parse(conf.Server) - if err != nil { - color.Red("Failed to parse APISIX address: %v", err) - } - - cli = newClientWithCertificates(c.baseURL, c.adminKey, u.Hostname(), conf.Insecure, caCertPool, []tls.Certificate{keyPair}) - } else { - cli = newClient(c.baseURL, c.adminKey) - } - - c.cli = cli - c.route = newRoute(cli) - c.service = newService(cli) - c.consumer = newConsumer(cli) - c.ssl = newSSL(cli) - c.globalRule = newGlobalRule(cli) - c.pluginConfig = newPluginConfig(cli) - c.consumerGroup = newConsumerGroup(cli) - c.pluginMetadata = newPluginMetadata(cli) - c.streamRoute = newStreamRoute(cli) - c.upstream = newUpstream(cli) - - return c, nil -} - -// Route implements Cluster.Route method. -func (c *cluster) Route() Route { - return c.route -} - -// Service implements Cluster.Service method. -func (c *cluster) Service() Service { - return c.service -} - -// Consumer implements Cluster.Consumer method. -func (c *cluster) Consumer() Consumer { - return c.consumer -} - -// SSL implements ClusterSSL method. -func (c *cluster) SSL() SSL { - return c.ssl -} - -// GlobalRule implements Cluster.GlobalRule method. -func (c *cluster) GlobalRule() GlobalRule { - return c.globalRule -} - -// PluginConfig implements Cluster.PluginConfig method. -func (c *cluster) PluginConfig() PluginConfig { - return c.pluginConfig -} - -// ConsumerGroup implements Cluster.ConsumerGroup method. -func (c *cluster) ConsumerGroup() ConsumerGroup { - return c.consumerGroup -} - -// PluginMetadata implements Cluster.PluginMetadata method. -func (c *cluster) PluginMetadata() PluginMetadata { - return c.pluginMetadata -} - -// StreamRoute implements Cluster.StreamRoute method. -func (c *cluster) StreamRoute() StreamRoute { - return c.streamRoute -} - -// Upstream implements Cluster.Upstream method. -func (c *cluster) Upstream() Upstream { - return c.upstream -} - -func (c *cluster) Ping() error { - _, err := c.Route().List(context.Background()) - return err -} - -func (c *cluster) SupportValidate() (bool, error) { - err := c.Route().Validate(context.Background(), &types.Route{ - ID: "test_validate_api", - Name: "test_validate_api", - Host: "httpbin.org", - Uri: "/*", - Upstream: &types.Upstream{ - ID: "test_validate_api", - Name: "test_validate_api", - Type: "roundrobin", - Nodes: []types.UpstreamNode{ - { - Host: "httpbin.org", - Port: 80, - Weight: 1, - }, - }, - }, - }) - if err == nil { - return true, nil - } - if strings.Contains(err.Error(), "not found") { - return false, nil - } - - return false, err -} - -func (c *cluster) SupportStreamRoute() (bool, error) { - cli := newResourceClient[types.StreamRoute](c.cli, "stream_routes") - _, err := cli.List(context.Background()) - if err != nil { - if strings.Contains(err.Error(), "stream mode is disabled") { - return false, nil - } - return false, err - } - return true, nil -} diff --git a/pkg/api/apisix/consumer.go b/pkg/api/apisix/consumer.go deleted file mode 100644 index 0ad6b8e0..00000000 --- a/pkg/api/apisix/consumer.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type consumerClient struct { - *resourceClient[types.Consumer] -} - -func newConsumer(c *Client) Consumer { - cli := newResourceClient[types.Consumer](c, "consumers") - return &consumerClient{ - resourceClient: cli, - } -} - -func (u *consumerClient) Create(ctx context.Context, obj *types.Consumer) (*types.Consumer, error) { - return u.resourceClient.Create(ctx, obj.Username, obj) -} - -func (u *consumerClient) Update(ctx context.Context, obj *types.Consumer) (*types.Consumer, error) { - return u.resourceClient.Update(ctx, obj.Username, obj) -} diff --git a/pkg/api/apisix/consumer_group.go b/pkg/api/apisix/consumer_group.go deleted file mode 100644 index 51288770..00000000 --- a/pkg/api/apisix/consumer_group.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type consumerGroupClient struct { - *resourceClient[types.ConsumerGroup] -} - -func newConsumerGroup(c *Client) ConsumerGroup { - cli := newResourceClient[types.ConsumerGroup](c, "consumer_groups") - return &consumerGroupClient{ - resourceClient: cli, - } -} - -func (u *consumerGroupClient) Create(ctx context.Context, obj *types.ConsumerGroup) (*types.ConsumerGroup, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *consumerGroupClient) Update(ctx context.Context, obj *types.ConsumerGroup) (*types.ConsumerGroup, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/global_rule.go b/pkg/api/apisix/global_rule.go deleted file mode 100644 index 8ddc4b58..00000000 --- a/pkg/api/apisix/global_rule.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type globalRuleClient struct { - *resourceClient[types.GlobalRule] -} - -func newGlobalRule(c *Client) GlobalRule { - cli := newResourceClient[types.GlobalRule](c, "global_rules") - return &globalRuleClient{ - resourceClient: cli, - } -} - -func (u *globalRuleClient) Create(ctx context.Context, obj *types.GlobalRule) (*types.GlobalRule, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *globalRuleClient) Update(ctx context.Context, obj *types.GlobalRule) (*types.GlobalRule, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/plugin_config.go b/pkg/api/apisix/plugin_config.go deleted file mode 100644 index 9ff8b848..00000000 --- a/pkg/api/apisix/plugin_config.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type pluginConfigClient struct { - *resourceClient[types.PluginConfig] -} - -func newPluginConfig(c *Client) PluginConfig { - cli := newResourceClient[types.PluginConfig](c, "plugin_configs") - return &pluginConfigClient{ - resourceClient: cli, - } -} - -func (u *pluginConfigClient) Create(ctx context.Context, obj *types.PluginConfig) (*types.PluginConfig, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *pluginConfigClient) Update(ctx context.Context, obj *types.PluginConfig) (*types.PluginConfig, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/plugin_metadata.go b/pkg/api/apisix/plugin_metadata.go deleted file mode 100644 index 21aff78a..00000000 --- a/pkg/api/apisix/plugin_metadata.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type pluginMetadataClient struct { - *resourceClient[types.PluginMetadata] -} - -func newPluginMetadata(c *Client) PluginMetadata { - cli := newResourceClient[types.PluginMetadata](c, "plugin_metadata") - return &pluginMetadataClient{ - resourceClient: cli, - } -} - -func (u *pluginMetadataClient) Create(ctx context.Context, obj *types.PluginMetadata) (*types.PluginMetadata, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *pluginMetadataClient) Update(ctx context.Context, obj *types.PluginMetadata) (*types.PluginMetadata, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/resource.go b/pkg/api/apisix/resource.go deleted file mode 100644 index f9c8b785..00000000 --- a/pkg/api/apisix/resource.go +++ /dev/null @@ -1,86 +0,0 @@ -package apisix - -import ( - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type getResponse = item - -type createResponse = item - -type updateResponse = item - -// listResponse is the v3 version unified LIST response mapping of APISIX. -type listResponse struct { - Total IntOrString `json:"total"` - List items `json:"list"` -} - -// IntOrString processing number and string types, after json deserialization will output int -type IntOrString struct { - IntValue int `json:"int_value"` -} - -func (ios *IntOrString) UnmarshalJSON(p []byte) error { - result := strings.Trim(string(p), "\"") - count, err := strconv.Atoi(result) - if err != nil { - return err - } - ios.IntValue = count - return nil -} - -type items []item - -// UnmarshalJSON implements json.Unmarshaler interface. -// lua-cjson doesn't distinguish empty array and table, -// and by default empty array will be encoded as '{}'. -// We have to maintain the compatibility. -func (items *items) UnmarshalJSON(p []byte) error { - if p[0] == '{' { - if len(p) != 2 { - return errors.New("unexpected non-empty object") - } - return nil - } - var data []item - if err := json.Unmarshal(p, &data); err != nil { - return err - } - *items = data - return nil -} - -type item struct { - Key string `json:"key"` - Value json.RawMessage `json:"value"` -} - -func unmarshalItem[T any](i *item) (*T, error) { - list := strings.Split(i.Key, "/") - if len(list) < 1 { - return nil, fmt.Errorf("bad upstream config key: %s", i.Key) - } - - var obj T - if err := json.Unmarshal(i.Value, &obj); err != nil { - return nil, err - } - - // patch PluginMetadata since the response doesn't contain ID - switch any(obj).(type) { - case *types.PluginMetadata: - any(obj).(*types.PluginMetadata).ID = list[len(list)-1] - case types.PluginMetadata: - any(&obj).(*types.PluginMetadata).ID = list[len(list)-1] - } - - return &obj, nil -} diff --git a/pkg/api/apisix/resource_client.go b/pkg/api/apisix/resource_client.go deleted file mode 100644 index 7a3e0045..00000000 --- a/pkg/api/apisix/resource_client.go +++ /dev/null @@ -1,131 +0,0 @@ -package apisix - -import ( - "context" - "encoding/json" - "fmt" - "reflect" - "strings" -) - -type resourceClient[T any] struct { - baseURL string - resourceName string - resourceURL string - validateURL string - client *Client -} - -func newResourceClient[T any](c *Client, resourceName string) *resourceClient[T] { - baseURL := c.baseURL - if !strings.HasSuffix(baseURL, "/") { - baseURL += "/" - } - if !strings.HasSuffix(baseURL, "apisix/admin/") { - baseURL += "apisix/admin/" - } - - return &resourceClient[T]{ - baseURL: baseURL, - resourceName: resourceName, - resourceURL: baseURL + resourceName, - validateURL: baseURL + "schema/validate/" + resourceName, - client: c, - } -} - -func (u *resourceClient[T]) Get(ctx context.Context, name string) (*T, error) { - url := u.resourceURL + "/" + name - resp, err := u.client.getResource(ctx, url) - if err != nil { - return nil, err - } - - ups, err := unmarshalItem[T](resp) - if err != nil { - return nil, err - } - return ups, nil -} - -func (u *resourceClient[T]) List(ctx context.Context) ([]*T, error) { - svcItems, err := u.client.listResource(ctx, u.resourceURL) - if err != nil { - return nil, err - } - - var items []*T - for _, item := range svcItems { - svc, err := unmarshalItem[T](&item) - if err != nil { - return nil, err - } - items = append(items, svc) - } - return items, nil -} - -func (u *resourceClient[T]) Create(ctx context.Context, id string, obj *T) (*T, error) { - body, err := json.Marshal(obj) - if err != nil { - return nil, err - } - url := u.resourceURL + "/" + id - - resp, err := u.client.createResource(ctx, url, body) - if err != nil { - return nil, err - } - svc, err := unmarshalItem[T](resp) - if err != nil { - return nil, err - } - return svc, err -} - -func (u *resourceClient[T]) Delete(ctx context.Context, name string) error { - url := u.resourceURL + "/" + name - if err := u.client.deleteResource(ctx, url); err != nil { - return err - } - return nil -} - -func (u *resourceClient[T]) Update(ctx context.Context, id string, obj *T) (*T, error) { - body, err := json.Marshal(obj) - if err != nil { - return nil, err - } - - url := u.resourceURL + "/" + id - resp, err := u.client.updateResource(ctx, url, body) - if err != nil { - return nil, err - } - svc, err := unmarshalItem[T](resp) - if err != nil { - return nil, err - } - return svc, err -} - -func GetResourceUniqueKey(resource interface{}) string { - value := reflect.ValueOf(resource) - value = reflect.Indirect(value) - nameOrID := value.FieldByName("ID") - if !nameOrID.IsValid() { - nameOrID = value.FieldByName("Name") - } - if !nameOrID.IsValid() { - nameOrID = value.FieldByName("Username") - } - return nameOrID.String() -} - -func (u *resourceClient[T]) Validate(ctx context.Context, resource *T) error { - err := u.client.validate(ctx, u.validateURL, resource) - if err != nil { - return fmt.Errorf("failed to validate resource '%s (%s)': %s", u.resourceName, GetResourceUniqueKey(resource), err.Error()) - } - return nil -} diff --git a/pkg/api/apisix/route.go b/pkg/api/apisix/route.go deleted file mode 100644 index 5fb1e2de..00000000 --- a/pkg/api/apisix/route.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type routeClient struct { - *resourceClient[types.Route] -} - -func newRoute(c *Client) Route { - cli := newResourceClient[types.Route](c, "routes") - return &routeClient{ - resourceClient: cli, - } -} - -func (u *routeClient) Create(ctx context.Context, obj *types.Route) (*types.Route, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *routeClient) Update(ctx context.Context, obj *types.Route) (*types.Route, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/service.go b/pkg/api/apisix/service.go deleted file mode 100644 index d711d15d..00000000 --- a/pkg/api/apisix/service.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type serviceClient struct { - *resourceClient[types.Service] -} - -func newService(c *Client) Service { - cli := newResourceClient[types.Service](c, "services") - return &serviceClient{ - resourceClient: cli, - } -} - -func (u *serviceClient) Create(ctx context.Context, obj *types.Service) (*types.Service, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *serviceClient) Update(ctx context.Context, obj *types.Service) (*types.Service, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/ssl.go b/pkg/api/apisix/ssl.go deleted file mode 100644 index 94a02ae0..00000000 --- a/pkg/api/apisix/ssl.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type sslClient struct { - *resourceClient[types.SSL] -} - -func newSSL(c *Client) SSL { - cli := newResourceClient[types.SSL](c, "ssls") - return &sslClient{ - resourceClient: cli, - } -} - -func (u *sslClient) Create(ctx context.Context, obj *types.SSL) (*types.SSL, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *sslClient) Update(ctx context.Context, obj *types.SSL) (*types.SSL, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/api/apisix/stream_route.go b/pkg/api/apisix/stream_route.go deleted file mode 100644 index 58f1dc10..00000000 --- a/pkg/api/apisix/stream_route.go +++ /dev/null @@ -1,39 +0,0 @@ -package apisix - -import ( - "context" - "strings" - - "github.com/pkg/errors" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type streamRouteClient struct { - *resourceClient[types.StreamRoute] -} - -func newStreamRoute(c *Client) StreamRoute { - cli := newResourceClient[types.StreamRoute](c, "stream_routes") - return &streamRouteClient{ - resourceClient: cli, - } -} - -func (u *streamRouteClient) Create(ctx context.Context, obj *types.StreamRoute) (*types.StreamRoute, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *streamRouteClient) Update(ctx context.Context, obj *types.StreamRoute) (*types.StreamRoute, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} - -func (u *streamRouteClient) List(ctx context.Context) ([]*types.StreamRoute, error) { - items, err := u.resourceClient.List(ctx) - if err != nil { - if !strings.Contains(err.Error(), "stream mode is disabled") { - return nil, errors.Wrap(err, "failed to list stream_routes") - } - } - return items, nil -} diff --git a/pkg/api/apisix/types/data/README.md b/pkg/api/apisix/types/data/README.md deleted file mode 100644 index 1545b05a..00000000 --- a/pkg/api/apisix/types/data/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# APISIX Plugins Default Values Generation From Schema - -The `default_values.json` file contains default values of all APISIX plugins. - -It is generated by the script `plugin_schemas.lua`. - -## How to Use - -Put the `plugin_schemas.lua` under `/apisix/admin/` folder. - -Then edit `/apisix/admin/init.lua`, add the utility function we need. - -```lua -local function get_plugins_filtered_schema_list() - set_ctx_and_check_token() - local args = get_uri_args() - local subsystem = args["subsystem"] - -- If subsystem is passed then it should be either http or stream. - -- If it is not passed/nil then http will be default. - subsystem = subsystem or "http" - if subsystem == "http" or subsystem == "stream" then - local plugins = require("apisix.admin.plugin_schemas").get_plugins_filtered_schema_list(subsystem) - core.response.exit(200, plugins) - end - core.response.exit(400,"invalid subsystem passed") -end -``` - -After this, find `uri_route` variable and add the entry we need: -```lua -local uri_route = { - // ... - { - paths = [[/apisix/admin/plugins/list]], - methods = {"GET"}, - handler = get_plugins_list, - }, - { - paths = [[/apisix/admin/plugins/filtered_schema_list]], - methods = {"GET"}, - handler = get_plugins_filtered_schema_list, - }, - // ... -``` - -Restart the APISIX, run the following command to generate default values file: - -```bash -curl http://127.0.0.1:9180/apisix/admin/plugins/filtered_schema_list -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X GET | jq --sort-keys . > default_values.json -``` - -We use `jq` here to sort the output keys. diff --git a/pkg/api/apisix/types/data/plugin_default_values.json b/pkg/api/apisix/types/data/plugin_default_values.json deleted file mode 100644 index 0715795d..00000000 --- a/pkg/api/apisix/types/data/plugin_default_values.json +++ /dev/null @@ -1,713 +0,0 @@ -{ -"api-breaker": { -"healthy": { - "http_statuses": [ - 200 - ], - "successes": 3 -}, -"max_breaker_sec": 300, -"unhealthy": { - "failures": 3, - "http_statuses": [ - 500 - ] -} -}, -"authz-keycloak": { - "access_token_expires_in": 300, - "access_token_expires_leeway": 0, - "cache_ttl_seconds": 86400, - "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket", - "http_method_as_scope": false, - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "lazy_load_paths": false, - "permissions": {}, - "policy_enforcement_mode": "ENFORCING", - "refresh_token_expires_in": 3600, - "refresh_token_expires_leeway": 0, - "ssl_verify": true, - "timeout": 3000 -}, -"aws-lambda": { - "authorization": { - "iam": { - "aws_region": "us-east-1", - "service": "execute-api" - } - }, - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "ssl_verify": true, - "timeout": 3000 -}, -"azure-functions": { - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "ssl_verify": true, - "timeout": 3000 -}, -"basic-auth": { - "hide_credentials": false -}, -"chaitin-waf": { - "append_waf_debug_header": false, - "append_waf_resp_header": true -}, -"clickhouse-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "database": "", - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "logtable": "", - "max_retry_count": 0, - "name": "clickhouse-logger", - "password": "", - "retry_delay": 1, - "ssl_verify": true, - "timeout": 3, - "user": "" -}, -"consumer-restriction": { - "rejected_code": 403 -}, -"cors": { - "allow_credential": false, - "allow_headers": "*", - "allow_methods": "*", - "allow_origins": "*", - "expose_headers": "*", - "max_age": 5 -}, -"csrf": { - "expires": 7200, - "name": "apisix-csrf-token" -}, -"datadog": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "max_retry_count": 0, - "name": "datadog", - "prefer_name": true, - "retry_delay": 1 -}, -"elasticsearch-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "max_retry_count": 0, - "name": "elasticsearch-logger", - "retry_delay": 1, - "ssl_verify": true, - "timeout": 10 -}, -"ext-plugin-post-req": { - "allow_degradation": false -}, -"ext-plugin-post-resp": { - "allow_degradation": false -}, -"ext-plugin-pre-req": { - "allow_degradation": false -}, -"file-logger": { - "include_resp_body": false -}, -"forward-auth": { - "allow_degradation": false, - "client_headers": {}, - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "request_headers": {}, - "request_method": "GET", - "ssl_verify": true, - "timeout": 3000, - "upstream_headers": {} -}, -"google-cloud-logging": { - "auth_config": { - "entries_uri": "https://logging.googleapis.com/v2/entries:write", - "scopes": [ - "https://www.googleapis.com/auth/logging.read", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/logging.admin", - "https://www.googleapis.com/auth/cloud-platform" - ], - "token_uri": "https://oauth2.googleapis.com/token" - }, - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "log_id": "apisix.apache.org%2Flogs", - "max_retry_count": 0, - "name": "google-cloud-logging", - "resource": { - "type": "global" - }, - "retry_delay": 1, - "ssl_verify": true -}, -"grpc-transcode": { - "deadline": 0, - "pb_option": [ - "enum_as_name", - "int64_as_number", - "auto_default_values", - "disable_hooks" - ], - "show_status_in_body": false -}, -"gzip": { - "buffers": { - "number": 32, - "size": 4096 - }, - "comp_level": 1, - "http_version": 1.1, - "min_length": 20, - "types": [ - "text/html" - ] -}, -"http-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "concat_method": "json", - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "name": "http logger", - "retry_delay": 1, - "ssl_verify": false, - "timeout": 3 -}, -"ip-restriction": { - "message": "Your IP address is not allowed" -}, -"jwt-auth": { - "cookie": "jwt", - "header": "authorization", - "hide_credentials": false, - "query": "jwt" -}, -"kafka-logger": { - "batch_max_size": 1000, - "brokers": { - "items": { - "sasl_config": { - "mechanism": "PLAIN" - } - }, - "type": "array" - }, - "buffer_duration": 60, - "cluster_name": 1, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "meta_format": "default", - "meta_refresh_interval": 30, - "name": "kafka logger", - "producer_batch_num": 200, - "producer_batch_size": 1048576, - "producer_max_buffering": 50000, - "producer_time_linger": 1, - "producer_type": "async", - "required_acks": 1, - "retry_delay": 1, - "timeout": 3 -}, -"key-auth": { - "header": "apikey", - "hide_credentials": false, - "query": "apikey" -}, -"ldap-auth": { - "tls_verify": false, - "uid": "cn", - "use_tls": false -}, -"limit-conn": { - "allow_degradation": false, - "key_type": "var", - "only_use_default_delay": false, - "rejected_code": 503 -}, -"limit-count": { - "allow_degradation": false, - "else": { - "if": { - "properties": { - "policy": { - "enum": [ - "redis-cluster" - ] - } - } - }, - "then": { - "redis_cluster_ssl": false, - "redis_cluster_ssl_verify": false, - "redis_timeout": 1000 - } - }, - "if": { - "properties": { - "policy": { - "enum": [ - "redis" - ] - } - } - }, - "key": "remote_addr", - "key_type": "var", - "policy": "local", - "rejected_code": 503, - "show_limit_quota_header": true, - "then": { - "redis_database": 0, - "redis_port": 6379, - "redis_ssl": false, - "redis_ssl_verify": false, - "redis_timeout": 1000 - } -}, -"limit-req": { - "allow_degradation": false, - "key_type": "var", - "nodelay": false, - "rejected_code": 503 -}, -"loggly": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "name": "loggly", - "retry_delay": 1, - "severity": "INFO", - "ssl_verify": true, - "tags": [ - "apisix" - ] -}, -"loki-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "endpoint_uri": "/loki/api/v1/push", - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "log_labels": { - "job": "apisix" - }, - "max_retry_count": 0, - "name": "loki logger", - "retry_delay": 1, - "ssl_verify": false, - "tenant_id": "fake", - "timeout": 3000 -}, -"mocking": { - "content_type": "application/json;charset=utf8", - "delay": 0, - "response_status": 200, - "with_mock_header": true -}, -"opa": { - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "ssl_verify": true, - "timeout": 3000, - "with_consumer": false, - "with_route": false, - "with_service": false -}, -"openfunction": { - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "ssl_verify": true, - "timeout": 3000 -}, -"openid-connect": { - "access_token_in_authorization_header": false, - "bearer_only": false, - "introspection_endpoint_auth_method": "client_secret_basic", - "logout_path": "/logout", - "realm": "apisix", - "scope": "openid", - "set_access_token_header": true, - "set_id_token_header": true, - "set_refresh_token_header": false, - "set_userinfo_header": true, - "ssl_verify": false, - "timeout": 3, - "unauth_action": "auth", - "use_pkce": false -}, -"opentelemetry": { - "sampler": { - "name": "always_off", - "options": { - "fraction": 0, - "root": { - "name": "always_off" - } - } - } -}, -"openwhisk": { - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "result": true, - "ssl_verify": true, - "timeout": 3000 -}, -"prometheus": { - "prefer_name": false -}, -"proxy-control": { - "request_buffering": true -}, -"proxy-mirror": { - "path_concat_mode": "replace", - "sample_ratio": 1 -}, -"proxy-rewrite": { - "use_real_request_uri_unsafe": false -}, -"real-ip": { - "recursive": false -}, -"redirect": { - "append_query_string": false, - "encode_uri": false, - "ret_code": 302 -}, -"referer-restriction": { - "bypass_missing": false, - "message": "Your referer host is not allowed" -}, -"request-id": { - "algorithm": "uuid", - "header_name": "X-Request-Id", - "include_in_response": true, - "range_id": { - "default": {}, - "properties": { - "char_set": "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789", - "length": 16 - } - } -}, -"request-validation": { - "rejected_code": 400 -}, -"response-rewrite": { - "body_base64": false, - "dependencies": { - "body": { - "not": { - "required": [ - "filters" - ] - } - }, - "filters": { - "not": { - "required": [ - "body" - ] - } - } - }, - "filters": { - "items": { - "options": "jo", - "scope": "once" - }, - "type": "array" - } -}, -"rocketmq-logger": { - "access_key": "", - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "meta_format": "default", - "name": "rocketmq logger", - "retry_delay": 1, - "secret_key": "", - "timeout": 3, - "use_tls": false -}, -"serverless-post-function": { - "phase": "access" -}, -"serverless-pre-function": { - "phase": "access" -}, -"skywalking": { - "sample_ratio": 1 -}, -"skywalking-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "max_retry_count": 0, - "name": "skywalking logger", - "retry_delay": 1, - "service_instance_name": "APISIX Instance Name", - "service_name": "APISIX", - "timeout": 3 -}, -"sls-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "max_retry_count": 0, - "name": "sls-logger", - "retry_delay": 1, - "timeout": 5000 -}, -"splunk-hec-logging": { - "batch_max_size": 1000, - "buffer_duration": 60, - "endpoint": { - "timeout": 10 - }, - "inactive_timeout": 5, - "max_retry_count": 0, - "name": "splunk-hec-logging", - "retry_delay": 1, - "ssl_verify": true -}, -"syslog": { - "batch_max_size": 1000, - "buffer_duration": 60, - "drop_limit": 1048576, - "flush_limit": 4096, - "inactive_timeout": 5, - "include_req_body": false, - "max_retry_count": 0, - "name": "sys logger", - "pool_size": 5, - "retry_delay": 1, - "sock_type": "tcp", - "timeout": 3000, - "tls": false -}, -"tcp-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "max_retry_count": 0, - "name": "tcp logger", - "retry_delay": 1, - "timeout": 1000, - "tls": false -}, -"tencent-cloud-cls": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "name": "tencent-cloud-cls", - "retry_delay": 1, - "sample_ratio": 1 -}, -"traffic-split": { - "rules": { - "items": { - "weighted_upstreams": { - "default": [ - { - "weight": 1 - } - ], - "items": { - "upstream": { - "checks": { - "active": { - "concurrency": 10, - "healthy": { - "http_statuses": [ - 200, - 302 - ], - "interval": 1, - "successes": 2 - }, - "http_path": "/", - "https_verify_certificate": true, - "timeout": 1, - "unhealthy": { - "http_failures": 5, - "http_statuses": [ - 429, - 404, - 500, - 501, - 502, - 503, - 504, - 505 - ], - "interval": 1, - "tcp_failures": 2, - "timeouts": 3 - } - }, - "passive": { - "healthy": { - "http_statuses": [ - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 226, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308 - ], - "successes": 5 - }, - "unhealthy": { - "http_failures": 5, - "http_statuses": [ - 429, - 500, - 503 - ], - "tcp_failures": 2, - "timeouts": 7 - } - } - }, - "hash_on": "vars", - "keepalive_pool": { - "idle_timeout": 60, - "requests": 1000, - "size": 320 - }, - "nodes": { - "anyOf": [ - null, - { - "items": { - "priority": 0 - }, - "type": "array" - } - ] - }, - "pass_host": "pass", - "scheme": "http", - "tls": { - "dependencies": { - "client_cert": { - "not": { - "required": [ - "client_cert_id" - ] - }, - "required": [ - "client_key" - ] - }, - "client_cert_id": { - "not": { - "required": [ - "client_client", - "client_key" - ] - } - }, - "client_key": { - "not": { - "required": [ - "client_cert_id" - ] - }, - "required": [ - "client_cert" - ] - } - }, - "verify": false - } - }, - "weight": 1 - }, - "type": "array" - } - }, - "type": "array" - } -}, -"ua-restriction": { - "bypass_missing": false, - "message": "Not allowed" -}, -"udp-logger": { - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "include_req_body": false, - "max_retry_count": 0, - "name": "udp logger", - "retry_delay": 1, - "timeout": 3 -}, -"uri-blocker": { - "case_insensitive": false, - "rejected_code": 403 -}, -"wolf-rbac": { - "appid": "unset", - "header_prefix": "X-", - "server": "http://127.0.0.1:12180" -}, -"zipkin": { - "service_name": "APISIX", - "span_version": 2 -} -} diff --git a/pkg/api/apisix/types/data/plugin_schemas.lua b/pkg/api/apisix/types/data/plugin_schemas.lua deleted file mode 100644 index 55187a63..00000000 --- a/pkg/api/apisix/types/data/plugin_schemas.lua +++ /dev/null @@ -1,226 +0,0 @@ -local _M = {} - -local _extract_default - -local function filter_type(schema) - if schema == nil then - return nil - end - local count = 0 - for _ in pairs(schema) do - count = count + 1 - end - if count == 1 and schema.type ~= nil then - return nil - elseif count == 1 and schema.properties ~= nil and next(schema.properties) == nil then - return nil - elseif count == 2 and schema.type == "object" and schema.properties ~= nil and next(schema.properties) == nil then - return nil - elseif count == 2 and schema.type ~= nil and schema.default ~= nil then - return schema.default - elseif count == 1 and schema.type == nil and schema.default ~= nil then - return schema.default - elseif count == 1 and schema.type == nil and schema.properties ~= nil then - return schema.properties - end - return schema -end - -local function extract_default(schema) - if schema == nil then - return schema - end - schema = _extract_default(schema) - if schema == nil then - return schema - end - if next(schema) == nil then - return nil - end - - schema = filter_type(schema) - return schema -end - --- non-recursive equal, array order is not considered -local function table_equal(o1, o2) - if o1 == o2 then return true end - local o1Type = type(o1) - local o2Type = type(o2) - if o1Type ~= o2Type then return false end - - if o1Type == "table" then - local keySet = {} - - for key1, value1 in pairs(o1) do - local value2 = o2[key1] - if value2 == nil or table_equal(value1, value2, ignore_mt) == false then - return false - end - keySet[key1] = true - end - - for key2, _ in pairs(o2) do - if not keySet[key2] then return false end - end - return true - end - - return false -end - -_extract_default = function (schema) - schema["description"] = nil - schema["$comment"] = nil - schema["enum"] = nil - schema["required"] = nil - schema["minItems"] = nil - schema["maxItems"] = nil - schema["uniqueItems"] = nil - schema["minLength"] = nil - schema["maxLength"] = nil - schema["minimum"] = nil - schema["maximum"] = nil - schema["pattern"] = nil - schema["encrypt_fields"] = nil - schema["minProperties"] = nil - --schema["additionalProperties"] = nil -- keep this - schema["title"] = nil - - if schema.allOf then - for i, def in ipairs(schema.allOf) do - schema.allOf[i] = extract_default(def) - end - if #schema.allOf == 0 then - schema.allOf = nil - end - end - if schema.oneOf then - for i, def in ipairs(schema.oneOf) do - schema.oneOf[i] = extract_default(def) - end - if #schema.oneOf == 0 then - schema.oneOf = nil - end - end - if schema.anyOf then - for i, def in ipairs(schema.anyOf) do - schema.anyOf[i] = extract_default(def) - end - if #schema.anyOf == 0 then - schema.anyOf = nil - end - end - - if schema["if"] ~= nil then - schema["then"] = extract_default(schema["then"]) - schema["else"] = extract_default(schema["else"]) - end - - --if not schema.type and not schema.default then - -- -- Not a valid schema, return as is - -- return schema - --end - - if schema.type == "object" or (schema.type == nil and schema.properties ~= nil) then - -- Object schema, iterate properties - if not schema.properties and not schema.patternProperties then - -- no properties? return it as is - if not schema.default then - return nil - end - return schema - end - if schema.properties ~= nil then - for prop, prop_schema in pairs(schema.properties) do - schema.properties[prop] = extract_default(prop_schema) - end - if schema.properties ~= nil and next(schema.properties) == nil then - schema.properties = nil - end - - if schema.properties ~= nil and schema.default ~= nil then - if table_equal(schema.properties, schema.default) then - schema.default = nil - end - end - if schema.properties ~= nil and schema.default == nil then - for prop, prop_schema in pairs(schema.properties) do - schema[prop] = prop_schema - end - schema.properties = nil - end - end - - if schema.patternProperties ~= nil then - for prop, prop_schema in pairs(schema.patternProperties) do - schema.patternProperties[prop] = extract_default(prop_schema) - end - if schema.patternProperties ~= nil and next(schema.patternProperties) == nil then - schema.patternProperties = nil - end - end - - schema.type = nil - elseif schema.type == "array" then - if not schema.items then - return schema - end - - schema.items = extract_default(schema.items) - elseif schema.type == "string" or schema.type == "integer" or schema.type == "number" or schema.type == "boolean" then - if schema.default == nil then - -- No default value, ignore - return nil - end - else - return schema - end - - return schema -end - -function _M.get_plugins_schema_list(subsystem) - local plugins = {} - - for dir in io.popen([[ls -pa ./apisix/plugins | grep -v /]]):lines() do - if string.sub(dir, -4) == ".lua" then - print(dir) - print(string.sub(dir, 1, #dir-4)) - local file = string.sub(dir, 1, #dir-4) - local plugin = require("apisix.plugins."..file) - plugins[file] = plugin.schema - end - end - - return plugins -end - -function _M.get_plugins_filtered_schema_list(subsystem) - local plugins = {} - - for dir in io.popen([[ls -pa ./apisix/plugins | grep -v /]]):lines() do - if string.sub(dir, -4) == ".lua" then - print(dir) - print(string.sub(dir, 1, #dir-4)) - local file = string.sub(dir, 1, #dir-4) - local plugin = require("apisix.plugins."..file) - - -- manual patch - if file == "openfunction" then - plugin.schema.properties.authorization = nil - end - - plugins[file] = extract_default(plugin.schema) - - -- manual patch - if file == "opentelemetry" then - plugins[file].sampler = plugins[file].sampler.default - end - end - end - - return plugins -end - -return _M diff --git a/pkg/api/apisix/types/plugin_schema.go b/pkg/api/apisix/types/plugin_schema.go deleted file mode 100644 index e1fa40a5..00000000 --- a/pkg/api/apisix/types/plugin_schema.go +++ /dev/null @@ -1,230 +0,0 @@ -package types - -import ( - _ "embed" - "encoding/json" -) - -var ( - //go:embed data/plugin_default_values.json - PluginDefaultValuesJson []byte - - PluginDefaultValues map[string]Plugin -) - -func init() { - err := json.Unmarshal(PluginDefaultValuesJson, &PluginDefaultValues) - if err != nil { - panic("failed to parse plugin defaults values") - } -} - -// Return true if the value is primitive types or array of primitive types, -// and very simple objects (without reserved keys) -func isBasicTypes(value interface{}) bool { - switch t := value.(type) { - case bool: - return true - case float64: - return true - case string: - return true - case []interface{}: - for _, v := range t { - if !isBasicTypes(v) { - return false - } - } - return true - //case map[string]interface{}: - // for k, v := range t { - // // we can do this trick because this function validates only the json we generated - // if k == "type" && v == "global" { - // continue - // } - // if _, ok := ReservedKeys[k]; ok { - // return false - // } - // } - // return true - } - return false -} - -// isArrayWithSchema returns the item schema if the defaultObject contains: -// key "type" == "array", has "items" key and the key is an object -// Otherwise, returns nil -func isArrayWithSchema(defaultObject map[string]interface{}) map[string]interface{} { - schemaType, hasType := defaultObject["type"] - items, hasArraySchema := defaultObject["items"] - - if !(hasType && schemaType == "array" && hasArraySchema) { - return nil - } - - itemDefaultSchema, ok := items.(map[string]interface{}) - if !ok { - return nil - } - - return itemDefaultSchema -} - -// ReservedKeys of default values generated from json schema -// Special cases: -// "google-cloud-logging": resource.type = global -// Other keys already checked manually -var ( - ReservedKeys = map[string]struct{}{ - "if": {}, - "then": {}, - "else": {}, - "default": {}, - "type": {}, - "properties": {}, - "dependencies": {}, - "items": {}, - } -) - -func isReservedKey(k string, object map[string]interface{}) bool { - if k == "type" { - v, ok := object[k] - if ok && (v == "array") { - // we have only "array" remains in the schema - return true - } - return false - } - - _, ok := ReservedKeys[k] - return ok -} - -func SetArrayDefaultValue(array []interface{}, schema map[string]interface{}) []interface{} { - if array == nil { - // it shouldn't be nil, but we still check here - array = []interface{}{} - } - - for i, item := range array { - itemObj, ok := item.(map[string]interface{}) - if ok { - array[i] = SetDefaultValue(itemObj, schema) - } - } - - return array -} - -func SetDefaultValue(object, defaultObject map[string]interface{}) map[string]interface{} { - if object == nil { - object = make(map[string]interface{}) - } - - // TODO: Should merge default and properties, but we don't have such schema, so leave it here for now - if properties, hasProperties := defaultObject["properties"]; hasProperties { - if propertiesObj, ok := properties.(map[string]interface{}); ok { - return propertiesObj - } - return object - } - if def, hasDefault := defaultObject["default"]; hasDefault { - if defObj, ok := def.(map[string]interface{}); ok { - return defObj - } - return object - } - - // process keys - for key, defaultValue := range defaultObject { - if isReservedKey(key, object) { - continue - } - value, ok := object[key] - if !ok { - // doesn't exist, simple set the value - if isBasicTypes(defaultValue) { - object[key] = defaultValue - } else { - defaultObj, ok := defaultValue.(map[string]interface{}) - if ok { - arraySchema := isArrayWithSchema(defaultObj) - if arraySchema != nil { - // do nothing, because empty array with schema doesn't have default value - } else { - object[key] = SetDefaultValue(nil, defaultObj) - } - } - } - } else { - // ok should always true - defaultObj, ok := defaultValue.(map[string]interface{}) - if !isBasicTypes(defaultValue) && ok { - // is obj - obj, isObj := value.(map[string]interface{}) - - // is an array - itemArray, isArray := value.([]interface{}) - itemDefaultSchema := isArrayWithSchema(defaultObj) - - if isObj { - // is object, should iterate recursively - object[key] = SetDefaultValue(obj, defaultObj) - } else if isArray && len(itemArray) > 0 && itemDefaultSchema != nil { - object[key] = SetArrayDefaultValue(itemArray, itemDefaultSchema) - } - } - } - } - - return object -} - -func GetPluginDefaultValues(name string, value Plugin) Plugin { - if value == nil { - value = make(Plugin) - } - defaultObject, ok := PluginDefaultValues[name] - if !ok { - return value - } - - if len(value) == 0 { - if ok { - return SetDefaultValue(nil, defaultObject) - } - return value - } - - value = SetDefaultValue(value, defaultObject) - return SpecialPatches(name, value) -} - -func SpecialPatches(name string, value Plugin) Plugin { - setDefaultValue := func(object Plugin, key string, value interface{}) { - _, ok := object[key] - if !ok { - object[key] = value - } - } - - if name == "limit-count" { - policy, ok := value["policy"] - if ok { - if policy == "redis" { - setDefaultValue(value, "redis_database", 0) - setDefaultValue(value, "redis_port", 6379) - setDefaultValue(value, "redis_ssl", false) - setDefaultValue(value, "redis_ssl_verify", false) - setDefaultValue(value, "redis_timeout", 1000) - } else if policy == "redis-cluster" { - setDefaultValue(value, "redis_cluster_ssl", false) - setDefaultValue(value, "redis_cluster_ssl_verify", false) - setDefaultValue(value, "redis_timeout", 1000) - } - } - } - - return value -} diff --git a/pkg/api/apisix/types/plugin_schema_test.go b/pkg/api/apisix/types/plugin_schema_test.go deleted file mode 100644 index 02d02a17..00000000 --- a/pkg/api/apisix/types/plugin_schema_test.go +++ /dev/null @@ -1,412 +0,0 @@ -package types - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -// Special Schemas to test: -// [Done]: -// api-breaker, array of primitive type -// aws-lambda, simple nested object -// request-id, default and properties -// kafka-logger, array type -// google-cloud-logging, special "type" field -// limit-count, if-then-else -// [TODO]: -// traffic-split, array type, anyOf, nested schema -// [leave]: -// response-rewrite, dependencies // We don't care about the dependency, leave here -// grpc-transcode, loggly.severity_map, openid-connect.session, proxy-rewrite.headers(oneOf): additionalProperties=true -// we don't care the additional properties, leave here - -func OutputEqual(t *testing.T, plugin Plugin, expected string) { - out, err := json.Marshal(plugin) - assert.Nil(t, err) - - // This also removes spaces inside string - removeAllSpaces := func(s string) string { - arr := strings.Split(s, "\n") - for i, s := range arr { - s = strings.TrimSpace(s) - s = strings.ReplaceAll(s, " ", "") - - arr[i] = s - } - s = strings.Join(arr, "") - - return s - } - - assert.Equal(t, removeAllSpaces(expected), removeAllSpaces(string(out))) -} - -func TestNoNil(t *testing.T) { - plugin := GetPluginDefaultValues("doesnt-exist", nil) - assert.NotNil(t, plugin) -} - -func TestNoDefaults(t *testing.T) { - plugin := Plugin{ - "authorization": map[string]interface{}{ - "iam": map[string]interface{}{ - "service": "service_name", - }, - "some_field": "some_value", - }, - "keepalive": false, - "keepalive_pool": 100, - } - plugin = GetPluginDefaultValues("doesnt-exist", plugin) - - OutputEqual(t, plugin, `{ - "authorization": { - "iam": { - "service": "service_name" - }, - "some_field": "some_value" - }, - "keepalive": false, - "keepalive_pool": 100 -}`) -} - -// Primitive type array -func TestArrayOfPrimitiveTypes(t *testing.T) { - plugin := GetPluginDefaultValues("api-breaker", nil) - - OutputEqual(t, plugin, `{ - "healthy": { - "http_statuses": [ - 200 - ], - "successes": 3 - }, - "max_breaker_sec": 300, - "unhealthy": { - "failures": 3, - "http_statuses": [ - 500 - ] - } -}`) - - // == value overwrite == - plugin = Plugin{ - "unhealthy": map[string]interface{}{ - "http_statuses": []interface{}{ - 400, 500, 600, 700, - }, - "failures": 5, - }, - "max_breaker_sec": 700, - } - plugin = GetPluginDefaultValues("api-breaker", plugin) - - OutputEqual(t, plugin, `{ - "healthy": { - "http_statuses": [ - 200 - ], - "successes": 3 - }, - "max_breaker_sec": 700, - "unhealthy": { - "failures": 5, - "http_statuses": [ - 400, 500, 600, 700 - ] - } -}`) - -} - -// Nested simple objects -func TestNestedSimpleObject(t *testing.T) { - plugin := GetPluginDefaultValues("aws-lambda", nil) - - OutputEqual(t, plugin, `{ - "authorization": { - "iam": { - "aws_region": "us-east-1", - "service": "execute-api" - } - }, - "keepalive": true, - "keepalive_pool": 5, - "keepalive_timeout": 60000, - "ssl_verify": true, - "timeout": 3000 -}`) - - // == value overwrite == - plugin = Plugin{ - "authorization": map[string]interface{}{ - "iam": map[string]interface{}{ - "service": "service_name", - }, - "some_field": "some_value", - }, - "keepalive": false, - "keepalive_pool": 100, - } - plugin = GetPluginDefaultValues("aws-lambda", plugin) - - OutputEqual(t, plugin, `{ - "authorization": { - "iam": { - "aws_region": "us-east-1", - "service": "service_name" - }, - "some_field": "some_value" - }, - "keepalive": false, - "keepalive_pool": 100, - "keepalive_timeout": 60000, - "ssl_verify": true, - "timeout": 3000 -}`) - -} - -// Default and properties -func TestDefaultAndProperties(t *testing.T) { - plugin := GetPluginDefaultValues("request-id", Plugin{ - "algorithm": "uuid", - }) - - OutputEqual(t, plugin, `{ - "algorithm": "uuid", - "header_name": "X-Request-Id", - "include_in_response": true, - "range_id": { - "char_set": "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789", - "length": 16 - } -}`) -} - -// Empty Array -func TestEmptyArraySchema(t *testing.T) { - plugin := GetPluginDefaultValues("kafka-logger", nil) - - OutputEqual(t, plugin, `{ - "batch_max_size": 1000, - "buffer_duration": 60, - "cluster_name": 1, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "meta_format": "default", - "meta_refresh_interval": 30, - "name": "kafka logger", - "producer_batch_num": 200, - "producer_batch_size": 1048576, - "producer_max_buffering": 50000, - "producer_time_linger": 1, - "producer_type": "async", - "required_acks": 1, - "retry_delay": 1, - "timeout": 3 -}`) - - // == value overwrite == - plugin = Plugin{ - "brokers": []interface{}{}, - } - plugin = GetPluginDefaultValues("kafka-logger", plugin) - - OutputEqual(t, plugin, `{ - "batch_max_size": 1000, - "brokers": [], - "buffer_duration": 60, - "cluster_name": 1, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "meta_format": "default", - "meta_refresh_interval": 30, - "name": "kafka logger", - "producer_batch_num": 200, - "producer_batch_size": 1048576, - "producer_max_buffering": 50000, - "producer_time_linger": 1, - "producer_type": "async", - "required_acks": 1, - "retry_delay": 1, - "timeout": 3 -}`) -} - -// Non-empty Array -func TestArraySchema(t *testing.T) { - // == array value overwrite == - plugin := Plugin{ - "brokers": []interface{}{ - // Note that we shouldn't use struct{} here. Because unmarshal only uses map[string]interface{} type - map[string]interface{}{}, - }, - } - plugin = GetPluginDefaultValues("kafka-logger", plugin) - - OutputEqual(t, plugin, `{ - "batch_max_size": 1000, - "brokers": [{ - "sasl_config": { - "mechanism": "PLAIN" - } - }], - "buffer_duration": 60, - "cluster_name": 1, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "meta_format": "default", - "meta_refresh_interval": 30, - "name": "kafka logger", - "producer_batch_num": 200, - "producer_batch_size": 1048576, - "producer_max_buffering": 50000, - "producer_time_linger": 1, - "producer_type": "async", - "required_acks": 1, - "retry_delay": 1, - "timeout": 3 -}`) - - // Make sure unmarshal also works - plugin = Plugin{} - err := json.Unmarshal([]byte(`{ - "brokers": [{ - "sasl_config": { - "mechanism": "PLAIN" - } - }] -}`), &plugin) - assert.Nil(t, err) - plugin = GetPluginDefaultValues("kafka-logger", plugin) - - OutputEqual(t, plugin, `{ - "batch_max_size": 1000, - "brokers": [{ - "sasl_config": { - "mechanism": "PLAIN" - } - }], - "buffer_duration": 60, - "cluster_name": 1, - "inactive_timeout": 5, - "include_req_body": false, - "include_resp_body": false, - "max_retry_count": 0, - "meta_format": "default", - "meta_refresh_interval": 30, - "name": "kafka logger", - "producer_batch_num": 200, - "producer_batch_size": 1048576, - "producer_max_buffering": 50000, - "producer_time_linger": 1, - "producer_type": "async", - "required_acks": 1, - "retry_delay": 1, - "timeout": 3 -}`) -} - -// google-cloud-logging -func TestGoogleCloudLogging(t *testing.T) { - plugin := GetPluginDefaultValues("google-cloud-logging", nil) - - OutputEqual(t, plugin, `{ - "auth_config": { - "entries_uri": "https://logging.googleapis.com/v2/entries:write", - "scopes": [ - "https://www.googleapis.com/auth/logging.read", - "https://www.googleapis.com/auth/logging.write", - "https://www.googleapis.com/auth/logging.admin", - "https://www.googleapis.com/auth/cloud-platform" - ], - "token_uri": "https://oauth2.googleapis.com/token" - }, - "batch_max_size": 1000, - "buffer_duration": 60, - "inactive_timeout": 5, - "log_id": "apisix.apache.org%2Flogs", - "max_retry_count": 0, - "name": "google-cloud-logging", - "resource": { - "type": "global" - }, - "retry_delay": 1, - "ssl_verify": true -}`) -} - -// limit-count -func TestLimitCount(t *testing.T) { - plugin := GetPluginDefaultValues("limit-count", nil) - - OutputEqual(t, plugin, `{ - "allow_degradation": false, - "key": "remote_addr", - "key_type": "var", - "policy": "local", - "rejected_code": 503, - "show_limit_quota_header": true -}`) - - plugin = GetPluginDefaultValues("limit-count", Plugin{ - "policy": "redis", - }) - - OutputEqual(t, plugin, `{ - "allow_degradation": false, - "key": "remote_addr", - "key_type": "var", - "policy": "redis", - "redis_database": 0, - "redis_port": 6379, - "redis_ssl": false, - "redis_ssl_verify": false, - "redis_timeout": 1000, - "rejected_code": 503, - "show_limit_quota_header": true -}`) - - plugin = GetPluginDefaultValues("limit-count", Plugin{ - "policy": "redis-cluster", - }) - - OutputEqual(t, plugin, `{ - "allow_degradation": false, - "key": "remote_addr", - "key_type": "var", - "policy": "redis-cluster", - "redis_cluster_ssl": false, - "redis_cluster_ssl_verify": false, - "redis_timeout": 1000, - "rejected_code": 503, - "show_limit_quota_header": true -}`) - -} - -//// traffic-split -//func TestTrafficSplit(t *testing.T) { -// plugin := GetPluginDefaultValues("traffic-split", Plugin{ -// "rule": []interface{}{ -// map[string]interface{}{ -// "match": map[string]interface{}{}, -// "weighted_upstream": map[string]interface{}{}, -// }, -// }, -// }) -// -// OutputEqual(t, plugin, ``) -//} diff --git a/pkg/api/apisix/types/resource_schema.go b/pkg/api/apisix/types/resource_schema.go deleted file mode 100644 index bd5b1a44..00000000 --- a/pkg/api/apisix/types/resource_schema.go +++ /dev/null @@ -1,173 +0,0 @@ -package types - -func PtrOf[T any](v T) *T { - return &v -} - -func SetRouteDefaultValues(route *Route) { - if route.Priority == nil { - route.Priority = PtrOf(0) - } - - if route.Status == nil { - route.Status = PtrOf(1) - } - - if route.Upstream != nil { - SetUpstreamDefaultValues(route.Upstream) - } -} - -func SetServiceDefaultValues(service *Service) { - if service.Upstream != nil { - SetUpstreamDefaultValues(service.Upstream) - } -} - -func SetStreamRouteDefaultValues(sr *StreamRoute) { - if sr.Upstream != nil { - SetUpstreamDefaultValues(sr.Upstream) - - if sr.Upstream.Scheme == "http" { - sr.Upstream.Scheme = "tcp" - } - } -} - -func SetUpstreamDefaultValues(upstream *Upstream) { - if upstream.TLS != nil { - if upstream.TLS.Verify == nil { - upstream.TLS.Verify = PtrOf(false) - } - } - - if upstream.KeepalivePool != nil { - if upstream.KeepalivePool.Size != nil { - upstream.KeepalivePool.Size = PtrOf(1) - } - if upstream.KeepalivePool.IdleTimeout != nil { - upstream.KeepalivePool.IdleTimeout = PtrOf(60) - } - if upstream.KeepalivePool.Requests != nil { - upstream.KeepalivePool.Requests = PtrOf(1000) - } - - } - - if len(upstream.Nodes) > 0 { - for i := range upstream.Nodes { - if upstream.Nodes[i].Priority == nil { - upstream.Nodes[i].Priority = PtrOf(0) - } - } - } - - if upstream.Checks != nil { - SetHealthCheckerDefaultValues(upstream.Checks) - } - - if upstream.Type == "" { - upstream.Type = "roundrobin" - } - if upstream.HashOn == "" { - upstream.HashOn = "vars" - } - if upstream.Scheme == "" { - upstream.Scheme = "http" - } - if upstream.PassHost == "" { - upstream.PassHost = "pass" - } -} - -func SetHealthCheckerDefaultValues(checker *UpstreamHealthCheck) { - if checker.Active != nil { - if checker.Active.Type == "" { - checker.Active.Type = "http" - } - if checker.Active.Timeout == nil { - checker.Active.Timeout = PtrOf(1) - } - if checker.Active.Concurrency == nil { - checker.Active.Concurrency = PtrOf(10) - } - if checker.Active.HTTPPath == "" { - checker.Active.HTTPPath = "/" - } - if checker.Active.HTTPSVerifyCert == nil { - checker.Active.HTTPSVerifyCert = PtrOf(true) - } - - // healthy - if checker.Active.Healthy.Interval == nil { - checker.Active.Healthy.Interval = PtrOf(1) - } - if checker.Active.Healthy.HTTPStatuses == nil { - checker.Active.Healthy.HTTPStatuses = []int{200, 302} - } - if checker.Active.Healthy.Successes == nil { - checker.Active.Healthy.Successes = PtrOf(2) - } - - // unhealthy - if checker.Active.Unhealthy.Interval == nil { - checker.Active.Unhealthy.Interval = PtrOf(1) - } - if checker.Active.Unhealthy.HTTPStatuses == nil { - checker.Active.Unhealthy.HTTPStatuses = []int{429, 404, 500, 501, 502, 503, 504, 505} - } - if checker.Active.Unhealthy.HTTPFailures == nil { - checker.Active.Unhealthy.HTTPFailures = PtrOf(5) - } - if checker.Active.Unhealthy.TCPFailures == nil { - checker.Active.Unhealthy.TCPFailures = PtrOf(2) - } - if checker.Active.Unhealthy.Timeouts == nil { - checker.Active.Unhealthy.Timeouts = PtrOf(3) - } - } - - if checker.Passive != nil { - if checker.Passive.Type == "" { - checker.Passive.Type = "http" - } - - // healthy - if checker.Passive.Healthy.HTTPStatuses == nil { - checker.Passive.Healthy.HTTPStatuses = []int{200, 201, 202, 203, 204, 205, 206, 207, - 208, 226, 300, 301, 302, 303, 304, 305, - 306, 307, 308} - } - if checker.Passive.Healthy.Successes == nil { - checker.Passive.Healthy.Successes = PtrOf(5) - } - - // unhealthy - if checker.Passive.Unhealthy.HTTPStatuses == nil { - checker.Passive.Unhealthy.HTTPStatuses = []int{429, 500, 503} - } - if checker.Passive.Unhealthy.HTTPFailures == nil { - checker.Passive.Unhealthy.HTTPFailures = PtrOf(5) - } - if checker.Passive.Unhealthy.TCPFailures == nil { - checker.Passive.Unhealthy.TCPFailures = PtrOf(2) - } - if checker.Passive.Unhealthy.Timeouts == nil { - checker.Passive.Unhealthy.Timeouts = PtrOf(7) - } - } -} - -func SetSSLDefaultValues(ssl *SSL) { - if ssl.Type == "" { - ssl.Type = "server" - } - if ssl.Client != nil { - if ssl.Client.Depth == nil { - ssl.Client.Depth = PtrOf(1) - } - } - if ssl.Status == nil { - ssl.Status = PtrOf(1) - } -} diff --git a/pkg/api/apisix/types/resource_schema_test.go b/pkg/api/apisix/types/resource_schema_test.go deleted file mode 100644 index 603d1b91..00000000 --- a/pkg/api/apisix/types/resource_schema_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "sigs.k8s.io/yaml" -) - -func ExpectInputHasOutput(t *testing.T, input string, output string) { - output = "name: \"\"\n" + output + "version: \"\"\n" - - var content Configuration - - err := yaml.Unmarshal([]byte(input), &content) - assert.Nil(t, err) - - o, err := yaml.Marshal(content) - assert.Nil(t, err) - - assert.Equal(t, output, string(o)) -} - -func TestRouteWithUpstreamDefaultValues(t *testing.T) { - input := `routes: - - name: route1 - uri: "/get" - methods: - - GET - - PUT - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -` - - output := `routes: -- id: "" - methods: - - GET - - PUT - name: route1 - priority: 0 - status: 1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin - uri: /get -` - - ExpectInputHasOutput(t, input, output) -} - -func TestStreamRouteWithUpstreamDefaultValues(t *testing.T) { - input := `stream_routes: - - id: "1" - server_port: 9100 - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -` - - output := `stream_routes: -- id: "1" - server_port: 9100 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: tcp - type: roundrobin -` - - ExpectInputHasOutput(t, input, output) -} - -func TestServiceDefaultValues(t *testing.T) { - input := `services: - - name: svc1 - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -` - output := `services: -- hosts: - - foo1.com - id: "" - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -` - ExpectInputHasOutput(t, input, output) -} diff --git a/pkg/api/apisix/types/types.go b/pkg/api/apisix/types/types.go deleted file mode 100644 index 44fa2579..00000000 --- a/pkg/api/apisix/types/types.go +++ /dev/null @@ -1,679 +0,0 @@ -package types - -import ( - "encoding/json" - "errors" - "fmt" - "reflect" - "strconv" - "strings" -) - -type HasLabels interface { - GetLabels() Labels - SetLabel(k, v string) -} - -var ( - _ HasLabels = (*Route)(nil) - _ HasLabels = (*Service)(nil) - _ HasLabels = (*Consumer)(nil) - _ HasLabels = (*SSL)(nil) - _ HasLabels = (*PluginConfig)(nil) - _ HasLabels = (*ConsumerGroup)(nil) - _ HasLabels = (*StreamRoute)(nil) - _ HasLabels = (*Upstream)(nil) -) - -func FilterResources[T HasLabels](filters Labels, resources []T) []T { - if len(filters) == 0 { - return resources - } - var filtered []T - for _, res := range resources { - labels := res.GetLabels() - if len(labels) > 0 { - matchAll := true - for k, v := range filters { - if label, ok := labels[k]; !ok || label != v { - matchAll = false - break - } - } - if matchAll { - filtered = append(filtered, res) - } - } - } - return filtered -} - -// Configuration is the configuration of services -type Configuration struct { - Name string `yaml:"name" json:"name"` - Version string `yaml:"version" json:"version"` - Meta *ConfigurationMeta `yaml:"meta,omitempty" json:"meta,omitempty"` - Services []*Service `yaml:"services,omitempty" json:"services,omitempty"` - Routes []*Route `yaml:"routes,omitempty" json:"routes,omitempty"` - Consumers []*Consumer `yaml:"consumers,omitempty" json:"consumers,omitempty"` - SSLs []*SSL `yaml:"ssls,omitempty" json:"ssls,omitempty"` - GlobalRules []*GlobalRule `yaml:"global_rules,omitempty" json:"global_rules,omitempty"` - PluginConfigs []*PluginConfig `yaml:"plugin_configs,omitempty" json:"plugin_configs,omitempty"` - ConsumerGroups []*ConsumerGroup `yaml:"consumer_groups,omitempty" json:"consumer_groups,omitempty"` - PluginMetadatas []*PluginMetadata `yaml:"plugin_metadatas,omitempty" json:"plugin_metadatas,omitempty"` - StreamRoutes []*StreamRoute `yaml:"stream_routes,omitempty" json:"stream_routes,omitempty"` - Upstreams []*Upstream `yaml:"upstreams,omitempty" json:"upstreams,omitempty"` -} - -type ConfigurationMode string - -var ( - ModeFull ConfigurationMode = "full" - ModePartial ConfigurationMode = "partial" -) - -type ConfigurationMeta struct { - Mode ConfigurationMode `json:"mode,omitempty" yaml:"mode,omitempty"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` -} - -// Labels is the APISIX resource labels -type Labels map[string]string - -// Vars represents the route match expressions of APISIX. -type Vars [][]StringOrSlice - -// Route apisix route object -type Route struct { - ID string `json:"id" yaml:"id"` - Name string `json:"name" yaml:"name"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - Description string `json:"desc,omitempty" yaml:"desc,omitempty"` - - Host string `json:"host,omitempty" yaml:"host,omitempty"` - Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"` - Uri string `json:"uri,omitempty" yaml:"uri,omitempty"` - Uris []string `json:"uris,omitempty" yaml:"uris,omitempty"` - Priority *int `json:"priority,omitempty" yaml:"priority,omitempty"` - Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` - Vars Vars `json:"vars,omitempty" yaml:"vars,omitempty"` - Methods []string `json:"methods,omitempty" yaml:"methods,omitempty"` - EnableWebsocket bool `json:"enable_websocket,omitempty" yaml:"enable_websocket,omitempty"` - RemoteAddr string `json:"remote_addr,omitempty" yaml:"remote_addr,omitempty"` - RemoteAddrs []string `json:"remote_addrs,omitempty" yaml:"remote_addrs,omitempty"` - Upstream *Upstream `json:"upstream,omitempty" yaml:"upstream,omitempty"` - UpstreamID string `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"` - ServiceID string `json:"service_id,omitempty" yaml:"service_id,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` - PluginConfigID string `json:"plugin_config_id,omitempty" yaml:"plugin_config_id,omitempty"` - FilterFunc string `json:"filter_func,omitempty" yaml:"filter_func,omitempty"` - Script string `json:"script,omitempty" yaml:"script,omitempty"` - ScriptID string `json:"script_id,omitempty" yaml:"script_id,omitempty"` - Status *int `json:"status,omitempty" yaml:"status,omitempty"` - - // api7 - StripPathPrefix bool `json:"strip_path_prefix,omitempty" yaml:"strip_path_prefix,omitempty"` -} - -func (r *Route) GetLabels() Labels { - return r.Labels -} - -func (r *Route) SetLabel(k, v string) { - if r.Labels == nil { - r.Labels = map[string]string{} - } - r.Labels[k] = v -} - -func (r *Route) UnmarshalJSON(cont []byte) error { - type unmarshalerRoute Route - - var route unmarshalerRoute - err := json.Unmarshal(cont, &route) - if err != nil { - return err - } - - *r = Route(route) - - SetRouteDefaultValues(r) - return nil -} - -// Service is the abstraction of a backend service on API gateway. -type Service struct { - ID string `json:"id" yaml:"id"` - - Name string `json:"name" yaml:"name"` - Description string `json:"desc,omitempty" yaml:"desc,omitempty"` - // Labels are used for resource classification and indexing - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - // HTTP hosts for this service. - Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"` - // Plugin settings on Service level - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` - // Upstream settings for the Service. - Upstream *Upstream `json:"upstream,omitempty" yaml:"upstream,omitempty"` - // UpstreamID settings for the Service. - UpstreamID string `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"` - // Enables a websocket. Set to false by default. - EnableWebsocket bool `json:"enable_websocket,omitempty" yaml:"enable_websocket,omitempty"` - - Script string `json:"script,omitempty" yaml:"script,omitempty"` - - // api7 - PathPrefix string `json:"path_prefix,omitempty" yaml:"path_prefix,omitempty"` - Status int `json:"status,omitempty" yaml:"status,omitempty"` -} - -func (s *Service) GetLabels() Labels { - return s.Labels -} - -func (s *Service) SetLabel(k, v string) { - if s.Labels == nil { - s.Labels = map[string]string{} - } - s.Labels[k] = v -} - -func (s *Service) UnmarshalJSON(cont []byte) error { - type unmarshalerService Service - - var route unmarshalerService - err := json.Unmarshal(cont, &route) - if err != nil { - return err - } - - *s = Service(route) - - SetServiceDefaultValues(s) - return nil -} - -// Upstream is the definition of the upstream on Service. -type Upstream struct { - // ID is the upstream name. It should be unique among all upstreams - // in the same service. - ID string `json:"id" yaml:"id"` - Name string `json:"name" yaml:"name"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - - Type string `json:"type,omitempty" yaml:"type,omitempty"` - HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` - Checks *UpstreamHealthCheck `json:"checks,omitempty" yaml:"checks,omitempty"` - Nodes UpstreamNodes `json:"nodes" yaml:"nodes"` - Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` - Retries int `json:"retries,omitempty" yaml:"retries,omitempty"` - RetryTimeout int `json:"retry_timeout,omitempty" yaml:"retry_timeout,omitempty"` - Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` - TLS *ClientTLS `json:"tls,omitempty" yaml:"tls,omitempty"` - KeepalivePool *KeepalivePool `json:"keepalive_pool,omitempty" yaml:"keepalive_pool,omitempty"` - PassHost string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` - UpstreamHost string `json:"upstream_host,omitempty" yaml:"upstream_host,omitempty"` - - // for Service Discovery - ServiceName string `json:"service_name,omitempty" yaml:"service_name,omitempty"` - DiscoveryType string `json:"discovery_type,omitempty" yaml:"discovery_type,omitempty"` - DiscoveryArgs map[string]string `json:"discovery_args,omitempty" yaml:"discovery_args,omitempty"` -} - -func (u *Upstream) GetLabels() Labels { - return u.Labels -} - -func (u *Upstream) SetLabel(k, v string) { - if u.Labels == nil { - u.Labels = map[string]string{} - } - u.Labels[k] = v -} - -func (u *Upstream) UnmarshalJSON(cont []byte) error { - type unmarshalerUpstream Upstream - - var route unmarshalerUpstream - err := json.Unmarshal(cont, &route) - if err != nil { - return err - } - - *u = Upstream(route) - - SetUpstreamDefaultValues(u) - return nil -} - -type KeepalivePool struct { - Size *int `json:"size,omitempty" yaml:"size,omitempty"` - IdleTimeout *int `json:"idle_timeout,omitempty" yaml:"idle_timeout,omitempty"` - Requests *int `json:"requests,omitempty" yaml:"requests,omitempty"` -} - -// UpstreamNode is the node in upstream -type UpstreamNode struct { - Host string `json:"host" yaml:"host"` - Port int `json:"port" yaml:"port"` - Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` - Priority *int `json:"priority,omitempty" yaml:"priority,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty"` -} - -// UpstreamNodes is the upstream node list. -type UpstreamNodes []UpstreamNode - -// UnmarshalJSON implements json.Unmarshaler interface. -// lua-cjson doesn't distinguish empty array and table, -// and by default empty array will be encoded as '{}'. -// We have to maintain the compatibility. -func (n *UpstreamNodes) UnmarshalJSON(p []byte) error { - var data []UpstreamNode - if p[0] == '{' { - value := map[string]float64{} - if err := json.Unmarshal(p, &value); err != nil { - return err - } - for k, v := range value { - node, err := mapKV2Node(k, v) - if err != nil { - return err - } - data = append(data, *node) - } - *n = data - return nil - } - if err := json.Unmarshal(p, &data); err != nil { - return err - } - *n = data - return nil -} - -// UpstreamHealthCheck defines the active and/or passive health check for an Upstream, -// with the upstream health check feature, pods can be kicked out or joined in quickly, -// if the feedback of Kubernetes liveness/readiness probe is long. -type UpstreamHealthCheck struct { - Active *UpstreamActiveHealthCheck `json:"active" yaml:"active"` - Passive *UpstreamPassiveHealthCheck `json:"passive,omitempty" yaml:"passive,omitempty"` -} - -// UpstreamActiveHealthCheck defines the active kind of upstream health check. -type UpstreamActiveHealthCheck struct { - Type string `json:"type,omitempty" yaml:"type,omitempty"` - Timeout *int `json:"timeout,omitempty" yaml:"timeout,omitempty"` - Concurrency *int `json:"concurrency,omitempty" yaml:"concurrency,omitempty"` - Host string `json:"host,omitempty" yaml:"host,omitempty"` - Port int32 `json:"port,omitempty" yaml:"port,omitempty"` - HTTPPath string `json:"http_path,omitempty" yaml:"http_path,omitempty"` - HTTPSVerifyCert *bool `json:"https_verify_certificate,omitempty" yaml:"https_verify_certificate,omitempty"` - HTTPRequestHeaders []string `json:"req_headers,omitempty" yaml:"req_headers,omitempty"` - Healthy UpstreamActiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"` - Unhealthy UpstreamActiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"` -} - -// UpstreamPassiveHealthCheck defines the passive kind of upstream health check. -type UpstreamPassiveHealthCheck struct { - Type string `json:"type,omitempty" yaml:"type,omitempty"` - Healthy UpstreamPassiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"` - Unhealthy UpstreamPassiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"` -} - -// UpstreamActiveHealthCheckHealthy defines the conditions to judge whether -// an upstream node is healthy with the active manner. -type UpstreamActiveHealthCheckHealthy struct { - UpstreamPassiveHealthCheckHealthy `json:",inline" yaml:",inline"` - - Interval *int `json:"interval,omitempty" yaml:"interval,omitempty"` -} - -// UpstreamPassiveHealthCheckHealthy defines the conditions to judge whether -// an upstream node is healthy with the passive manner. -type UpstreamPassiveHealthCheckHealthy struct { - HTTPStatuses []int `json:"http_statuses,omitempty" yaml:"http_statuses,omitempty"` - Successes *int `json:"successes,omitempty" yaml:"successes,omitempty"` -} - -// UpstreamActiveHealthCheckUnhealthy defines the conditions to judge whether -// an upstream node is unhealthy with the active manager. -type UpstreamActiveHealthCheckUnhealthy struct { - UpstreamPassiveHealthCheckUnhealthy `json:",inline" yaml:",inline"` - - Interval *int `json:"interval,omitempty" yaml:"interval,omitempty"` -} - -// UpstreamPassiveHealthCheckUnhealthy defines the conditions to judge whether -// an upstream node is unhealthy with the passive manager. -type UpstreamPassiveHealthCheckUnhealthy struct { - HTTPStatuses []int `json:"http_statuses,omitempty" yaml:"http_statuses,omitempty"` - HTTPFailures *int `json:"http_failures,omitempty" yaml:"http_failures,omitempty"` - TCPFailures *int `json:"tcp_failures,omitempty" yaml:"tcp_failures,omitempty"` - Timeouts *int `json:"timeouts,omitempty" yaml:"timeouts,omitempty"` -} - -// ClientTLS is tls cert and key use in mTLS -type ClientTLS struct { - Cert string `json:"client_cert,omitempty" yaml:"client_cert,omitempty"` - Key string `json:"client_key,omitempty" yaml:"client_key,omitempty"` - // ClientCertID is the referenced SSL id, can't be used with client_cert and client_key - ClientCertID string `json:"client_cert_id,omitempty"` - - // Verify Turn on server certificate verification, currently only kafka upstream is supported - Verify *bool `json:"verify,omitempty" yaml:"verify,omitempty"` -} - -// UpstreamTimeout represents the timeout settings on Upstream. -type UpstreamTimeout struct { - // Connect is the connect timeout - Connect int `json:"connect" yaml:"connect"` - // Send is the send timeout - Send int `json:"send" yaml:"send"` - // Read is the read timeout - Read int `json:"read" yaml:"read"` -} - -func mapKV2Node(key string, val float64) (*UpstreamNode, error) { - hp := strings.Split(key, ":") - host := hp[0] - // according to APISIX upstream nodes policy, port is required - port := "80" - - if len(hp) > 2 { - return nil, errors.New("invalid upstream node") - } else if len(hp) == 2 { - port = hp[1] - } - - portInt, err := strconv.Atoi(port) - if err != nil { - return nil, fmt.Errorf("parse port to int fail: %s", err.Error()) - } - - node := &UpstreamNode{ - Host: host, - Port: portInt, - Weight: int(val), - } - - return node, nil -} - -type Plugin map[string]interface{} -type Plugins map[string]Plugin - -func (p *Plugins) DeepCopyInto(out *Plugins) { - b, _ := json.Marshal(&p) - _ = json.Unmarshal(b, out) -} - -func (p *Plugins) DeepCopy() *Plugins { - if p == nil { - return nil - } - out := new(Plugins) - p.DeepCopyInto(out) - return out -} - -func (p *Plugins) UnmarshalJSON(cont []byte) error { - var plugins map[string]Plugin - err := json.Unmarshal(cont, &plugins) - if err != nil { - return err - } - - if p == nil || *p == nil { - *p = make(Plugins) - } - for name, config := range plugins { - (*p)[name] = GetPluginDefaultValues(name, config) - } - - return nil -} - -// StringOrSlice represents a string or a string slice. -// TODO Do not use interface{} to avoid the reflection overheads. -type StringOrSlice struct { - StrVal string `json:"-"` - SliceVal []string `json:"-"` -} - -func (s *StringOrSlice) MarshalJSON() ([]byte, error) { - var ( - p []byte - err error - ) - if s.SliceVal != nil { - p, err = json.Marshal(s.SliceVal) - } else { - p, err = json.Marshal(s.StrVal) - } - return p, err -} - -func (s *StringOrSlice) UnmarshalJSON(p []byte) error { - var err error - - if len(p) == 0 { - return errors.New("empty object") - } - if p[0] == '[' { - err = json.Unmarshal(p, &s.SliceVal) - } else { - err = json.Unmarshal(p, &s.StrVal) - } - return err -} - -// Consumer represents the consumer object in APISIX. -type Consumer struct { - Username string `json:"username" yaml:"username"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` - GroupID string `json:"group_id,omitempty" yaml:"group_id,omitempty"` -} - -func (c *Consumer) GetLabels() Labels { - return c.Labels -} - -func (c *Consumer) SetLabel(k, v string) { - if c.Labels == nil { - c.Labels = map[string]string{} - } - c.Labels[k] = v -} - -// SSL represents the ssl object in APISIX. -type SSL struct { - ID string `json:"id" yaml:"id"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - Type string `json:"type,omitempty" yaml:"type,omitempty"` - SNI string `json:"sni" yaml:"sni"` - SNIs []string `json:"snis" yaml:"snis"` - Cert string `json:"cert,omitempty" yaml:"cert,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` - Certs []string `json:"certs,omitempty" yaml:"certs,omitempty"` - Keys []string `json:"keys,omitempty" yaml:"keys,omitempty"` - Client *MutualTLSClientConfig `json:"client,omitempty" yaml:"client,omitempty"` - ExpTime int `json:"exptime,omitempty" yaml:"exptime,omitempty"` - Status *int `json:"status,omitempty" yaml:"status,omitempty"` - SSLProtocols []string `json:"ssl_protocols,omitempty" yaml:"ssl_protocols,omitempty"` - ValidityStart int `json:"validity_start,omitempty" yaml:"validity_start,omitempty"` - ValidityEnd int `json:"validity_end,omitempty" yaml:"validity_end,omitempty"` -} - -func (ssl *SSL) GetLabels() Labels { - return ssl.Labels -} - -func (ssl *SSL) SetLabel(k, v string) { - if ssl.Labels == nil { - ssl.Labels = map[string]string{} - } - ssl.Labels[k] = v -} - -func (ssl *SSL) UnmarshalJSON(cont []byte) error { - type unmarshalerSSL SSL - - var route unmarshalerSSL - err := json.Unmarshal(cont, &route) - if err != nil { - return err - } - - *ssl = SSL(route) - - SetSSLDefaultValues(ssl) - return nil -} - -// MutualTLSClientConfig apisix SSL client field -type MutualTLSClientConfig struct { - CA string `json:"ca,omitempty" yaml:"ca,omitempty"` - Depth *int `json:"depth,omitempty" yaml:"depth,omitempty"` - SkipMtlsUriRegex []string `json:"skip_mtls_uri_regex,omitempty" yaml:"skip_mtls_uri_regex,omitempty"` -} - -// GlobalRule represents the global_rule object in APISIX. -type GlobalRule struct { - ID string `json:"id" yaml:"id"` - Plugins Plugins `json:"plugins" yaml:"plugins"` -} - -// PluginConfig apisix plugin object -type PluginConfig struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - - Plugins Plugins `json:"plugins" yaml:"plugins"` -} - -func (p *PluginConfig) GetLabels() Labels { - return p.Labels -} - -func (p *PluginConfig) SetLabel(k, v string) { - if p.Labels == nil { - p.Labels = map[string]string{} - } - p.Labels[k] = v -} - -// ConsumerGroup apisix consumer group object -type ConsumerGroup struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - - Plugins Plugins `json:"plugins" yaml:"plugins"` -} - -func (c *ConsumerGroup) GetLabels() Labels { - return c.Labels -} - -func (c *ConsumerGroup) SetLabel(k, v string) { - if c.Labels == nil { - c.Labels = map[string]string{} - } - c.Labels[k] = v -} - -const ( - UpstreamPassHostPass = "pass" -) - -type PluginMetadata struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Config map[string]interface{} `json:",inline" yaml:",inline"` -} - -func (s *PluginMetadata) MarshalJSON() ([]byte, error) { - if s.Config == nil { - s.Config = make(map[string]interface{}) - } - - s.Config["id"] = s.ID - - p, err := json.Marshal(s.Config) - - delete(s.Config, "id") - - return p, err -} - -func (s *PluginMetadata) UnmarshalJSON(p []byte) error { - var ( - config map[string]interface{} - err error - ) - - err = json.Unmarshal(p, &config) - if err != nil { - return err - } - - id, ok := config["id"] - if ok { - if reflect.TypeOf(id).Kind() != reflect.String { - return errors.New("plugin metadata id is not a string, input: " + string(p)) - } - s.ID = fmt.Sprintf("%v", id) - delete(config, "id") - } - - s.Config = config - - return nil -} - -// StreamRoute represents the stream_route object in APISIX. -type StreamRoute struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Desc string `json:"desc,omitempty" yaml:"desc,omitempty"` - Labels Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - RemoteAddr string `json:"remote_addr,omitempty" yaml:"remote_addr,omitempty"` - ServerAddr string `json:"server_addr,omitempty" yaml:"server_addr,omitempty"` - ServerPort int `json:"server_port,omitempty" yaml:"server_port,omitempty"` - SNI string `json:"sni,omitempty" yaml:"sni,omitempty"` - Upstream *Upstream `json:"upstream,omitempty" yaml:"upstream,omitempty"` - UpstreamID string `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"` - ServiceID string `json:"service_id,omitempty" yaml:"service_id,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` - // Protocol -} - -func (s *StreamRoute) GetLabels() Labels { - return s.Labels -} - -func (s *StreamRoute) SetLabel(k, v string) { - if s.Labels == nil { - s.Labels = map[string]string{} - } - s.Labels[k] = v -} - -func (s *StreamRoute) UnmarshalJSON(cont []byte) error { - type unmarshalerStreamRoute StreamRoute - - var route unmarshalerStreamRoute - err := json.Unmarshal(cont, &route) - if err != nil { - return err - } - - *s = StreamRoute(route) - - SetStreamRouteDefaultValues(s) - return nil -} diff --git a/pkg/api/apisix/types/types_test.go b/pkg/api/apisix/types/types_test.go deleted file mode 100644 index a78210a3..00000000 --- a/pkg/api/apisix/types/types_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package types - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPluginMetadataMarshal(t *testing.T) { - metadata := &PluginMetadata{ - ID: "http-logger", - Config: map[string]interface{}{ - "log_format": map[string]interface{}{ - "host": "$host", - "@timestamp": "$time_iso8601", - "client_ip": "$remote_addr", - }, - }, - } - - out, err := json.Marshal(metadata) - - assert.Nil(t, err) - assert.Equal(t, `{"id":"http-logger","log_format":{"@timestamp":"$time_iso8601","client_ip":"$remote_addr","host":"$host"}}`, string(out)) - - var unmarshalled PluginMetadata - err = json.Unmarshal(out, &unmarshalled) - assert.Nil(t, err) - - assert.Equal(t, metadata.ID, unmarshalled.ID) - expectedConf := metadata.Config["log_format"].(map[string]interface{}) - unmarshalledConf := unmarshalled.Config["log_format"].(map[string]interface{}) - assert.Equal(t, expectedConf["host"], unmarshalledConf["host"]) - assert.Equal(t, expectedConf["@timestamp"], unmarshalledConf["@timestamp"]) - assert.Equal(t, expectedConf["client_ip"], unmarshalledConf["client_ip"]) -} diff --git a/pkg/api/apisix/upstream.go b/pkg/api/apisix/upstream.go deleted file mode 100644 index 5d56b8bf..00000000 --- a/pkg/api/apisix/upstream.go +++ /dev/null @@ -1,26 +0,0 @@ -package apisix - -import ( - "context" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -type upstreamClient struct { - *resourceClient[types.Upstream] -} - -func newUpstream(c *Client) Upstream { - cli := newResourceClient[types.Upstream](c, "upstreams") - return &upstreamClient{ - resourceClient: cli, - } -} - -func (u *upstreamClient) Create(ctx context.Context, obj *types.Upstream) (*types.Upstream, error) { - return u.resourceClient.Create(ctx, obj.ID, obj) -} - -func (u *upstreamClient) Update(ctx context.Context, obj *types.Upstream) (*types.Upstream, error) { - return u.resourceClient.Update(ctx, obj.ID, obj) -} diff --git a/pkg/common/file.go b/pkg/common/file.go deleted file mode 100644 index 4d148b79..00000000 --- a/pkg/common/file.go +++ /dev/null @@ -1,200 +0,0 @@ -package common - -import ( - "bufio" - "context" - "io" - "os" - - "github.com/fatih/color" - "sigs.k8s.io/yaml" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" -) - -func NormalizeConfiguration(content *types.Configuration) { - for _, route := range content.Routes { - if route.ID == "" { - route.ID = route.Name - } - if route.Name == "" { - route.Name = route.ID - } - } - - for _, service := range content.Services { - if service.ID == "" { - service.ID = service.Name - } - if service.Upstream != nil && service.Upstream.ID == "" { - service.Upstream.ID = service.Upstream.Name - } - } - - if content.Meta != nil && len(content.Meta.Labels) > 0 { - labels := content.Meta.Labels - - for _, route := range content.Routes { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.Services { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.Consumers { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.SSLs { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.PluginConfigs { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.ConsumerGroups { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.StreamRoutes { - for k, v := range labels { - route.SetLabel(k, v) - } - } - for _, route := range content.Upstreams { - for k, v := range labels { - route.SetLabel(k, v) - } - } - } -} - -func GetContentFromFile(filename string) (*types.Configuration, error) { - var content types.Configuration - - f, err := os.Open(filename) - if err != nil { - color.Red("Open file %s failed: %s", filename, err) - return nil, err - } - defer f.Close() - - reader := bufio.NewReader(f) - fileContent, err := io.ReadAll(reader) - if err != nil { - color.Red("Read file %s failed: %s", filename, err) - return nil, err - } - - // I should use YAML unmarshal the fileContent to a Configuration struct - err = yaml.Unmarshal(fileContent, &content) - if err != nil { - color.Red("Unmarshal file %s failed: %s", filename, err) - return nil, err - } - - NormalizeConfiguration(&content) - - return &content, nil -} - -func GetContentFromRemote(cluster apisix.Cluster) (*types.Configuration, error) { - svcs, err := cluster.Service().List(context.Background()) - if err != nil { - return nil, err - } - - routes, err := cluster.Route().List(context.Background()) - if err != nil { - return nil, err - } - - consumers, err := cluster.Consumer().List(context.Background()) - if err != nil { - return nil, err - } - - ssls, err := cluster.SSL().List(context.Background()) - if err != nil { - return nil, err - } - - globalRules, err := cluster.GlobalRule().List(context.Background()) - if err != nil { - return nil, err - } - - pluginConfigs, err := cluster.PluginConfig().List(context.Background()) - if err != nil { - return nil, err - } - - consumerGroups, err := cluster.ConsumerGroup().List(context.Background()) - if err != nil { - return nil, err - } - - pluginMetadatas, err := cluster.PluginMetadata().List(context.Background()) - if err != nil { - return nil, err - } - - streamRoutes, err := cluster.StreamRoute().List(context.Background()) - if err != nil { - return nil, err - } - - upstream, err := cluster.Upstream().List(context.Background()) - if err != nil { - return nil, err - } - - return &types.Configuration{ - Routes: routes, - Services: svcs, - Consumers: consumers, - SSLs: ssls, - GlobalRules: globalRules, - PluginConfigs: pluginConfigs, - ConsumerGroups: consumerGroups, - PluginMetadatas: pluginMetadatas, - StreamRoutes: streamRoutes, - Upstreams: upstream, - }, nil -} - -func SaveAPISIXConfiguration(path string, conf *types.Configuration) error { - f, err := os.Create(path) - if err != nil { - return err - } - - defer f.Close() - - data, err := yaml.Marshal(conf) - if err != nil { - color.Red(err.Error()) - return err - } - - _, err = f.Write(data) - if err != nil { - return err - } - - err = f.Sync() - if err != nil { - return err - } - - return nil -} diff --git a/pkg/common/file_test.go b/pkg/common/file_test.go deleted file mode 100644 index d10bfbb9..00000000 --- a/pkg/common/file_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// BEGIN: xz3c4v5b6n7m -package common - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGetContentFromFile(t *testing.T) { - // create a temporary file - tmpfile, err := os.CreateTemp("", "example") - if err != nil { - t.Fatal(err) - } - defer os.Remove(tmpfile.Name()) // clean up - - // write some content to the file - expectedContent := `name: test -version: "1.0.0" -services: -- name: svc1 - hosts: - - svc1.example.com -` - if _, err := tmpfile.Write([]byte(expectedContent)); err != nil { - t.Fatal(err) - } - - // read the content from the file using GetContentFromFile - actualContent, err := GetContentFromFile(tmpfile.Name()) - if err != nil { - t.Fatal(err) - } - - // assert that the content matches the expected content - assert.Equal(t, "test", actualContent.Name) - assert.Equal(t, "1.0.0", actualContent.Version) -} - -// END: xz3c4v5b6n7m diff --git a/pkg/common/utils.go b/pkg/common/utils.go deleted file mode 100644 index 629c48bd..00000000 --- a/pkg/common/utils.go +++ /dev/null @@ -1,22 +0,0 @@ -package common - -import ( - "fmt" - "hash/crc32" - "unsafe" -) - -// string2Byte converts string to a byte slice without memory allocation. -func string2Byte(s string) []byte { - return unsafe.Slice(unsafe.StringData(s), len(s)) -} - -func GenID(raw string) string { - if raw == "" { - return "" - } - p := string2Byte(raw) - - res := crc32.ChecksumIEEE(p) - return fmt.Sprintf("%x", res) -} diff --git a/pkg/config/types.go b/pkg/config/types.go deleted file mode 100644 index 1da4a20c..00000000 --- a/pkg/config/types.go +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright © 2023 API7.ai -*/ -package config - -type ClientConfig struct { - Server string - Token string - - CAPath string - Certificate string - CertificateKey string - Insecure bool -} diff --git a/pkg/data/events.go b/pkg/data/events.go deleted file mode 100644 index 01990554..00000000 --- a/pkg/data/events.go +++ /dev/null @@ -1,183 +0,0 @@ -package data - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/hexops/gotextdiff" - "github.com/hexops/gotextdiff/myers" - "github.com/hexops/gotextdiff/span" - "github.com/pkg/errors" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" -) - -// ResourceType is the type of resource -type ResourceType string - -var ( - // ServiceResourceType is the resource type of service - ServiceResourceType ResourceType = "service" - // RouteResourceType is the resource type of route - RouteResourceType ResourceType = "route" - // ConsumerResourceType is the resource type of consumer - ConsumerResourceType ResourceType = "consumer" - // SSLResourceType is the resource type of SSL - SSLResourceType ResourceType = "ssl" - // GlobalRuleResourceType is the resource type of global rule - GlobalRuleResourceType ResourceType = "global_rule" - // PluginConfigResourceType is the resource type of plugin config - PluginConfigResourceType ResourceType = "plugin_config" - // ConsumerGroupResourceType is the resource type of consumer group - ConsumerGroupResourceType ResourceType = "consumer_group" - // PluginMetadataResourceType is the resource type of consumer group - PluginMetadataResourceType ResourceType = "plugin_metadata" - // StreamRouteResourceType is the resource type of stream route - StreamRouteResourceType ResourceType = "stream_route" - // UpstreamResourceType is the resource type of upstream - UpstreamResourceType ResourceType = "upstream" -) - -const ( - // CreateOption is the option of create - CreateOption = iota - // DeleteOption is the option of delete - DeleteOption - // UpdateOption is the option of update - UpdateOption -) - -// Event is the event of adc -type Event struct { - ResourceType ResourceType `json:"resource_type"` - Option int `json:"option"` - OldValue interface{} `json:"old_value"` - Value interface{} `json:"value"` -} - -// Output returns the output of event, -// if the event is create, it will return the message of creating resource. -// if the event is update, it will return the diff of old value and new value. -// if the event is delete, it will return the message of deleting resource. -func (e *Event) Output(diffOnly bool) (string, error) { - var output string - switch e.Option { - case CreateOption: - if diffOnly { - output = fmt.Sprintf("+++ %s: \"%s\"", e.ResourceType, apisix.GetResourceUniqueKey(e.Value)) - } else { - output = fmt.Sprintf("creating %s: \"%s\"", e.ResourceType, apisix.GetResourceUniqueKey(e.Value)) - } - case DeleteOption: - if diffOnly { - output = fmt.Sprintf("--- %s: \"%s\"", e.ResourceType, apisix.GetResourceUniqueKey(e.OldValue)) - } else { - output = fmt.Sprintf("deleting %s: \"%s\"", e.ResourceType, apisix.GetResourceUniqueKey(e.OldValue)) - } - case UpdateOption: - remote, err := json.MarshalIndent(e.OldValue, "", "\t") - if err != nil { - return "", err - } - remote = append(remote, '\n') - - local, err := json.MarshalIndent(e.Value, "", "\t") - if err != nil { - return "", err - } - local = append(local, '\n') - - edits := myers.ComputeEdits(span.URIFromPath("remote"), string(remote), string(local)) - diff := fmt.Sprint(gotextdiff.ToUnified("remote", "local", string(remote), edits)) - if diffOnly { - output = fmt.Sprintf("update %s: \"%s\"\n%s", e.ResourceType, apisix.GetResourceUniqueKey(e.Value), diff) - } else { - output = fmt.Sprintf("updating %s: \"%s\"\n%s", e.ResourceType, apisix.GetResourceUniqueKey(e.Value), diff) - } - } - - return output, nil -} - -func apply[T any](client apisix.ResourceClient[T], event *Event) error { - var err error - switch event.Option { - case CreateOption: - _, err = client.Create(context.Background(), event.Value.(*T)) - case DeleteOption: - err = client.Delete(context.Background(), apisix.GetResourceUniqueKey(event.OldValue)) - case UpdateOption: - _, err = client.Update(context.Background(), event.Value.(*T)) - } - - return errors.Wrap(err, "failed to apply "+string(event.ResourceType)) -} - -func applyService(cluster apisix.Cluster, event *Event) error { - return apply[types.Service](cluster.Service(), event) -} - -func applyRoute(cluster apisix.Cluster, event *Event) error { - return apply[types.Route](cluster.Route(), event) -} - -func applyConsumer(cluster apisix.Cluster, event *Event) error { - return apply[types.Consumer](cluster.Consumer(), event) -} - -func applySSL(cluster apisix.Cluster, event *Event) error { - return apply[types.SSL](cluster.SSL(), event) -} - -func applyGlobalRule(cluster apisix.Cluster, event *Event) error { - return apply[types.GlobalRule](cluster.GlobalRule(), event) -} - -func applyPluginConfig(cluster apisix.Cluster, event *Event) error { - return apply[types.PluginConfig](cluster.PluginConfig(), event) -} - -func applyConsumerGroup(cluster apisix.Cluster, event *Event) error { - return apply[types.ConsumerGroup](cluster.ConsumerGroup(), event) -} - -func applyPluginMetadata(cluster apisix.Cluster, event *Event) error { - return apply[types.PluginMetadata](cluster.PluginMetadata(), event) -} - -func applyStreamRoute(cluster apisix.Cluster, event *Event) error { - return apply[types.StreamRoute](cluster.StreamRoute(), event) -} - -func applyUpstream(cluster apisix.Cluster, event *Event) error { - return apply[types.Upstream](cluster.Upstream(), event) -} - -func (e *Event) Apply(cluster apisix.Cluster) error { - switch e.ResourceType { - case ServiceResourceType: - return applyService(cluster, e) - case RouteResourceType: - return applyRoute(cluster, e) - case ConsumerResourceType: - return applyConsumer(cluster, e) - case SSLResourceType: - return applySSL(cluster, e) - case GlobalRuleResourceType: - return applyGlobalRule(cluster, e) - case PluginConfigResourceType: - return applyPluginConfig(cluster, e) - case ConsumerGroupResourceType: - return applyConsumerGroup(cluster, e) - case PluginMetadataResourceType: - return applyPluginMetadata(cluster, e) - case StreamRouteResourceType: - return applyStreamRoute(cluster, e) - case UpstreamResourceType: - return applyUpstream(cluster, e) - } - - return nil -} diff --git a/pkg/data/events_test.go b/pkg/data/events_test.go deleted file mode 100644 index 2958a71f..00000000 --- a/pkg/data/events_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package data - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/api7/adc/pkg/api/apisix/types" -) - -var ( - svc = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "svc.example.com", - }, - Labels: map[string]string{ - "label1": "v1", - "label2": "v2", - }, - Upstream: &types.Upstream{ - Name: "upstream1", - Nodes: []types.UpstreamNode{ - { - Host: "httpbin.org", - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Labels: map[string]string{ - "label1": "v1", - "label2": "v2", - }, - Methods: []string{http.MethodGet}, - Uris: []string{"/get"}, - ServiceID: "svc", - } -) - -func TestEventOutput(t *testing.T) { - // Test case 1: delete events - event := &Event{ - ResourceType: ServiceResourceType, - Option: DeleteOption, - OldValue: svc, - } - output, err := event.Output(false) - assert.Nil(t, err, "should not return error") - assert.Equal(t, "deleting service: \"svc\"", output) - - // Test case 2: create events - event = &Event{ - ResourceType: ServiceResourceType, - Option: CreateOption, - Value: svc, - } - output, err = event.Output(false) - assert.Nil(t, err, "should not return error") - assert.Equal(t, "creating service: \"svc\"", output) - - // Test case 3: update events - route1 := *route - route1.Description = "route1" - event = &Event{ - ResourceType: RouteResourceType, - Option: UpdateOption, - OldValue: route, - Value: route1, - } - output, err = event.Output(false) - assert.Nil(t, err, "should not return error") - assert.Contains(t, output, "updating route: \"route\"", "should contain the route name") - assert.Contains(t, output, "+\t\"desc\": \"route1\"", "should contain the changes") -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..8e56f57d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,10616 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@readme/openapi-parser': + specifier: ^2.5.1 + version: 2.5.1(openapi-types@12.1.3) + axios: + specifier: ^1.7.2 + version: 1.7.2 + chalk: + specifier: ^4.1.2 + version: 4.1.2 + commander: + specifier: ^12.1.0 + version: 12.1.0 + deep-diff: + specifier: ^1.0.2 + version: 1.0.2 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + immer: + specifier: ^10.1.1 + version: 10.1.1 + json-schema: + specifier: ^0.4.0 + version: 0.4.0 + listr2: + specifier: ^8.2.1 + version: 8.2.1 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + parse-duration: + specifier: ^1.1.0 + version: 1.1.0 + pluralize: + specifier: ^8.0.0 + version: 8.0.0 + reflect-metadata: + specifier: ^0.1.14 + version: 0.1.14 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + signale: + specifier: ^1.4.0 + version: 1.4.0 + slugify: + specifier: ^1.6.6 + version: 1.6.6 + tslib: + specifier: ^2.6.2 + version: 2.6.2 + winston: + specifier: ^3.13.0 + version: 3.13.0 + yaml: + specifier: ^2.4.2 + version: 2.4.2 + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@nx/esbuild': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/eslint': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/eslint-plugin': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/jest': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/js': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/node': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/webpack': + specifier: 18.3.4 + version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/workspace': + specifier: 18.3.4 + version: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + '@pmmmwh/react-refresh-webpack-plugin': + specifier: ^0.5.13 + version: 0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + '@svgr/webpack': + specifier: ^8.1.0 + version: 8.1.0(typescript@5.4.5) + '@swc-node/register': + specifier: ~1.8.0 + version: 1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5) + '@swc/core': + specifier: ~1.3.107 + version: 1.3.107(@swc/helpers@0.5.11) + '@swc/helpers': + specifier: ~0.5.11 + version: 0.5.11 + '@trivago/prettier-plugin-sort-imports': + specifier: ^4.3.0 + version: 4.3.0(prettier@3.2.5) + '@types/deep-diff': + specifier: ^1.0.5 + version: 1.0.5 + '@types/jest': + specifier: ^29.5.12 + version: 29.5.12 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 + '@types/json-schema': + specifier: ^7.0.15 + version: 7.0.15 + '@types/lodash': + specifier: ^4.17.4 + version: 4.17.4 + '@types/node': + specifier: 18.16.9 + version: 18.16.9 + '@types/pluralize': + specifier: ^0.0.33 + version: 0.0.33 + '@types/qs': + specifier: ^6.9.15 + version: 6.9.15 + '@types/signale': + specifier: ^1.4.7 + version: 1.4.7 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint@8.48.0)(typescript@5.4.5) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.48.0)(typescript@5.4.5) + esbuild: + specifier: ^0.19.12 + version: 0.19.12 + eslint: + specifier: ~8.48.0 + version: 8.48.0 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.48.0) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-environment-node: + specifier: ^29.7.0 + version: 29.7.0 + nx: + specifier: 18.3.4 + version: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + openapi-types: + specifier: ^12.1.3 + version: 12.1.3 + prettier: + specifier: ^3.2.5 + version: 3.2.5 + qs: + specifier: ^6.12.1 + version: 6.12.1 + ts-jest: + specifier: ^29.1.3 + version: 29.1.3(@babel/core@7.24.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.6))(esbuild@0.19.12)(jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)))(typescript@5.4.5) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5) + typescript: + specifier: ^5.4.5 + version: 5.4.5 + url-loader: + specifier: ^4.1.1 + version: 4.1.1(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + webpack: + specifier: ^5.91.0 + version: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + zod-to-json-schema: + specifier: ^3.23.0 + version: 3.23.0(zod@3.23.8) + +packages: + + '@adobe/css-tools@4.3.3': + resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@babel/code-frame@7.24.6': + resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.6': + resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.6': + resolution: {integrity: sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.17.7': + resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.24.6': + resolution: {integrity: sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.24.6': + resolution: {integrity: sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-builder-binary-assignment-operator-visitor@7.24.6': + resolution: {integrity: sha512-+wnfqc5uHiMYtvRX7qu80Toef8BXeh4HHR1SPeonGb1SKPniNEd4a/nlaJJMv/OIEYvIVavvo0yR7u10Gqz0Iw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.6': + resolution: {integrity: sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.24.6': + resolution: {integrity: sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.24.6': + resolution: {integrity: sha512-C875lFBIWWwyv6MHZUG9HmRrlTDgOsLWZfYR0nW69gaKJNe0/Mpxx5r0EID2ZdHQkdUmQo2t0uNckTL08/1BgA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.2': + resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-environment-visitor@7.24.6': + resolution: {integrity: sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.6': + resolution: {integrity: sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.6': + resolution: {integrity: sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.24.6': + resolution: {integrity: sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.6': + resolution: {integrity: sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.6': + resolution: {integrity: sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.24.6': + resolution: {integrity: sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.24.6': + resolution: {integrity: sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.24.6': + resolution: {integrity: sha512-1Qursq9ArRZPAMOZf/nuzVW8HgJLkTB9y9LfP4lW2MVp4e9WkLJDovfKBxoDcCk6VuzIxyqWHyBoaCtSRP10yg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.24.6': + resolution: {integrity: sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.6': + resolution: {integrity: sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-skip-transparent-expression-wrappers@7.24.6': + resolution: {integrity: sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.6': + resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.6': + resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.6': + resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.6': + resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.24.6': + resolution: {integrity: sha512-f1JLrlw/jbiNfxvdrfBgio/gRBk3yTAEJWirpAkiJG2Hb22E7cEYKHWo0dFPTv/niPovzIdPdEDetrv6tC6gPQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.6': + resolution: {integrity: sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.6': + resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.6': + resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6': + resolution: {integrity: sha512-bYndrJ6Ph6Ar+GaB5VAc0JPoP80bQCm4qon6JEzXfRl5QZyQ8Ur1K6k7htxWmPA5z+k7JQvaMUrtXlqclWYzKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6': + resolution: {integrity: sha512-iVuhb6poq5ikqRq2XWU6OQ+R5o9wF+r/or9CeUyovgptz0UlnK4/seOQ1Istu/XybYjAhQv1FRSSfHHufIku5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6': + resolution: {integrity: sha512-c8TER5xMDYzzFcGqOEp9l4hvB7dcbhcGjcLVwxWfe4P5DOafdwjsBJZKsmv+o3aXh7NhopvayQIovHrh2zSRUQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6': + resolution: {integrity: sha512-z8zEjYmwBUHN/pCF3NuWBhHQjJCrd33qAi8MgANfMrAvn72k2cImT8VjK9LJFu4ysOLJqhfkYYb3MvwANRUNZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-decorators@7.24.6': + resolution: {integrity: sha512-8DjR0/DzlBhz2SVi9a19/N2U5+C3y3rseXuyoKL9SP8vnbewscj1eHZtL6kpEn4UCuUmqEo0mvqyDYRFoN2gpA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.24.6': + resolution: {integrity: sha512-gInH8LEqBp+wkwTVihCd/qf+4s28g81FZyvlIbAurHk9eSiItEKG7E0uNK2UdpgsD79aJVAW3R3c85h0YJ0jsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-export-namespace-from@7.8.3': + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.24.6': + resolution: {integrity: sha512-BE6o2BogJKJImTmGpkmOic4V0hlRRxVtzqxiSPa8TIFxyhi4EFjHm08nq1M4STK4RytuLMgnSz0/wfflvGFNOg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.24.6': + resolution: {integrity: sha512-D+CfsVZousPXIdudSII7RGy52+dYRtbyKAZcvtQKq/NpsivyMVduepzcLqG5pMBugtMdedxdC8Ramdpcne9ZWQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.24.6': + resolution: {integrity: sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.24.6': + resolution: {integrity: sha512-TzCtxGgVTEJWWwcYwQhCIQ6WaKlo80/B+Onsk4RRCcYqpYGFcG9etPW94VToGte5AAcxRrhjPUFvUS3Y2qKi4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.24.6': + resolution: {integrity: sha512-jSSSDt4ZidNMggcLx8SaKsbGNEfIl0PHx/4mFEulorE7bpYLbN0d3pDW3eJ7Y5Z3yPhy3L3NaPCYyTUY7TuugQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.24.6': + resolution: {integrity: sha512-VEP2o4iR2DqQU6KPgizTW2mnMx6BG5b5O9iQdrW9HesLkv8GIA8x2daXBQxw1MrsIkFQGA/iJ204CKoQ8UcnAA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.24.6': + resolution: {integrity: sha512-NTBA2SioI3OsHeIn6sQmhvXleSl9T70YY/hostQLveWs0ic+qvbA3fa0kwAwQ0OA/XGaAerNZRQGJyRfhbJK4g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.24.6': + resolution: {integrity: sha512-XNW7jolYHW9CwORrZgA/97tL/k05qe/HL0z/qqJq1mdWhwwCM6D4BJBV7wAz9HgFziN5dTOG31znkVIzwxv+vw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.24.6': + resolution: {integrity: sha512-S/t1Xh4ehW7sGA7c1j/hiOBLnEYCp/c2sEG4ZkL8kI1xX9tW2pqJTCHKtdhe/jHKt8nG0pFCrDHUXd4DvjHS9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.24.6': + resolution: {integrity: sha512-j6dZ0Z2Z2slWLR3kt9aOmSIrBvnntWjMDN/TVcMPxhXMLmJVqX605CBRlcGI4b32GMbfifTEsdEjGjiE+j/c3A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.24.6': + resolution: {integrity: sha512-1QSRfoPI9RoLRa8Mnakc6v3e0gJxiZQTYrMfLn+mD0sz5+ndSzwymp2hDcYJTyT0MOn0yuWzj8phlIvO72gTHA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.24.6': + resolution: {integrity: sha512-+fN+NO2gh8JtRmDSOB6gaCVo36ha8kfCW1nMq2Gc0DABln0VcHN4PrALDvF5/diLzIRKptC7z/d7Lp64zk92Fg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.24.6': + resolution: {integrity: sha512-cRzPobcfRP0ZtuIEkA8QzghoUpSB3X3qSH5W2+FzG+VjWbJXExtx0nbRqwumdBN1x/ot2SlTNQLfBCnPdzp6kg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.24.6': + resolution: {integrity: sha512-YLW6AE5LQpk5npNXL7i/O+U9CE4XsBCuRPgyjl1EICZYKmcitV+ayuuUGMJm2lC1WWjXYszeTnIxF/dq/GhIZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.24.6': + resolution: {integrity: sha512-rCXPnSEKvkm/EjzOtLoGvKseK+dS4kZwx1HexO3BtRtgL0fQ34awHn34aeSHuXtZY2F8a1X8xqBBPRtOxDVmcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.24.6': + resolution: {integrity: sha512-/8Odwp/aVkZwPFJMllSbawhDAO3UJi65foB00HYnK/uXvvCPm0TAXSByjz1mpRmp0q6oX2SIxpkUOpPFHk7FLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dynamic-import@7.24.6': + resolution: {integrity: sha512-vpq8SSLRTBLOHUZHSnBqVo0AKX3PBaoPs2vVzYVWslXDTDIpwAcCDtfhUcHSQQoYoUvcFPTdC8TZYXu9ZnLT/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.24.6': + resolution: {integrity: sha512-EemYpHtmz0lHE7hxxxYEuTYOOBZ43WkDgZ4arQ4r+VX9QHuNZC+WH3wUWmRNvR8ECpTRne29aZV6XO22qpOtdA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.24.6': + resolution: {integrity: sha512-inXaTM1SVrIxCkIJ5gqWiozHfFMStuGbGJAxZFBoHcRRdDP0ySLb3jH6JOwmfiinPwyMZqMBX+7NBDCO4z0NSA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.24.6': + resolution: {integrity: sha512-n3Sf72TnqK4nw/jziSqEl1qaWPbCRw2CziHH+jdRYvw4J6yeCzsj4jdw8hIntOEeDGTmHVe2w4MVL44PN0GMzg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.24.6': + resolution: {integrity: sha512-sOajCu6V0P1KPljWHKiDq6ymgqB+vfo3isUS4McqW1DZtvSVU2v/wuMhmRmkg3sFoq6GMaUUf8W4WtoSLkOV/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.24.6': + resolution: {integrity: sha512-Uvgd9p2gUnzYJxVdBLcU0KurF8aVhkmVyMKW4MIY1/BByvs3EBpv45q01o7pRTVmTvtQq5zDlytP3dcUgm7v9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.24.6': + resolution: {integrity: sha512-f2wHfR2HF6yMj+y+/y07+SLqnOSwRp8KYLpQKOzS58XLVlULhXbiYcygfXQxJlMbhII9+yXDwOUFLf60/TL5tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.24.6': + resolution: {integrity: sha512-EKaWvnezBCMkRIHxMJSIIylzhqK09YpiJtDbr2wsXTwnO0TxyjMUkaw4RlFIZMIS0iDj0KyIg7H7XCguHu/YDA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.24.6': + resolution: {integrity: sha512-9g8iV146szUo5GWgXpRbq/GALTnY+WnNuRTuRHWWFfWGbP9ukRL0aO/jpu9dmOPikclkxnNsjY8/gsWl6bmZJQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.24.6': + resolution: {integrity: sha512-eAGogjZgcwqAxhyFgqghvoHRr+EYRQPFjUXrTYKBRb5qPnAVxOOglaxc4/byHqjvq/bqO2F3/CGwTHsgKJYHhQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.24.6': + resolution: {integrity: sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.24.6': + resolution: {integrity: sha512-xg1Z0J5JVYxtpX954XqaaAT6NpAY6LtZXvYFCJmGFJWwtlz2EmJoR8LycFRGNE8dBKizGWkGQZGegtkV8y8s+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.24.6': + resolution: {integrity: sha512-esRCC/KsSEUvrSjv5rFYnjZI6qv4R1e/iHQrqwbZIoRJqk7xCvEUiN7L1XrmW5QSmQe3n1XD88wbgDTWLbVSyg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.24.6': + resolution: {integrity: sha512-6DneiCiu91wm3YiNIGDWZsl6GfTTbspuj/toTEqLh9d4cx50UIzSdg+T96p8DuT7aJOBRhFyaE9ZvTHkXrXr6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.24.6': + resolution: {integrity: sha512-f8liz9JG2Va8A4J5ZBuaSdwfPqN6axfWRK+y66fjKYbwf9VBLuq4WxtinhJhvp1w6lamKUwLG0slK2RxqFgvHA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.24.6': + resolution: {integrity: sha512-+QlAiZBMsBK5NqrBWFXCYeXyiU1y7BQ/OYaiPAcQJMomn5Tyg+r5WuVtyEuvTbpV7L25ZSLfE+2E9ywj4FD48A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.24.6': + resolution: {integrity: sha512-6voawq8T25Jvvnc4/rXcWZQKKxUNZcKMS8ZNrjxQqoRFernJJKjE3s18Qo6VFaatG5aiX5JV1oPD7DbJhn0a4Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.24.6': + resolution: {integrity: sha512-OKmi5wiMoRW5Smttne7BwHM8s/fb5JFs+bVGNSeHWzwZkWXWValR1M30jyXo1s/RaqgwwhEC62u4rFH/FBcBPg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.24.6': + resolution: {integrity: sha512-N/C76ihFKlZgKfdkEYKtaRUtXZAgK7sOY4h2qrbVbVTXPrKGIi8aww5WGe/+Wmg8onn8sr2ut6FXlsbu/j6JHg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.24.6': + resolution: {integrity: sha512-L5pZ+b3O1mSzJ71HmxSCmTVd03VOT2GXOigug6vDYJzE5awLI7P1g0wFcdmGuwSDSrQ0L2rDOe/hHws8J1rv3w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.24.6': + resolution: {integrity: sha512-cHbqF6l1QP11OkYTYQ+hhVx1E017O5ZcSPXk9oODpqhcAD1htsWG2NpHrrhthEO2qZomLK0FXS+u7NfrkF5aOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.24.6': + resolution: {integrity: sha512-ST7guE8vLV+vI70wmAxuZpIKzVjvFX9Qs8bl5w6tN/6gOypPWUmMQL2p7LJz5E63vEGrDhAiYetniJFyBH1RkA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.24.6': + resolution: {integrity: sha512-T9LtDI0BgwXOzyXrvgLTT8DFjCC/XgWLjflczTLXyvxbnSR/gpv0hbmzlHE/kmh9nOvlygbamLKRo6Op4yB6aw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.24.6': + resolution: {integrity: sha512-Qu/ypFxCY5NkAnEhCF86Mvg3NSabKsh/TPpBVswEdkGl7+FbsYHy1ziRqJpwGH4thBdQHh8zx+z7vMYmcJ7iaQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.24.6': + resolution: {integrity: sha512-oARaglxhRsN18OYsnPTpb8TcKQWDYNsPNmTnx5++WOAsUJ0cSC/FZVlIJCKvPbU4yn/UXsS0551CFKJhN0CaMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-constant-elements@7.24.6': + resolution: {integrity: sha512-vQfyXRtG/kNIcTYRd/49uJnwvMig9X3R4XsTVXRml2RFupZFY+2RDuK+/ymb+MfX2WuIHAgUZc2xEvQrnI7QCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.24.6': + resolution: {integrity: sha512-/3iiEEHDsJuj9QU09gbyWGSUxDboFcD7Nj6dnHIlboWSodxXAoaY/zlNMHeYAC0WsERMqgO9a7UaM77CsYgWcg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.24.6': + resolution: {integrity: sha512-F7EsNp5StNDouSSdYyDSxh4J+xvj/JqG+Cb6s2fA+jCyHOzigG5vTwgH8tU2U8Voyiu5zCG9bAK49wTr/wPH0w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.24.6': + resolution: {integrity: sha512-pCtPHhpRZHfwdA5G1Gpk5mIzMA99hv0R8S/Ket50Rw+S+8hkt3wBWqdqHaPw0CuUYxdshUgsPiLQ5fAs4ASMhw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.24.6': + resolution: {integrity: sha512-0HoDQlFJJkXRyV2N+xOpUETbKHcouSwijRQbKWVtxsPoq5bbB30qZag9/pSc5xcWVYjTHlLsBsY+hZDnzQTPNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.24.6': + resolution: {integrity: sha512-SMDxO95I8WXRtXhTAc8t/NFQUT7VYbIWwJCJgEli9ml4MhqUMh4S6hxgH6SmAC3eAQNWCDJFxcFeEt9w2sDdXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-reserved-words@7.24.6': + resolution: {integrity: sha512-DcrgFXRRlK64dGE0ZFBPD5egM2uM8mgfrvTMOSB2yKzOtjpGegVYkzh3s1zZg1bBck3nkXiaOamJUqK3Syk+4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.24.6': + resolution: {integrity: sha512-W3gQydMb0SY99y/2lV0Okx2xg/8KzmZLQsLaiCmwNRl1kKomz14VurEm+2TossUb+sRvBCnGe+wx8KtIgDtBbQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.24.6': + resolution: {integrity: sha512-xnEUvHSMr9eOWS5Al2YPfc32ten7CXdH7Zwyyk7IqITg4nX61oHj+GxpNvl+y5JHjfN3KXE2IV55wAWowBYMVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.24.6': + resolution: {integrity: sha512-h/2j7oIUDjS+ULsIrNZ6/TKG97FgmEk1PXryk/HQq6op4XUUUwif2f69fJrzK0wza2zjCS1xhXmouACaWV5uPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.24.6': + resolution: {integrity: sha512-fN8OcTLfGmYv7FnDrsjodYBo1DhPL3Pze/9mIIE2MGCT1KgADYIOD7rEglpLHZj8PZlC/JFX5WcD+85FLAQusw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.24.6': + resolution: {integrity: sha512-BJbEqJIcKwrqUP+KfUIkxz3q8VzXe2R8Wv8TaNgO1cx+nNavxn/2+H8kp9tgFSOL6wYPPEgFvU6IKS4qoGqhmg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.24.6': + resolution: {integrity: sha512-IshCXQ+G9JIFJI7bUpxTE/oA2lgVLAIK8q1KdJNoPXOpvRaNjMySGuvLfBw/Xi2/1lLo953uE8hyYSDW3TSYig==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.24.6': + resolution: {integrity: sha512-H0i+hDLmaYYSt6KU9cZE0gb3Cbssa/oxWis7PX4ofQzbvsfix9Lbh8SRk7LCPDlLWJHUiFeHU0qRRpF/4Zv7mQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.24.6': + resolution: {integrity: sha512-bKl3xxcPbkQQo5eX9LjjDpU2xYHeEeNQbOhj0iPvetSzA+Tu9q/o5lujF4Sek60CM6MgYvOS/DJuwGbiEYAnLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.24.6': + resolution: {integrity: sha512-8EIgImzVUxy15cZiPii9GvLZwsy7Vxc+8meSlR3cXFmBIl5W5Tn9LGBf7CDKkHj4uVfNXCJB8RsVfnmY61iedA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.24.6': + resolution: {integrity: sha512-pssN6ExsvxaKU638qcWb81RrvvgZom3jDgU/r5xFZ7TONkZGFf4MhI2ltMb8OcQWhHyxgIavEU+hgqtbKOmsPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.24.6': + resolution: {integrity: sha512-quiMsb28oXWIDK0gXLALOJRXLgICLiulqdZGOaPPd0vRT7fQp74NtdADAVu+D8s00C+0Xs0MxVP0VKF/sZEUgw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.24.6': + resolution: {integrity: sha512-CrxEAvN7VxfjOG8JNF2Y/eMqMJbZPZ185amwGUBp8D9USK90xQmv7dLdFSa+VbD7fdIqcy/Mfv7WtzG8+/qxKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.24.6': + resolution: {integrity: sha512-8mpzh1bWvmINmwM3xpz6ahu57mNaWavMm+wBNjQ4AFu1nghKBiIRET7l/Wmj4drXany/BBGjJZngICcD98F1iw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.24.6': + resolution: {integrity: sha512-U10aHPDnokCFRXgyT/MaIRTivUu2K/mu0vJlwRS9LxJmJet+PFQNKpggPyFCUtC6zWSBPjvxjnpNkAn3Uw2m5w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/regjsgen@0.8.0': + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + + '@babel/runtime@7.24.6': + resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.24.6': + resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.23.2': + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.24.6': + resolution: {integrity: sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.17.0': + resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.6': + resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.10.0': + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.48.0': + resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/momoa@2.0.4': + resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} + engines: {node: '>=10.10.0'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nrwl/devkit@18.3.4': + resolution: {integrity: sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==} + + '@nrwl/esbuild@18.3.4': + resolution: {integrity: sha512-XVZdgNn4im0uRvPmQj8KRoS1KjCu0oiJ3AGDIAzlOAvy1gZZQvHPqzbhduIL6v38izMTQNKseXk9PCaY4B8YMQ==} + + '@nrwl/eslint-plugin-nx@18.3.4': + resolution: {integrity: sha512-IiBAxWgOZC4dIqnirpoRJ0YvDeR9HjlOyKna2CZoUj4Dr5uYOiNHbxVbfLPhsIenRLEbjttCxrA9Mm9k++bdjA==} + + '@nrwl/jest@18.3.4': + resolution: {integrity: sha512-pIYd8WBQz6DKfhIKkZn9VsNBPR0QGnbAdI9AfrQPoGfj19x3tzqLSjcg/O5UvIs6174U1b+0ccxWmQvFep22Kw==} + + '@nrwl/js@18.3.4': + resolution: {integrity: sha512-oyiMoxzDVGQe5E4UFGO/WAOU211HEIdRxSEOfs1lPhvA8lKbUa0IWDqPOugNws/YHAr+vUTU3sZDJ3uU3RJuYQ==} + + '@nrwl/node@18.3.4': + resolution: {integrity: sha512-33u3nST6w/mheKjWb+wkdS4EcReS3fqXOtRo50NFcDGwVQIIA+xpQwEh8idjXUcHmwIPqn1oG76qsexpEQdS8w==} + + '@nrwl/tao@18.3.4': + resolution: {integrity: sha512-+7KsDYmGj1cvNaXZcjSYOPN1h17hsGFBtVX7MqnpJLLkQTUhKg2rQxqyluzshJ+RoDUVtYPGyHg1AizlB66RIA==} + hasBin: true + + '@nrwl/webpack@18.3.4': + resolution: {integrity: sha512-l4vWrum8nLBtDqeLM5BluKZScKSRegU3fvXmH4JJ+KY6ovVadZzcbM7SPiFKcBvIzzp034dajbp0iN/RJFSWlg==} + + '@nrwl/workspace@18.3.4': + resolution: {integrity: sha512-ziPHZcSYj46aPYrRHaKu56/SmYCijLT5vIm/UaoWD5v5Fy5CRigO/ezUImsHGHMEZWfHt44s4jsv7QdJWAXe7w==} + + '@nx/devkit@18.3.4': + resolution: {integrity: sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==} + peerDependencies: + nx: '>= 16 <= 19' + + '@nx/esbuild@18.3.4': + resolution: {integrity: sha512-W+rHeHzyARGp5wBsXQTcWWmooITdKrNSeRMXSL0Hx98jCmV+Mz7ErprNstltNSEB8d51ch4UifGdcMg/yc0dVQ==} + peerDependencies: + esbuild: ~0.19.2 + peerDependenciesMeta: + esbuild: + optional: true + + '@nx/eslint-plugin@18.3.4': + resolution: {integrity: sha512-rGQX/w+qexGWOLjHECZeZ2RDgaKEUvQW+zGBNlw/5u7tZcTYUpG10VG1j+BrwBZ1gT9YV1b/0IUYdwdEo1NNwA==} + peerDependencies: + '@typescript-eslint/parser': ^6.13.2 || ^7.0.0 + eslint-config-prettier: ^9.0.0 + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + '@nx/eslint@18.3.4': + resolution: {integrity: sha512-PiAMeI87RD9pi/IvOUWwszNBvwaY/V8fqcUUKIi6uzrzCsaKTmelTlaeJbH/z7Ulzx3iFPx1w1tcagI0ZWDBaQ==} + peerDependencies: + js-yaml: 4.1.0 + peerDependenciesMeta: + js-yaml: + optional: true + + '@nx/jest@18.3.4': + resolution: {integrity: sha512-sWbrhz8RYZ7j1uUbyB7tvulnRncNwtnEEMIGYUrHSQB1of+aG+FA2VtI3KCoWrfzMc5EDl7DLpKY1VMW2ArWhQ==} + + '@nx/js@18.3.4': + resolution: {integrity: sha512-+MPacp/B09e5QwaFQBkev9pW862ZpmesqR2lUUnFAXUBKtjYVIAmhJWHOtevqC1om4OxvTsbluYHsbAkAUzlMA==} + peerDependencies: + verdaccio: ^5.0.4 + peerDependenciesMeta: + verdaccio: + optional: true + + '@nx/linter@18.3.4': + resolution: {integrity: sha512-hvgsd1JMjRykHheIJOwxDAYLcy6b9wLtBZGV4pdg1Q4himocgG9Rhi/V7ha+hPtV8oq+iIRuNnLPY+UcdK2ovQ==} + + '@nx/node@18.3.4': + resolution: {integrity: sha512-w9eCtj820pUWsRsBal/qJ9czWw47K6UHmRZYpjIlOv5/7iwtq8NnFau9doYULsTu7caR27MExrUsKe+vmb4U2A==} + + '@nx/nx-darwin-arm64@18.3.4': + resolution: {integrity: sha512-MOGk9z4fIoOkJB68diH3bwoWrC8X9IzMNsz1mu0cbVfgCRAfIV3b+lMsiwQYzWal3UWW5DE5Rkss4F8whiV5Uw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@nx/nx-darwin-x64@18.3.4': + resolution: {integrity: sha512-tSzPRnNB3QdPM+KYiIuRCUtyCwcuIRC95FfP0ZB3WvfDeNxJChEAChNqmCMDE4iFvZhGuze8WqkJuIVdte+lyQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@nx/nx-freebsd-x64@18.3.4': + resolution: {integrity: sha512-bjSPak/d+bcR95/pxHMRhnnpHc6MnrQcG6f5AjX15Esm4JdrdQKPBmG1RybuK0WKSyD5wgVhkAGc/QQUom9l8g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@nx/nx-linux-arm-gnueabihf@18.3.4': + resolution: {integrity: sha512-/1HnUL7jhH0S7PxJqf6R1pk3QlAU22GY89EQV9fd+RDUtp7IyzaTlkebijTIqfxlSjC4OO3bPizaxEaxdd3uKQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@nx/nx-linux-arm64-gnu@18.3.4': + resolution: {integrity: sha512-g/2IaB2bZTKaBNPEf9LxtIXb1XHdhh3VO9PnePIrwkkixPMLN0dTxT5Sttt75lvLP3EU1AUR5w3Aaz2Q1mYtWA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@nx/nx-linux-arm64-musl@18.3.4': + resolution: {integrity: sha512-MgfKLoEF6I1cCS+0ooFLEjJSSVdCYyCT9Q96IHRJntAEL8u/0GR2OUoBoLC+q1lnbIkJr/uqTJxA2Jh+sJTIbA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@nx/nx-linux-x64-gnu@18.3.4': + resolution: {integrity: sha512-vbHxv7m3gjthBvw50EYCtgyY0Zg5nVTaQtX+wRsmKybV2i7wHbw5zIe1aL4zHUm6TcPGbIQK+utVM+hyCqKHVA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@nx/nx-linux-x64-musl@18.3.4': + resolution: {integrity: sha512-qIJKJCYFRLVSALsvg3avjReOjuYk91Q0hFXMJ2KaEM1Y3tdzcFN0fKBiaHexgbFIUk8zJuS4dJObTqSYMXowbg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@nx/nx-win32-arm64-msvc@18.3.4': + resolution: {integrity: sha512-UxC8mRkFTPdZbKFprZkiBqVw8624xU38kI0xyooxKlFpt5lccTBwJ0B7+R8p1RoWyvh2DSyFI9VvfD7lczg1lA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@nx/nx-win32-x64-msvc@18.3.4': + resolution: {integrity: sha512-/RqEjNU9hxIBxRLafCNKoH3SaB2FShf+1ZnIYCdAoCZBxLJebDpnhiyrVs0lPnMj9248JbizEMdJj1+bs/bXig==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nx/webpack@18.3.4': + resolution: {integrity: sha512-bufCGJJ+KvZ90OOCzcDh3hpYz6Zc6b6iNffzaPsJuL5YEOFVdwyn91lWfu761bzBOCqs2DPJenEmAGhSNK+muQ==} + + '@nx/workspace@18.3.4': + resolution: {integrity: sha512-H5HmEOWb9wnrNXfI2DhK6AmMVz1snuJvjT2jcMf9kxlVW0pKGTFW+OyHfSYq6Ni3OGWb1f9O63erLYHo45zPeA==} + + '@phenomnomnominal/tsquery@5.0.1': + resolution: {integrity: sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==} + peerDependencies: + typescript: ^3 || ^4 || ^5 + + '@pmmmwh/react-refresh-webpack-plugin@0.5.13': + resolution: {integrity: sha512-odZVYXly+JwzYri9rKqqUAk0cY6zLpv4dxoKinhoJNShV36Gpxf+CyDIILJ4tYsJ1ZxIWs233Y39iVnynvDA/g==} + engines: {node: '>= 10.13'} + peerDependencies: + '@types/webpack': 4.x || 5.x + react-refresh: '>=0.10.0 <1.0.0' + sockjs-client: ^1.4.0 + type-fest: '>=0.17.0 <5.0.0' + webpack: '>=4.43.0 <6.0.0' + webpack-dev-server: 3.x || 4.x || 5.x + webpack-hot-middleware: 2.x + webpack-plugin-serve: 0.x || 1.x + peerDependenciesMeta: + '@types/webpack': + optional: true + sockjs-client: + optional: true + type-fest: + optional: true + webpack-dev-server: + optional: true + webpack-hot-middleware: + optional: true + webpack-plugin-serve: + optional: true + + '@readme/better-ajv-errors@1.6.0': + resolution: {integrity: sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==} + engines: {node: '>=14'} + peerDependencies: + ajv: 4.11.8 - 8 + + '@readme/json-schema-ref-parser@1.2.0': + resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==} + + '@readme/openapi-parser@2.5.1': + resolution: {integrity: sha512-p9ndWhwjtP+DEiOOF6jeNMpdmYIPM4nl+JEIdnQNdq7b68esI024x7HiYACZpaYaSvISDSyKc7aiRJx4K4IDhg==} + engines: {node: '>=18'} + peerDependencies: + openapi-types: '>=7' + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/plugin-svgo@8.1.0': + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/webpack@8.1.0': + resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} + engines: {node: '>=14'} + + '@swc-node/core@1.13.1': + resolution: {integrity: sha512-emB5l2nZsXjUEAuusqjYvWnQMLWZp6K039Mv8aq5SX1rsNM/N7DNhw1i4/DX7AyzNZ0tT+ASWyTvqEURldp5HA==} + engines: {node: '>= 10'} + peerDependencies: + '@swc/core': '>= 1.4.13' + '@swc/types': '>= 0.1' + + '@swc-node/register@1.8.0': + resolution: {integrity: sha512-8K3589HoBSmVmrEVrtr4K5sWEithpGDzcFGic81OW0A9sZY38IV5EGRODQWCk0SBDyLhaF+pid120vJAtsHo1A==} + peerDependencies: + '@swc/core': '>= 1.3' + typescript: '>= 4.3' + + '@swc-node/sourcemap-support@0.4.0': + resolution: {integrity: sha512-weuRmYTO+4yOtHtPZHXlPdA1dJJJp3QOoZAFZ6uZidu992F2X5v1fQdnb26xs1o3Ex/e2sYhRyY5R6NGNuoATQ==} + + '@swc/core-darwin-arm64@1.3.107': + resolution: {integrity: sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.3.107': + resolution: {integrity: sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.3.107': + resolution: {integrity: sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.3.107': + resolution: {integrity: sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.3.107': + resolution: {integrity: sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.3.107': + resolution: {integrity: sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.3.107': + resolution: {integrity: sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.3.107': + resolution: {integrity: sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.3.107': + resolution: {integrity: sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.3.107': + resolution: {integrity: sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.3.107': + resolution: {integrity: sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.11': + resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} + + '@swc/types@0.1.7': + resolution: {integrity: sha512-scHWahbHF0eyj3JsxG9CFJgFdFNaVQCNAimBlT6PzS3n/HptxqREjsm4OH6AN3lYcffZYSPxXW8ua2BEHp0lJQ==} + + '@trivago/prettier-plugin-sort-imports@4.3.0': + resolution: {integrity: sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==} + peerDependencies: + '@vue/compiler-sfc': 3.x + prettier: 2.x - 3.x + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/deep-diff@1.0.5': + resolution: {integrity: sha512-PQyNSy1YMZU1hgZA5tTYfHPpUAo9Dorn1PZho2/budQLfqLu3JIP37JAavnwYpR1S2yFZTXa3hxaE4ifGW5jaA==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@8.56.10': + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/express-serve-static-core@4.19.1': + resolution: {integrity: sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/http-proxy@1.17.14': + resolution: {integrity: sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.12': + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/lodash@4.17.4': + resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node-forge@1.3.11': + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + + '@types/node@18.16.9': + resolution: {integrity: sha512-IeB32oIV4oGArLrd7znD2rkHQ6EDCM+2Sr76dJnrHwv9OHBTTM6nuDLK9bmikXzPa0ZlWMWtRGo/Uw4mrzQedA==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/pluralize@0.0.33': + resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/signale@1.4.7': + resolution: {integrity: sha512-nc0j37QupTT7OcYeH3gRE1ZfzUalEUsDKJsJ3IsJr0pjjFZTjtrX1Bsn6Kv56YXI/H9rNSwAkIPRxNlZI8GyQw==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/ws@8.5.10': + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.32': + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + + '@typescript-eslint/eslint-plugin@6.21.0': + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@6.21.0': + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@6.21.0': + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/scope-manager@7.10.0': + resolution: {integrity: sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@6.21.0': + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/type-utils@7.10.0': + resolution: {integrity: sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@6.21.0': + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/types@7.10.0': + resolution: {integrity: sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@6.21.0': + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@7.10.0': + resolution: {integrity: sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@6.21.0': + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + '@typescript-eslint/utils@7.10.0': + resolution: {integrity: sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@6.21.0': + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/visitor-keys@7.10.0': + resolution: {integrity: sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@webassemblyjs/ast@1.12.1': + resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + + '@webassemblyjs/floating-point-hex-parser@1.11.6': + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + + '@webassemblyjs/helper-api-error@1.11.6': + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + + '@webassemblyjs/helper-buffer@1.12.1': + resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + + '@webassemblyjs/helper-numbers@1.11.6': + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + + '@webassemblyjs/helper-wasm-bytecode@1.11.6': + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + + '@webassemblyjs/helper-wasm-section@1.12.1': + resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + + '@webassemblyjs/ieee754@1.11.6': + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + + '@webassemblyjs/leb128@1.11.6': + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + + '@webassemblyjs/utf8@1.11.6': + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + + '@webassemblyjs/wasm-edit@1.12.1': + resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + + '@webassemblyjs/wasm-gen@1.12.1': + resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + + '@webassemblyjs/wasm-opt@1.12.1': + resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + + '@webassemblyjs/wasm-parser@1.12.1': + resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + + '@webassemblyjs/wast-printer@1.12.1': + resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@yarnpkg/lockfile@1.1.0': + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} + + '@yarnpkg/parsers@3.0.0-rc.46': + resolution: {integrity: sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==} + engines: {node: '>=14.15.0'} + + '@zkochan/js-yaml@0.0.6': + resolution: {integrity: sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==} + hasBin: true + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-import-assertions@1.9.0: + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + + acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + + address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.14.0: + resolution: {integrity: sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array-union@3.0.1: + resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} + engines: {node: '>=12'} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.4.19: + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-loader@9.1.3: + resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: '>=5' + + babel-plugin-const-enum@1.2.0: + resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-plugin-macros@2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + + babel-plugin-polyfill-corejs2@0.4.11: + resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.10.4: + resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.2: + resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-transform-typescript-metadata@0.3.2: + resolution: {integrity: sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==} + peerDependencies: + '@babel/core': ^7 + '@babel/traverse': ^7 + peerDependenciesMeta: + '@babel/traverse': + optional: true + + babel-preset-current-node-syntax@1.0.1: + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bonjour-service@1.2.1: + resolution: {integrity: sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001623: + resolution: {integrity: sha512-X/XhAVKlpIxWPpgRTnlgZssJrF0m6YtRA0QDWgsBNT12uZM6LPRydR7ip405Y3t1LamD8cP2TZFEDZFBf5ApcA==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chrome-trace-event@1.0.3: + resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + engines: {node: '>=6.0'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cli-spinners@2.6.1: + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + code-error-fragment@0.0.230: + resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==} + engines: {node: '>= 4'} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + columnify@1.6.0: + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + engines: {node: '>=8.0.0'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + common-path-prefix@3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.7.4: + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + copy-webpack-plugin@10.2.4: + resolution: {integrity: sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==} + engines: {node: '>= 12.20.0'} + peerDependencies: + webpack: ^5.1.0 + + core-js-compat@3.37.1: + resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} + + core-js-pure@3.37.1: + resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-minimizer-webpack-plugin@5.0.1: + resolution: {integrity: sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@parcel/css': '*' + '@swc/css': '*' + clean-css: '*' + csso: '*' + esbuild: '*' + lightningcss: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@parcel/css': + optional: true + '@swc/css': + optional: true + clean-css: + optional: true + csso: + optional: true + esbuild: + optional: true + lightningcss: + optional: true + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@6.1.2: + resolution: {integrity: sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano-utils@4.0.2: + resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano@6.1.2: + resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-diff@1.0.2: + resolution: {integrity: sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-gateway@6.0.3: + resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} + engines: {node: '>= 10'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + detect-port@1.6.1: + resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==} + engines: {node: '>= 4.0.0'} + hasBin: true + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + + dotenv@16.3.2: + resolution: {integrity: sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==} + engines: {node: '>=12'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.4.783: + resolution: {integrity: sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + enhanced-resolve@5.16.1: + resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==} + engines: {node: '>=10.13.0'} + + enquirer@2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.5.3: + resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} + + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.48.0: + resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.2.7: + resolution: {integrity: sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==} + engines: {node: '>=8'} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + figures@2.0.0: + resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} + engines: {node: '>=4'} + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + + find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + + find-up@2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + fork-ts-checker-webpack-plugin@7.2.13: + resolution: {integrity: sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + vue-template-compiler: '*' + webpack: ^5.11.0 + peerDependenciesMeta: + vue-template-compiler: + optional: true + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-monkey@1.0.6: + resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@12.2.0: + resolution: {integrity: sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + harmony-reflect@1.6.2: + resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-entities@2.5.2: + resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + + http-proxy-middleware@2.0.6: + resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/express': ^4.17.13 + peerDependenciesMeta: + '@types/express': + optional: true + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + identity-obj-proxy@3.0.0: + resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} + engines: {node: '>=4'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + + immutable@4.3.6: + resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.2: + resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jake@10.9.1: + resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} + engines: {node: '>=10'} + hasBin: true + + javascript-natural-sort@0.7.1: + resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-to-ast@2.1.0: + resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==} + engines: {node: '>= 4'} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + launch-editor@2.6.1: + resolution: {integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==} + + less-loader@11.1.0: + resolution: {integrity: sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==} + engines: {node: '>= 14.15.0'} + peerDependencies: + less: ^3.5.0 || ^4.0.0 + webpack: ^5.0.0 + + less@4.1.3: + resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} + engines: {node: '>=6'} + hasBin: true + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + license-webpack-plugin@4.0.2: + resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==} + peerDependencies: + webpack: '*' + peerDependenciesMeta: + webpack: + optional: true + + lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lines-and-columns@2.0.4: + resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + listr2@8.2.1: + resolution: {integrity: sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==} + engines: {node: '>=18.0.0'} + + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + locate-path@2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@6.0.0: + resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} + engines: {node: '>=18'} + + logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + engines: {node: 14 || >=16.14} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mini-css-extract-plugin@2.4.7: + resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-machine-id@1.1.12: + resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-package-arg@11.0.1: + resolution: {integrity: sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nx@18.3.4: + resolution: {integrity: sha512-7rOHRyxpnZGJ3pHnwmpoAMHt9hNuwibWhOhPBJDhJVcbQJtGfwcWWyV/iSEnVXwKZ2lfHVE3TwE+gXFdT/GFiw==} + hasBin: true + peerDependencies: + '@swc-node/register': ^1.8.0 + '@swc/core': ^1.3.85 + peerDependenciesMeta: + '@swc-node/register': + optional: true + '@swc/core': + optional: true + + object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.3.0: + resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} + engines: {node: '>=10'} + + p-limit@1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-try@1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-duration@1.1.0: + resolution: {integrity: sha512-z6t9dvSJYaPoQq7quMzdEagSFtpGu+utzHqqxmpVWNNZRIXnvqyCvn9XsTdh7c/w0Bqmdz3RB3YnRaKtpRtEXQ==} + + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse5@4.0.0: + resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-conf@2.1.0: + resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} + engines: {node: '>=4'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + postcss-calc@9.0.1: + resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.2.2 + + postcss-colormin@6.1.0: + resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-convert-values@6.1.0: + resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-comments@6.0.2: + resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-duplicates@6.0.3: + resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-empty@6.0.3: + resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-overridden@6.0.2: + resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-import@14.1.0: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-loader@6.2.1: + resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + + postcss-merge-longhand@6.0.5: + resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-rules@6.1.1: + resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-font-values@6.1.0: + resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-gradients@6.0.3: + resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-params@6.1.0: + resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-selectors@6.0.4: + resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.0.5: + resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.0: + resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-normalize-charset@6.0.2: + resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-display-values@6.0.2: + resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-positions@6.0.2: + resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-repeat-style@6.0.2: + resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-string@6.0.2: + resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-timing-functions@6.0.2: + resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-unicode@6.1.0: + resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-url@6.0.2: + resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-whitespace@6.0.2: + resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-ordered-values@6.0.2: + resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-initial@6.1.0: + resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-transforms@6.0.2: + resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-selector-parser@6.1.0: + resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} + engines: {node: '>=4'} + + postcss-svgo@6.0.3: + resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} + engines: {node: ^14 || ^16 || >= 18} + peerDependencies: + postcss: ^8.4.31 + + postcss-unique-selectors@6.0.4: + resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + + qs@6.12.1: + resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-refresh@0.10.0: + resolution: {integrity: sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + reflect-metadata@0.1.14: + resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} + + regenerate-unicode-properties@10.1.1: + resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regenerator-transform@0.15.2: + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + + regexpu-core@5.3.2: + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + engines: {node: '>=4'} + + regjsparser@0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve.exports@1.1.0: + resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} + engines: {node: '>=10'} + + resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.3.1: + resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass-loader@12.6.0: + resolution: {integrity: sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + + sass@1.77.2: + resolution: {integrity: sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + + sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.2.0: + resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} + engines: {node: '>= 12.13.0'} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signale@1.4.0: + resolution: {integrity: sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==} + engines: {node: '>=6'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + slugify@1.6.6: + resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} + engines: {node: '>=8.0.0'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map-loader@3.0.2: + resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.19: + resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.1.0: + resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} + engines: {node: '>=18'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strong-log-transformer@2.1.0: + resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==} + engines: {node: '>=4'} + hasBin: true + + style-loader@3.3.4: + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + stylehacks@6.1.1: + resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + stylus-loader@7.1.3: + resolution: {integrity: sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + stylus: '>=0.52.4' + webpack: ^5.0.0 + + stylus@0.59.0: + resolution: {integrity: sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.3.10: + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.31.0: + resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-jest@29.1.3: + resolution: {integrity: sha512-6L9qz3ginTd1NKhOxmkP0qU3FyKjj5CPoY+anszfVn6Pmv/RIKzhiMCsH7Yb7UvJR9I2A64rm4zQl531s2F1iw==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + ts-loader@9.5.1: + resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + + ts-node@10.9.1: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsconfig-paths-webpack-plugin@4.0.0: + resolution: {integrity: sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-assert@1.0.9: + resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + + unicode-canonical-property-names-ecmascript@2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.1.0: + resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.0.16: + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-loader@4.1.1: + resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + file-loader: '*' + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + v8-to-istanbul@9.2.0: + resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + watchpack@2.4.1: + resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webpack-dev-middleware@5.3.4: + resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + webpack-dev-server@4.15.2: + resolution: {integrity: sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==} + engines: {node: '>= 12.13.0'} + hasBin: true + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + + webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + + webpack-subresource-integrity@5.1.0: + resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==} + engines: {node: '>= 12'} + peerDependencies: + html-webpack-plugin: '>= 5.0.0-beta.1 < 6' + webpack: ^5.12.0 + peerDependenciesMeta: + html-webpack-plugin: + optional: true + + webpack@5.91.0: + resolution: {integrity: sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + + winston@3.13.0: + resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ws@8.17.0: + resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.4.2: + resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + + zod-to-json-schema@3.23.0: + resolution: {integrity: sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==} + peerDependencies: + zod: ^3.23.3 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@adobe/css-tools@4.3.3': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@babel/code-frame@7.24.6': + dependencies: + '@babel/highlight': 7.24.6 + picocolors: 1.0.1 + + '@babel/compat-data@7.24.6': {} + + '@babel/core@7.24.6': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.6 + '@babel/generator': 7.24.6 + '@babel/helper-compilation-targets': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) + '@babel/helpers': 7.24.6 + '@babel/parser': 7.24.6 + '@babel/template': 7.24.6 + '@babel/traverse': 7.24.6 + '@babel/types': 7.24.6 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.17.7': + dependencies: + '@babel/types': 7.17.0 + jsesc: 2.5.2 + source-map: 0.5.7 + + '@babel/generator@7.24.6': + dependencies: + '@babel/types': 7.24.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-annotate-as-pure@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-builder-binary-assignment-operator-visitor@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-compilation-targets@7.24.6': + dependencies: + '@babel/compat-data': 7.24.6 + '@babel/helper-validator-option': 7.24.6 + browserslist: 4.23.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-function-name': 7.24.6 + '@babel/helper-member-expression-to-functions': 7.24.6 + '@babel/helper-optimise-call-expression': 7.24.6 + '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6) + '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + '@babel/helper-split-export-declaration': 7.24.6 + semver: 6.3.1 + + '@babel/helper-create-regexp-features-plugin@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + regexpu-core: 5.3.2 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-compilation-targets': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-environment-visitor@7.24.6': {} + + '@babel/helper-function-name@7.24.6': + dependencies: + '@babel/template': 7.24.6 + '@babel/types': 7.24.6 + + '@babel/helper-hoist-variables@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-member-expression-to-functions@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-module-imports@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-module-transforms@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-module-imports': 7.24.6 + '@babel/helper-simple-access': 7.24.6 + '@babel/helper-split-export-declaration': 7.24.6 + '@babel/helper-validator-identifier': 7.24.6 + + '@babel/helper-optimise-call-expression@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-plugin-utils@7.24.6': {} + + '@babel/helper-remap-async-to-generator@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-wrap-function': 7.24.6 + + '@babel/helper-replace-supers@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-member-expression-to-functions': 7.24.6 + '@babel/helper-optimise-call-expression': 7.24.6 + + '@babel/helper-simple-access@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-skip-transparent-expression-wrappers@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-split-export-declaration@7.24.6': + dependencies: + '@babel/types': 7.24.6 + + '@babel/helper-string-parser@7.24.6': {} + + '@babel/helper-validator-identifier@7.24.6': {} + + '@babel/helper-validator-option@7.24.6': {} + + '@babel/helper-wrap-function@7.24.6': + dependencies: + '@babel/helper-function-name': 7.24.6 + '@babel/template': 7.24.6 + '@babel/types': 7.24.6 + + '@babel/helpers@7.24.6': + dependencies: + '@babel/template': 7.24.6 + '@babel/types': 7.24.6 + + '@babel/highlight@7.24.6': + dependencies: + '@babel/helper-validator-identifier': 7.24.6 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.24.6': + dependencies: + '@babel/types': 7.17.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-proposal-decorators@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-decorators': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-decorators@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-import-assertions@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-import-attributes@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-jsx@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-typescript@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-arrow-functions@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-async-generator-functions@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6) + + '@babel/plugin-transform-async-to-generator@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-module-imports': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-transform-block-scoped-functions@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-block-scoping@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-class-properties@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-class-static-block@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.6) + + '@babel/plugin-transform-classes@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-compilation-targets': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-function-name': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6) + '@babel/helper-split-export-declaration': 7.24.6 + globals: 11.12.0 + + '@babel/plugin-transform-computed-properties@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/template': 7.24.6 + + '@babel/plugin-transform-destructuring@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-dotall-regex@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-duplicate-keys@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-dynamic-import@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.6) + + '@babel/plugin-transform-exponentiation-operator@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-export-namespace-from@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.6) + + '@babel/plugin-transform-for-of@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + + '@babel/plugin-transform-function-name@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-compilation-targets': 7.24.6 + '@babel/helper-function-name': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-json-strings@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6) + + '@babel/plugin-transform-literals@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-logical-assignment-operators@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6) + + '@babel/plugin-transform-member-expression-literals@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-modules-amd@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-modules-commonjs@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-simple-access': 7.24.6 + + '@babel/plugin-transform-modules-systemjs@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-hoist-variables': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-validator-identifier': 7.24.6 + + '@babel/plugin-transform-modules-umd@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-named-capturing-groups-regex@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-new-target@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6) + + '@babel/plugin-transform-numeric-separator@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6) + + '@babel/plugin-transform-object-rest-spread@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-compilation-targets': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-transform-object-super@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-transform-optional-catch-binding@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6) + + '@babel/plugin-transform-optional-chaining@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6) + + '@babel/plugin-transform-parameters@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-private-methods@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-private-property-in-object@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.6) + + '@babel/plugin-transform-property-literals@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-react-constant-elements@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-react-display-name@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-react-jsx-development@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/plugin-transform-react-jsx': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-transform-react-jsx@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-module-imports': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-jsx': 7.24.6(@babel/core@7.24.6) + '@babel/types': 7.24.6 + + '@babel/plugin-transform-react-pure-annotations@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-regenerator@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + regenerator-transform: 0.15.2 + + '@babel/plugin-transform-reserved-words@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-runtime@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-module-imports': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.6) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.6) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.6) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-spread@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + + '@babel/plugin-transform-sticky-regex@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-template-literals@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-typeof-symbol@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-typescript@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-typescript': 7.24.6(@babel/core@7.24.6) + + '@babel/plugin-transform-unicode-escapes@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-unicode-property-regex@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-unicode-regex@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/plugin-transform-unicode-sets-regex@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6) + '@babel/helper-plugin-utils': 7.24.6 + + '@babel/preset-env@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/compat-data': 7.24.6 + '@babel/core': 7.24.6 + '@babel/helper-compilation-targets': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-validator-option': 7.24.6 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.6) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.6) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.6) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-import-assertions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-import-attributes': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.6) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.6) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.6) + '@babel/plugin-transform-arrow-functions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-async-generator-functions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-async-to-generator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-block-scoped-functions': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-block-scoping': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-class-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-class-static-block': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-classes': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-computed-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-destructuring': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-dotall-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-duplicate-keys': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-dynamic-import': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-exponentiation-operator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-export-namespace-from': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-for-of': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-function-name': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-json-strings': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-logical-assignment-operators': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-member-expression-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-amd': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-commonjs': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-systemjs': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-umd': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-new-target': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-numeric-separator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-object-rest-spread': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-object-super': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-optional-catch-binding': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-private-methods': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-private-property-in-object': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-property-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-regenerator': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-reserved-words': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-shorthand-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-spread': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-sticky-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-template-literals': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-typeof-symbol': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-escapes': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-property-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-regex': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-unicode-sets-regex': 7.24.6(@babel/core@7.24.6) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.6) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.6) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.6) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.6) + core-js-compat: 3.37.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/types': 7.24.6 + esutils: 2.0.3 + + '@babel/preset-react@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-validator-option': 7.24.6 + '@babel/plugin-transform-react-display-name': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-react-jsx': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-react-jsx-development': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-react-pure-annotations': 7.24.6(@babel/core@7.24.6) + + '@babel/preset-typescript@7.24.6(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-validator-option': 7.24.6 + '@babel/plugin-syntax-jsx': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-modules-commonjs': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-typescript': 7.24.6(@babel/core@7.24.6) + + '@babel/regjsgen@0.8.0': {} + + '@babel/runtime@7.24.6': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.24.6': + dependencies: + '@babel/code-frame': 7.24.6 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + + '@babel/traverse@7.23.2': + dependencies: + '@babel/code-frame': 7.24.6 + '@babel/generator': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-function-name': 7.24.6 + '@babel/helper-hoist-variables': 7.24.6 + '@babel/helper-split-export-declaration': 7.24.6 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.24.6': + dependencies: + '@babel/code-frame': 7.24.6 + '@babel/generator': 7.24.6 + '@babel/helper-environment-visitor': 7.24.6 + '@babel/helper-function-name': 7.24.6 + '@babel/helper-hoist-variables': 7.24.6 + '@babel/helper-split-export-declaration': 7.24.6 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.17.0': + dependencies: + '@babel/helper-validator-identifier': 7.24.6 + to-fast-properties: 2.0.0 + + '@babel/types@7.24.6': + dependencies: + '@babel/helper-string-parser': 7.24.6 + '@babel/helper-validator-identifier': 7.24.6 + to-fast-properties: 2.0.0 + + '@bcoe/v8-coverage@0.2.3': {} + + '@colors/colors@1.6.0': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@dabh/diagnostics@2.0.3': + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + + '@esbuild/aix-ppc64@0.19.12': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + + '@esbuild/android-arm@0.19.12': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + + '@esbuild/linux-loong64@0.19.12': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.48.0)': + dependencies: + eslint: 8.48.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.10.0': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.48.0': {} + + '@humanwhocodes/config-array@0.11.14': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/momoa@2.0.4': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.7 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 18.16.9 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 18.16.9 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.2.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.24.6 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.7 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.16.9 + '@types/yargs': 17.0.32 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@jsdevtools/ono@7.1.3': {} + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@nrwl/devkit@18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + dependencies: + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + transitivePeerDependencies: + - nx + + '@nrwl/esbuild@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@nx/esbuild': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - esbuild + - nx + - supports-color + - typescript + - verdaccio + + '@nrwl/eslint-plugin-nx@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@nx/eslint-plugin': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - '@typescript-eslint/parser' + - debug + - eslint + - eslint-config-prettier + - nx + - supports-color + - typescript + - verdaccio + + '@nrwl/jest@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + dependencies: + '@nx/jest': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - babel-plugin-macros + - debug + - node-notifier + - nx + - supports-color + - ts-node + - typescript + - verdaccio + + '@nrwl/js@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - nx + - supports-color + - typescript + - verdaccio + + '@nrwl/node@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + dependencies: + '@nx/node': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - babel-plugin-macros + - debug + - js-yaml + - node-notifier + - nx + - supports-color + - ts-node + - typescript + - verdaccio + + '@nrwl/tao@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))': + dependencies: + nx: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + tslib: 2.6.2 + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + - debug + + '@nrwl/webpack@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@nx/webpack': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + transitivePeerDependencies: + - '@babel/traverse' + - '@parcel/css' + - '@rspack/core' + - '@swc-node/register' + - '@swc/core' + - '@swc/css' + - '@swc/wasm' + - '@types/node' + - bufferutil + - clean-css + - csso + - debug + - esbuild + - fibers + - html-webpack-plugin + - lightningcss + - node-sass + - nx + - sass-embedded + - supports-color + - typescript + - uglify-js + - utf-8-validate + - verdaccio + - vue-template-compiler + - webpack-cli + + '@nrwl/workspace@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))': + dependencies: + '@nx/workspace': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + - debug + + '@nx/devkit@18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + dependencies: + '@nrwl/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + ejs: 3.1.10 + enquirer: 2.3.6 + ignore: 5.3.1 + nx: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + semver: 7.6.2 + tmp: 0.2.3 + tslib: 2.6.2 + yargs-parser: 21.1.1 + + '@nx/esbuild@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@nrwl/esbuild': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + chalk: 4.1.2 + fast-glob: 3.2.7 + fs-extra: 11.2.0 + tsconfig-paths: 4.2.0 + tslib: 2.6.2 + optionalDependencies: + esbuild: 0.19.12 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - nx + - supports-color + - typescript + - verdaccio + + '@nx/eslint-plugin@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@nrwl/eslint-plugin-nx': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@typescript-eslint/parser': 6.21.0(eslint@8.48.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 7.10.0(eslint@8.48.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.10.0(eslint@8.48.0)(typescript@5.4.5) + chalk: 4.1.2 + confusing-browser-globals: 1.0.11 + jsonc-eslint-parser: 2.4.0 + semver: 7.6.2 + tslib: 2.6.2 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@8.48.0) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - eslint + - nx + - supports-color + - typescript + - verdaccio + + '@nx/eslint@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + dependencies: + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/linter': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + eslint: 8.48.0 + tslib: 2.6.2 + typescript: 5.4.5 + optionalDependencies: + js-yaml: 4.1.0 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - nx + - supports-color + - verdaccio + + '@nx/jest@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + dependencies: + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@nrwl/jest': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@phenomnomnominal/tsquery': 5.0.1(typescript@5.4.5) + chalk: 4.1.2 + identity-obj-proxy: 3.0.0 + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-resolve: 29.7.0 + jest-util: 29.7.0 + minimatch: 9.0.3 + resolve.exports: 1.1.0 + tslib: 2.6.2 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - babel-plugin-macros + - debug + - node-notifier + - nx + - supports-color + - ts-node + - typescript + - verdaccio + + '@nx/js@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@babel/core': 7.24.6 + '@babel/plugin-proposal-decorators': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-class-properties': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-transform-runtime': 7.24.6(@babel/core@7.24.6) + '@babel/preset-env': 7.24.6(@babel/core@7.24.6) + '@babel/preset-typescript': 7.24.6(@babel/core@7.24.6) + '@babel/runtime': 7.24.6 + '@nrwl/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/workspace': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + '@phenomnomnominal/tsquery': 5.0.1(typescript@5.4.5) + babel-plugin-const-enum: 1.2.0(@babel/core@7.24.6) + babel-plugin-macros: 2.8.0 + babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.24.6)(@babel/traverse@7.24.6) + chalk: 4.1.2 + columnify: 1.6.0 + detect-port: 1.6.1 + fast-glob: 3.2.7 + fs-extra: 11.2.0 + ignore: 5.3.1 + js-tokens: 4.0.0 + minimatch: 9.0.3 + npm-package-arg: 11.0.1 + npm-run-path: 4.0.1 + ora: 5.3.0 + semver: 7.6.2 + source-map-support: 0.5.19 + ts-node: 10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5) + tsconfig-paths: 4.2.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - nx + - supports-color + - typescript + + '@nx/linter@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + dependencies: + '@nx/eslint': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - debug + - js-yaml + - nx + - supports-color + - verdaccio + + '@nx/node@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + dependencies: + '@nrwl/node': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/eslint': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/jest': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + tslib: 2.6.2 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - babel-plugin-macros + - debug + - js-yaml + - node-notifier + - nx + - supports-color + - ts-node + - typescript + - verdaccio + + '@nx/nx-darwin-arm64@18.3.4': + optional: true + + '@nx/nx-darwin-x64@18.3.4': + optional: true + + '@nx/nx-freebsd-x64@18.3.4': + optional: true + + '@nx/nx-linux-arm-gnueabihf@18.3.4': + optional: true + + '@nx/nx-linux-arm64-gnu@18.3.4': + optional: true + + '@nx/nx-linux-arm64-musl@18.3.4': + optional: true + + '@nx/nx-linux-x64-gnu@18.3.4': + optional: true + + '@nx/nx-linux-x64-musl@18.3.4': + optional: true + + '@nx/nx-win32-arm64-msvc@18.3.4': + optional: true + + '@nx/nx-win32-x64-msvc@18.3.4': + optional: true + + '@nx/webpack@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + dependencies: + '@babel/core': 7.24.6 + '@nrwl/webpack': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + ajv: 8.14.0 + autoprefixer: 10.4.19(postcss@8.4.38) + babel-loader: 9.1.3(@babel/core@7.24.6)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + browserslist: 4.23.0 + chalk: 4.1.2 + copy-webpack-plugin: 10.2.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + css-loader: 6.11.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + css-minimizer-webpack-plugin: 5.0.1(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + fork-ts-checker-webpack-plugin: 7.2.13(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + less: 4.1.3 + less-loader: 11.1.0(less@4.1.3)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + license-webpack-plugin: 4.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + loader-utils: 2.0.4 + mini-css-extract-plugin: 2.4.7(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + parse5: 4.0.0 + postcss: 8.4.38 + postcss-import: 14.1.0(postcss@8.4.38) + postcss-loader: 6.2.1(postcss@8.4.38)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + rxjs: 7.8.1 + sass: 1.77.2 + sass-loader: 12.6.0(sass@1.77.2)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + source-map-loader: 3.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + style-loader: 3.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + stylus: 0.59.0 + stylus-loader: 7.1.3(stylus@0.59.0)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + ts-loader: 9.5.1(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + tsconfig-paths-webpack-plugin: 4.0.0 + tslib: 2.6.2 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + webpack-node-externals: 3.0.0 + webpack-subresource-integrity: 5.1.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + transitivePeerDependencies: + - '@babel/traverse' + - '@parcel/css' + - '@rspack/core' + - '@swc-node/register' + - '@swc/core' + - '@swc/css' + - '@swc/wasm' + - '@types/node' + - bufferutil + - clean-css + - csso + - debug + - esbuild + - fibers + - html-webpack-plugin + - lightningcss + - node-sass + - nx + - sass-embedded + - supports-color + - typescript + - uglify-js + - utf-8-validate + - verdaccio + - vue-template-compiler + - webpack-cli + + '@nx/workspace@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))': + dependencies: + '@nrwl/workspace': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + chalk: 4.1.2 + enquirer: 2.3.6 + nx: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + tslib: 2.6.2 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + - debug + + '@phenomnomnominal/tsquery@5.0.1(typescript@5.4.5)': + dependencies: + esquery: 1.5.0 + typescript: 5.4.5 + + '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12))': + dependencies: + ansi-html-community: 0.0.8 + core-js-pure: 3.37.1 + error-stack-parser: 2.1.4 + html-entities: 2.5.2 + loader-utils: 2.0.4 + react-refresh: 0.10.0 + schema-utils: 3.3.0 + source-map: 0.7.4 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + optionalDependencies: + type-fest: 3.13.1 + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + + '@readme/better-ajv-errors@1.6.0(ajv@8.14.0)': + dependencies: + '@babel/code-frame': 7.24.6 + '@babel/runtime': 7.24.6 + '@humanwhocodes/momoa': 2.0.4 + ajv: 8.14.0 + chalk: 4.1.2 + json-to-ast: 2.1.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@readme/json-schema-ref-parser@1.2.0': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + call-me-maybe: 1.0.2 + js-yaml: 4.1.0 + + '@readme/openapi-parser@2.5.1(openapi-types@12.1.3)': + dependencies: + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + '@readme/better-ajv-errors': 1.6.0(ajv@8.14.0) + '@readme/json-schema-ref-parser': 1.2.0 + ajv: 8.14.0 + ajv-draft-04: 1.0.0(ajv@8.14.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + + '@sinclair/typebox@0.27.8': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + + '@svgr/babel-preset@8.1.0(@babel/core@7.24.6)': + dependencies: + '@babel/core': 7.24.6 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.24.6) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.24.6) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.24.6) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.24.6) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.24.6) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.24.6) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.24.6) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.24.6) + + '@svgr/core@8.1.0(typescript@5.4.5)': + dependencies: + '@babel/core': 7.24.6 + '@svgr/babel-preset': 8.1.0(@babel/core@7.24.6) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.4.5) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.24.6 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))': + dependencies: + '@babel/core': 7.24.6 + '@svgr/babel-preset': 8.1.0(@babel/core@7.24.6) + '@svgr/core': 8.1.0(typescript@5.4.5) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.4.5) + cosmiconfig: 8.3.6(typescript@5.4.5) + deepmerge: 4.3.1 + svgo: 3.3.2 + transitivePeerDependencies: + - typescript + + '@svgr/webpack@8.1.0(typescript@5.4.5)': + dependencies: + '@babel/core': 7.24.6 + '@babel/plugin-transform-react-constant-elements': 7.24.6(@babel/core@7.24.6) + '@babel/preset-env': 7.24.6(@babel/core@7.24.6) + '@babel/preset-react': 7.24.6(@babel/core@7.24.6) + '@babel/preset-typescript': 7.24.6(@babel/core@7.24.6) + '@svgr/core': 8.1.0(typescript@5.4.5) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5) + transitivePeerDependencies: + - supports-color + - typescript + + '@swc-node/core@1.13.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)': + dependencies: + '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@swc/types': 0.1.7 + + '@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5)': + dependencies: + '@swc-node/core': 1.13.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7) + '@swc-node/sourcemap-support': 0.4.0 + '@swc/core': 1.3.107(@swc/helpers@0.5.11) + colorette: 2.0.20 + debug: 4.3.4 + pirates: 4.0.6 + tslib: 2.6.2 + typescript: 5.4.5 + transitivePeerDependencies: + - '@swc/types' + - supports-color + + '@swc-node/sourcemap-support@0.4.0': + dependencies: + source-map-support: 0.5.21 + tslib: 2.6.2 + + '@swc/core-darwin-arm64@1.3.107': + optional: true + + '@swc/core-darwin-x64@1.3.107': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.3.107': + optional: true + + '@swc/core-linux-arm64-gnu@1.3.107': + optional: true + + '@swc/core-linux-arm64-musl@1.3.107': + optional: true + + '@swc/core-linux-x64-gnu@1.3.107': + optional: true + + '@swc/core-linux-x64-musl@1.3.107': + optional: true + + '@swc/core-win32-arm64-msvc@1.3.107': + optional: true + + '@swc/core-win32-ia32-msvc@1.3.107': + optional: true + + '@swc/core-win32-x64-msvc@1.3.107': + optional: true + + '@swc/core@1.3.107(@swc/helpers@0.5.11)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.7 + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.107 + '@swc/core-darwin-x64': 1.3.107 + '@swc/core-linux-arm-gnueabihf': 1.3.107 + '@swc/core-linux-arm64-gnu': 1.3.107 + '@swc/core-linux-arm64-musl': 1.3.107 + '@swc/core-linux-x64-gnu': 1.3.107 + '@swc/core-linux-x64-musl': 1.3.107 + '@swc/core-win32-arm64-msvc': 1.3.107 + '@swc/core-win32-ia32-msvc': 1.3.107 + '@swc/core-win32-x64-msvc': 1.3.107 + '@swc/helpers': 0.5.11 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.11': + dependencies: + tslib: 2.6.2 + + '@swc/types@0.1.7': + dependencies: + '@swc/counter': 0.1.3 + + '@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.2.5)': + dependencies: + '@babel/generator': 7.17.7 + '@babel/parser': 7.24.6 + '@babel/traverse': 7.23.2 + '@babel/types': 7.17.0 + javascript-natural-sort: 0.7.1 + lodash: 4.17.21 + prettier: 3.2.5 + transitivePeerDependencies: + - supports-color + + '@trysound/sax@0.2.0': {} + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.24.6 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.24.6 + + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 18.16.9 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 18.16.9 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 4.19.1 + '@types/node': 18.16.9 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 18.16.9 + + '@types/deep-diff@1.0.5': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 8.56.10 + '@types/estree': 1.0.5 + + '@types/eslint@8.56.10': + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.5': {} + + '@types/express-serve-static-core@4.19.1': + dependencies: + '@types/node': 18.16.9 + '@types/qs': 6.9.15 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express@4.17.21': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.19.1 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 18.16.9 + + '@types/http-errors@2.0.4': {} + + '@types/http-proxy@1.17.14': + dependencies: + '@types/node': 18.16.9 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.12': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/js-yaml@4.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/lodash@4.17.4': {} + + '@types/mime@1.3.5': {} + + '@types/node-forge@1.3.11': + dependencies: + '@types/node': 18.16.9 + + '@types/node@18.16.9': {} + + '@types/parse-json@4.0.2': {} + + '@types/pluralize@0.0.33': {} + + '@types/qs@6.9.15': {} + + '@types/range-parser@1.2.7': {} + + '@types/retry@0.12.0': {} + + '@types/semver@7.5.8': {} + + '@types/send@0.17.4': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 18.16.9 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.21 + + '@types/serve-static@1.15.7': + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 18.16.9 + '@types/send': 0.17.4 + + '@types/signale@1.4.7': + dependencies: + '@types/node': 18.16.9 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 18.16.9 + + '@types/stack-utils@2.0.3': {} + + '@types/triple-beam@1.3.5': {} + + '@types/ws@8.5.10': + dependencies: + '@types/node': 18.16.9 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.32': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint@8.48.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.21.0(eslint@8.48.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.48.0)(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@8.48.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + eslint: 8.48.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + eslint: 8.48.0 + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + + '@typescript-eslint/scope-manager@7.10.0': + dependencies: + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/visitor-keys': 7.10.0 + + '@typescript-eslint/type-utils@6.21.0(eslint@8.48.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@8.48.0)(typescript@5.4.5) + debug: 4.3.4 + eslint: 8.48.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@7.10.0(eslint@8.48.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) + '@typescript-eslint/utils': 7.10.0(eslint@8.48.0)(typescript@5.4.5) + debug: 4.3.4 + eslint: 8.48.0 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.21.0': {} + + '@typescript-eslint/types@7.10.0': {} + + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@7.10.0(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/visitor-keys': 7.10.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.4 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@6.21.0(eslint@8.48.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) + eslint: 8.48.0 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@7.10.0(eslint@8.48.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@typescript-eslint/scope-manager': 7.10.0 + '@typescript-eslint/types': 7.10.0 + '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) + eslint: 8.48.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@7.10.0': + dependencies: + '@typescript-eslint/types': 7.10.0 + eslint-visitor-keys: 3.4.3 + + '@webassemblyjs/ast@1.12.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + + '@webassemblyjs/floating-point-hex-parser@1.11.6': {} + + '@webassemblyjs/helper-api-error@1.11.6': {} + + '@webassemblyjs/helper-buffer@1.12.1': {} + + '@webassemblyjs/helper-numbers@1.11.6': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.11.6': {} + + '@webassemblyjs/helper-wasm-section@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.12.1 + + '@webassemblyjs/ieee754@1.11.6': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.11.6': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.11.6': {} + + '@webassemblyjs/wasm-edit@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-opt': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/wast-printer': 1.12.1 + + '@webassemblyjs/wasm-gen@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + + '@webassemblyjs/wasm-opt@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + + '@webassemblyjs/wasm-parser@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + + '@webassemblyjs/wast-printer@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@yarnpkg/lockfile@1.1.0': {} + + '@yarnpkg/parsers@3.0.0-rc.46': + dependencies: + js-yaml: 3.14.1 + tslib: 2.6.2 + + '@zkochan/js-yaml@0.0.6': + dependencies: + argparse: 2.0.1 + + abab@2.0.6: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-import-assertions@1.9.0(acorn@8.11.3): + dependencies: + acorn: 8.11.3 + + acorn-jsx@5.3.2(acorn@8.11.3): + dependencies: + acorn: 8.11.3 + + acorn-walk@8.3.2: {} + + acorn@8.11.3: {} + + address@1.2.2: {} + + ajv-draft-04@1.0.0(ajv@8.14.0): + optionalDependencies: + ajv: 8.14.0 + + ajv-formats@2.1.1(ajv@8.14.0): + optionalDependencies: + ajv: 8.14.0 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.14.0): + dependencies: + ajv: 8.14.0 + fast-deep-equal: 3.1.3 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.14.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@6.2.1: {} + + ansi-html-community@0.0.8: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@4.1.3: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-flatten@1.1.1: {} + + array-union@2.1.0: {} + + array-union@3.0.1: {} + + async@3.2.5: {} + + asynckit@0.4.0: {} + + autoprefixer@10.4.19(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001623 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.1 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + axios@1.7.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-jest@29.7.0(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.24.6) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-loader@9.1.3(@babel/core@7.24.6)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + '@babel/core': 7.24.6 + find-cache-dir: 4.0.0 + schema-utils: 4.2.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + babel-plugin-const-enum@1.2.0(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + '@babel/plugin-syntax-typescript': 7.24.6(@babel/core@7.24.6) + '@babel/traverse': 7.24.6 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.24.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.24.6 + '@babel/types': 7.24.6 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-plugin-macros@2.8.0: + dependencies: + '@babel/runtime': 7.24.6 + cosmiconfig: 6.0.0 + resolve: 1.22.8 + + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.6): + dependencies: + '@babel/compat-data': 7.24.6 + '@babel/core': 7.24.6 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.6) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.6) + core-js-compat: 3.37.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.6) + transitivePeerDependencies: + - supports-color + + babel-plugin-transform-typescript-metadata@0.3.2(@babel/core@7.24.6)(@babel/traverse@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@babel/helper-plugin-utils': 7.24.6 + optionalDependencies: + '@babel/traverse': 7.24.6 + + babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.6) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.6) + + babel-preset-jest@29.6.3(@babel/core@7.24.6): + dependencies: + '@babel/core': 7.24.6 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.6) + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + batch@0.6.1: {} + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.2.1: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.0: + dependencies: + caniuse-lite: 1.0.30001623 + electron-to-chromium: 1.4.783 + node-releases: 2.0.14 + update-browserslist-db: 1.0.16(browserslist@4.23.0) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bytes@3.0.0: {} + + bytes@3.1.2: {} + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + call-me-maybe@1.0.2: {} + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001623 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001623: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chrome-trace-event@1.0.3: {} + + ci-info@3.9.0: {} + + cjs-module-lexer@1.3.1: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + + cli-spinners@2.6.1: {} + + cli-spinners@2.9.2: {} + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.1.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + + co@4.6.0: {} + + code-error-fragment@0.0.230: {} + + collect-v8-coverage@1.0.2: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + + colord@2.9.3: {} + + colorette@2.0.20: {} + + colorspace@1.1.4: + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + + columnify@1.6.0: + dependencies: + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@2.20.3: {} + + commander@7.2.0: {} + + common-path-prefix@3.0.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.52.0 + + compression@1.7.4: + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + confusing-browser-globals@1.0.11: {} + + connect-history-api-fallback@2.0.0: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.6: {} + + cookie@0.6.0: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-webpack-plugin@10.2.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + fast-glob: 3.3.2 + glob-parent: 6.0.2 + globby: 12.2.0 + normalize-path: 3.0.0 + schema-utils: 4.2.0 + serialize-javascript: 6.0.2 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + core-js-compat@3.37.1: + dependencies: + browserslist: 4.23.0 + + core-js-pure@3.37.1: {} + + core-util-is@1.0.3: {} + + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@8.3.6(typescript@5.4.5): + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.4.5 + + create-jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-require@1.1.1: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-declaration-sorter@7.2.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + css-loader@6.11.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + icss-utils: 5.1.0(postcss@8.4.38) + postcss: 8.4.38 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.38) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.38) + postcss-modules-scope: 3.2.0(postcss@8.4.38) + postcss-modules-values: 4.0.0(postcss@8.4.38) + postcss-value-parser: 4.2.0 + semver: 7.6.2 + optionalDependencies: + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + css-minimizer-webpack-plugin@5.0.1(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + cssnano: 6.1.2(postcss@8.4.38) + jest-worker: 29.7.0 + postcss: 8.4.38 + schema-utils: 4.2.0 + serialize-javascript: 6.0.2 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + optionalDependencies: + esbuild: 0.19.12 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.0 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@6.1.2(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + css-declaration-sorter: 7.2.0(postcss@8.4.38) + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-calc: 9.0.1(postcss@8.4.38) + postcss-colormin: 6.1.0(postcss@8.4.38) + postcss-convert-values: 6.1.0(postcss@8.4.38) + postcss-discard-comments: 6.0.2(postcss@8.4.38) + postcss-discard-duplicates: 6.0.3(postcss@8.4.38) + postcss-discard-empty: 6.0.3(postcss@8.4.38) + postcss-discard-overridden: 6.0.2(postcss@8.4.38) + postcss-merge-longhand: 6.0.5(postcss@8.4.38) + postcss-merge-rules: 6.1.1(postcss@8.4.38) + postcss-minify-font-values: 6.1.0(postcss@8.4.38) + postcss-minify-gradients: 6.0.3(postcss@8.4.38) + postcss-minify-params: 6.1.0(postcss@8.4.38) + postcss-minify-selectors: 6.0.4(postcss@8.4.38) + postcss-normalize-charset: 6.0.2(postcss@8.4.38) + postcss-normalize-display-values: 6.0.2(postcss@8.4.38) + postcss-normalize-positions: 6.0.2(postcss@8.4.38) + postcss-normalize-repeat-style: 6.0.2(postcss@8.4.38) + postcss-normalize-string: 6.0.2(postcss@8.4.38) + postcss-normalize-timing-functions: 6.0.2(postcss@8.4.38) + postcss-normalize-unicode: 6.1.0(postcss@8.4.38) + postcss-normalize-url: 6.0.2(postcss@8.4.38) + postcss-normalize-whitespace: 6.0.2(postcss@8.4.38) + postcss-ordered-values: 6.0.2(postcss@8.4.38) + postcss-reduce-initial: 6.1.0(postcss@8.4.38) + postcss-reduce-transforms: 6.0.2(postcss@8.4.38) + postcss-svgo: 6.0.3(postcss@8.4.38) + postcss-unique-selectors: 6.0.4(postcss@8.4.38) + + cssnano-utils@4.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + cssnano@6.1.2(postcss@8.4.38): + dependencies: + cssnano-preset-default: 6.1.2(postcss@8.4.38) + lilconfig: 3.1.1 + postcss: 8.4.38 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + dedent@1.5.3: {} + + deep-diff@1.0.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-gateway@6.0.3: + dependencies: + execa: 5.1.1 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-lazy-prop@2.0.0: {} + + delayed-stream@1.0.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-newline@3.1.0: {} + + detect-node@2.1.0: {} + + detect-port@1.6.1: + dependencies: + address: 1.2.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + diff-sequences@29.6.3: {} + + diff@4.0.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + + dotenv-expand@10.0.0: {} + + dotenv@16.3.2: {} + + dotenv@16.4.5: {} + + duplexer@0.1.2: {} + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.1 + + electron-to-chromium@1.4.783: {} + + emittery@0.13.1: {} + + emoji-regex@10.3.0: {} + + emoji-regex@8.0.0: {} + + emojis-list@3.0.0: {} + + enabled@2.0.0: {} + + encodeurl@1.0.2: {} + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.16.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + enquirer@2.3.6: + dependencies: + ansi-colors: 4.1.3 + + entities@4.5.0: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + es-module-lexer@1.5.3: {} + + esbuild@0.19.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + + escalade@3.1.2: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@9.1.0(eslint@8.48.0): + dependencies: + eslint: 8.48.0 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.48.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.48.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.5.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-glob@3.2.7: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fecha@4.2.3: {} + + figures@2.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.2.0: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-cache-dir@4.0.0: + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + + find-up@2.1.0: + dependencies: + locate-path: 2.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flat@5.0.2: {} + + flatted@3.3.1: {} + + fn.name@1.1.0: {} + + follow-redirects@1.15.6: {} + + fork-ts-checker-webpack-plugin@7.2.13(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + '@babel/code-frame': 7.24.6 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.6.2 + tapable: 2.2.1 + typescript: 5.4.5 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + forwarded@0.2.0: {} + + fraction.js@4.3.7: {} + + fresh@0.5.2: {} + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-monkey@1.0.6: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.2.0: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-package-type@0.1.0: {} + + get-stream@6.0.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + + globby@12.2.0: + dependencies: + array-union: 3.0.1 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 4.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graceful-fs@4.2.11: {} + + grapheme-splitter@1.0.4: {} + + graphemer@1.4.0: {} + + handle-thing@2.0.1: {} + + harmony-reflect@1.6.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.2.2 + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-entities@2.5.2: {} + + html-escaper@2.0.2: {} + + http-deceiver@1.2.7: {} + + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-parser-js@0.5.8: {} + + http-proxy-middleware@2.0.6(@types/express@4.17.21): + dependencies: + '@types/http-proxy': 1.17.14 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.7 + optionalDependencies: + '@types/express': 4.17.21 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.6 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + human-signals@2.1.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + identity-obj-proxy@3.0.0: + dependencies: + harmony-reflect: 1.6.2 + + ieee754@1.2.1: {} + + ignore@5.3.1: {} + + image-size@0.5.5: + optional: true + + immer@10.1.1: {} + + immutable@4.3.6: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.1.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.2.0: {} + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.13.1: + dependencies: + hasown: 2.0.2 + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.2.0 + + is-generator-fn@2.1.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-interactive@1.0.0: {} + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@3.0.0: {} + + is-stream@2.0.1: {} + + is-unicode-supported@0.1.0: {} + + is-what@3.14.1: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@1.0.0: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.24.6 + '@babel/parser': 7.24.6 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.2: + dependencies: + '@babel/core': 7.24.6 + '@babel/parser': 7.24.6 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jake@10.9.1: + dependencies: + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + javascript-natural-sort@0.7.1: {} + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + dependencies: + '@babel/core': 7.24.6 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.6) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.7 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.16.9 + ts-node: 10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 18.16.9 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.7 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.24.6 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.7 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + chalk: 4.1.2 + cjs-module-lexer: 1.3.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.24.6 + '@babel/generator': 7.24.6 + '@babel/plugin-syntax-jsx': 7.24.6(@babel/core@7.24.6) + '@babel/plugin-syntax-typescript': 7.24.6(@babel/core@7.24.6) + '@babel/types': 7.24.6 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.6) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.16.9 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@27.5.1: + dependencies: + '@types/node': 18.16.9 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@29.7.0: + dependencies: + '@types/node': 18.16.9 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@0.5.0: {} + + jsesc@2.5.2: {} + + json-buffer@3.0.1: {} + + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-to-ast@2.1.0: + dependencies: + code-error-fragment: 0.0.230 + grapheme-splitter: 1.0.4 + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.0: + dependencies: + acorn: 8.11.3 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.6.2 + + jsonc-parser@3.2.0: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + klona@2.0.6: {} + + kuler@2.0.0: {} + + launch-editor@2.6.1: + dependencies: + picocolors: 1.0.1 + shell-quote: 1.8.1 + + less-loader@11.1.0(less@4.1.3)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + klona: 2.0.6 + less: 4.1.3 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + less@4.1.3: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.6.2 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + license-webpack-plugin@4.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + webpack-sources: 3.2.3 + optionalDependencies: + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + lilconfig@3.1.1: {} + + lines-and-columns@1.2.4: {} + + lines-and-columns@2.0.4: {} + + listr2@8.2.1: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.0.0 + rfdc: 1.3.1 + wrap-ansi: 9.0.0 + + load-json-file@4.0.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + + loader-runner@4.3.0: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + locate-path@2.0.0: + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.debounce@4.0.8: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-update@6.0.0: + dependencies: + ansi-escapes: 6.2.1 + cli-cursor: 4.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + logform@2.6.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.4.1 + + lower-case@2.0.2: + dependencies: + tslib: 2.6.2 + + lru-cache@10.2.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-dir@4.0.0: + dependencies: + semver: 7.6.2 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + media-typer@0.3.0: {} + + memfs@3.5.3: + dependencies: + fs-monkey: 1.0.6 + + merge-descriptors@1.0.1: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + mimic-fn@2.1.0: {} + + mini-css-extract-plugin@2.4.7(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + schema-utils: 4.2.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + minimalistic-assert@1.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.4: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + ms@2.0.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.3.0 + optional: true + + negotiator@0.6.3: {} + + neo-async@2.6.2: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + + node-abort-controller@3.1.1: {} + + node-forge@1.3.1: {} + + node-int64@0.4.0: {} + + node-machine-id@1.1.12: {} + + node-releases@2.0.14: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + npm-package-arg@11.0.1: + dependencies: + hosted-git-info: 7.0.2 + proc-log: 3.0.0 + semver: 7.6.2 + validate-npm-package-name: 5.0.1 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)): + dependencies: + '@nrwl/tao': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + '@yarnpkg/lockfile': 1.1.0 + '@yarnpkg/parsers': 3.0.0-rc.46 + '@zkochan/js-yaml': 0.0.6 + axios: 1.7.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + cliui: 8.0.1 + dotenv: 16.3.2 + dotenv-expand: 10.0.0 + enquirer: 2.3.6 + figures: 3.2.0 + flat: 5.0.2 + fs-extra: 11.2.0 + ignore: 5.3.1 + jest-diff: 29.7.0 + js-yaml: 4.1.0 + jsonc-parser: 3.2.0 + lines-and-columns: 2.0.4 + minimatch: 9.0.3 + node-machine-id: 1.1.12 + npm-run-path: 4.0.1 + open: 8.4.2 + ora: 5.3.0 + semver: 7.6.2 + string-width: 4.2.3 + strong-log-transformer: 2.1.0 + tar-stream: 2.2.0 + tmp: 0.2.3 + tsconfig-paths: 4.2.0 + tslib: 2.6.2 + yargs: 17.7.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@nx/nx-darwin-arm64': 18.3.4 + '@nx/nx-darwin-x64': 18.3.4 + '@nx/nx-freebsd-x64': 18.3.4 + '@nx/nx-linux-arm-gnueabihf': 18.3.4 + '@nx/nx-linux-arm64-gnu': 18.3.4 + '@nx/nx-linux-arm64-musl': 18.3.4 + '@nx/nx-linux-x64-gnu': 18.3.4 + '@nx/nx-linux-x64-musl': 18.3.4 + '@nx/nx-win32-arm64-msvc': 18.3.4 + '@nx/nx-win32-x64-msvc': 18.3.4 + '@swc-node/register': 1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5) + '@swc/core': 1.3.107(@swc/helpers@0.5.11) + transitivePeerDependencies: + - debug + + object-inspect@1.13.1: {} + + obuf@1.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.0.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + openapi-types@12.1.3: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@5.3.0: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + p-limit@1.3.0: + dependencies: + p-try: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.0.0 + + p-locate@2.0.0: + dependencies: + p-limit: 1.3.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-try@1.0.0: {} + + p-try@2.2.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-duration@1.1.0: {} + + parse-json@4.0.0: + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.6 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-node-version@1.0.1: {} + + parse5@4.0.0: {} + + parseurl@1.3.3: {} + + path-exists@3.0.0: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@0.1.7: {} + + path-type@4.0.0: {} + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pify@3.0.0: {} + + pify@4.0.1: + optional: true + + pirates@4.0.6: {} + + pkg-conf@2.1.0: + dependencies: + find-up: 2.1.0 + load-json-file: 4.0.0 + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-dir@7.0.0: + dependencies: + find-up: 6.3.0 + + pluralize@8.0.0: {} + + postcss-calc@9.0.1(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + postcss-value-parser: 4.2.0 + + postcss-colormin@6.1.0(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-convert-values@6.1.0(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-discard-duplicates@6.0.3(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-discard-empty@6.0.3(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-discard-overridden@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-import@14.1.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-loader@6.2.1(postcss@8.4.38)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + cosmiconfig: 7.1.0 + klona: 2.0.6 + postcss: 8.4.38 + semver: 7.6.2 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + postcss-merge-longhand@6.0.5(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + stylehacks: 6.1.1(postcss@8.4.38) + + postcss-merge-rules@6.1.1(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + caniuse-api: 3.0.0 + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + + postcss-minify-font-values@6.1.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@6.0.3(postcss@8.4.38): + dependencies: + colord: 2.9.3 + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-minify-params@6.1.0(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@6.0.4(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + + postcss-modules-extract-imports@3.1.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-modules-local-by-default@4.0.5(postcss@8.4.38): + dependencies: + icss-utils: 5.1.0(postcss@8.4.38) + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + + postcss-modules-values@4.0.0(postcss@8.4.38): + dependencies: + icss-utils: 5.1.0(postcss@8.4.38) + postcss: 8.4.38 + + postcss-normalize-charset@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + + postcss-normalize-display-values@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@6.1.0(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@6.0.2(postcss@8.4.38): + dependencies: + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@6.1.0(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + caniuse-api: 3.0.0 + postcss: 8.4.38 + + postcss-reduce-transforms@6.0.2(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + postcss-selector-parser@6.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@6.0.3(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + svgo: 3.3.2 + + postcss-unique-selectors@6.0.4(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + prelude-ls@1.2.1: {} + + prettier@3.2.5: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + proc-log@3.0.0: {} + + process-nextick-args@2.0.1: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.1.0: {} + + prr@1.0.1: + optional: true + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + qs@6.11.0: + dependencies: + side-channel: 1.0.6 + + qs@6.12.1: + dependencies: + side-channel: 1.0.6 + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + react-is@18.3.1: {} + + react-refresh@0.10.0: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + reflect-metadata@0.1.14: {} + + regenerate-unicode-properties@10.1.1: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.14.1: {} + + regenerator-transform@0.15.2: + dependencies: + '@babel/runtime': 7.24.6 + + regexpu-core@5.3.2: + dependencies: + '@babel/regjsgen': 0.8.0 + regenerate: 1.4.2 + regenerate-unicode-properties: 10.1.1 + regjsparser: 0.9.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.1.0 + + regjsparser@0.9.1: + dependencies: + jsesc: 0.5.0 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve.exports@1.1.0: {} + + resolve.exports@2.0.2: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + retry@0.13.1: {} + + reusify@1.0.4: {} + + rfdc@1.3.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.1: + dependencies: + tslib: 2.6.2 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.4.3: {} + + safer-buffer@2.1.2: {} + + sass-loader@12.6.0(sass@1.77.2)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + klona: 2.0.6 + neo-async: 2.6.2 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + optionalDependencies: + sass: 1.77.2 + + sass@1.77.2: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.6 + source-map-js: 1.2.0 + + sax@1.2.4: {} + + sax@1.3.0: + optional: true + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@4.2.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.14.0 + ajv-formats: 2.1.1(ajv@8.14.0) + ajv-keywords: 5.1.0(ajv@8.14.0) + + select-hose@2.0.0: {} + + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + + semver@5.7.2: + optional: true + + semver@6.3.1: {} + + semver@7.6.2: {} + + send@0.18.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.15.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + setprototypeof@1.1.0: {} + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.1: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + + signal-exit@3.0.7: {} + + signale@1.4.0: + dependencies: + chalk: 2.4.2 + figures: 2.0.0 + pkg-conf: 2.1.0 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slash@4.0.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + slugify@1.6.6: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + + source-map-js@1.2.0: {} + + source-map-loader@3.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + source-map-js: 1.2.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.19: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + spdy-transport@3.0.0: + dependencies: + debug: 4.3.4 + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.3.4 + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + sprintf-js@1.0.3: {} + + stack-trace@0.0.10: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackframe@1.3.4: {} + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.1.0: + dependencies: + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + strong-log-transformer@2.1.0: + dependencies: + duplexer: 0.1.2 + minimist: 1.2.8 + through: 2.3.8 + + style-loader@3.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + stylehacks@6.1.1(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + + stylus-loader@7.1.3(stylus@0.59.0)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + fast-glob: 3.3.2 + normalize-path: 3.0.0 + stylus: 0.59.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + stylus@0.59.0: + dependencies: + '@adobe/css-tools': 4.3.3 + debug: 4.3.4 + glob: 7.2.3 + sax: 1.2.4 + source-map: 0.7.4 + transitivePeerDependencies: + - supports-color + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-parser@2.0.4: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.0.1 + + tapable@2.2.1: {} + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + optionalDependencies: + '@swc/core': 1.3.107(@swc/helpers@0.5.11) + esbuild: 0.19.12 + + terser@5.31.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.11.3 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + text-hex@1.0.0: {} + + text-table@0.2.0: {} + + through@2.3.8: {} + + thunky@1.1.0: {} + + tmp@0.2.3: {} + + tmpl@1.0.5: {} + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + triple-beam@1.4.1: {} + + ts-api-utils@1.3.0(typescript@5.4.5): + dependencies: + typescript: 5.4.5 + + ts-jest@29.1.3(@babel/core@7.24.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.6))(esbuild@0.19.12)(jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)))(typescript@5.4.5): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.2 + typescript: 5.4.5 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.24.6 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.6) + esbuild: 0.19.12 + + ts-loader@9.5.1(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.16.1 + micromatch: 4.0.7 + semver: 7.6.2 + source-map: 0.7.4 + typescript: 5.4.5 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.16.9 + acorn: 8.11.3 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.4.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.3.107(@swc/helpers@0.5.11) + + ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.16.9 + acorn: 8.11.3 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.4.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.3.107(@swc/helpers@0.5.11) + + tsconfig-paths-webpack-plugin@4.0.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.16.1 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.6.2: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@3.13.1: + optional: true + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-assert@1.0.9: {} + + typescript@5.4.5: {} + + unicode-canonical-property-names-ecmascript@2.0.0: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.1.0 + + unicode-match-property-value-ecmascript@2.1.0: {} + + unicode-property-aliases-ecmascript@2.1.0: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.0.16(browserslist@4.23.0): + dependencies: + browserslist: 4.23.0 + escalade: 3.1.2 + picocolors: 1.0.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-loader@4.1.1(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + loader-utils: 2.0.4 + mime-types: 2.1.35 + schema-utils: 3.3.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + uuid@8.3.2: {} + + v8-compile-cache-lib@3.0.1: {} + + v8-to-istanbul@9.2.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-name@5.0.1: {} + + vary@1.1.2: {} + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + watchpack@2.4.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + colorette: 2.0.20 + memfs: 3.5.3 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.2.0 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.21 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.7 + '@types/sockjs': 0.3.36 + '@types/ws': 8.5.10 + ansi-html-community: 0.0.8 + bonjour-service: 1.2.1 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.7.4 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.19.2 + graceful-fs: 4.2.11 + html-entities: 2.5.2 + http-proxy-middleware: 2.0.6(@types/express@4.17.21) + ipaddr.js: 2.2.0 + launch-editor: 2.6.1 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.2.0 + selfsigned: 2.4.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + ws: 8.17.0 + optionalDependencies: + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + webpack-node-externals@3.0.0: {} + + webpack-sources@3.2.3: {} + + webpack-subresource-integrity@5.1.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + dependencies: + typed-assert: 1.0.9 + webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + + webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.11.3 + acorn-import-assertions: 1.9.0(acorn@8.11.3) + browserslist: 4.23.0 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.16.1 + es-module-lexer: 1.5.3 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + watchpack: 2.4.1 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.8 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + winston-transport@4.7.0: + dependencies: + logform: 2.6.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.13.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.5 + is-stream: 2.0.1 + logform: 2.6.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.7.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.1.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + ws@8.17.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@1.10.2: {} + + yaml@2.4.2: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.0.0: {} + + zod-to-json-schema@3.23.0(zod@3.23.8): + dependencies: + zod: 3.23.8 + + zod@3.23.8: {} diff --git a/test/cli/cli_test.go b/test/cli/cli_test.go deleted file mode 100644 index 08acb04c..00000000 --- a/test/cli/cli_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - _ "github.com/api7/adc/test/cli/suites-basic" - _ "github.com/api7/adc/test/cli/suites-consumer" - _ "github.com/api7/adc/test/cli/suites-consumer-group" - _ "github.com/api7/adc/test/cli/suites-global-rule" - _ "github.com/api7/adc/test/cli/suites-plugin-config" - _ "github.com/api7/adc/test/cli/suites-plugin-metadata" - _ "github.com/api7/adc/test/cli/suites-stream-route" - _ "github.com/api7/adc/test/cli/suites-usecase" -) - -func TestADC(t *testing.T) { - gomega.RegisterFailHandler(ginkgo.Fail) - ginkgo.RunSpecs(t, "ADC CLI test suites") -} diff --git a/test/cli/suites-basic/diff.go b/test/cli/suites-basic/diff.go deleted file mode 100644 index 37886051..00000000 --- a/test/cli/suites-basic/diff.go +++ /dev/null @@ -1,24 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ service: "svc1" -+++ service: "svc2" -+++ route: "route1" -+++ route: "route2" -Summary: create 4, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-basic/dump.go b/test/cli/suites-basic/dump.go deleted file mode 100644 index 71c29a6b..00000000 --- a/test/cli/suites-basic/dump.go +++ /dev/null @@ -1,302 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump APISIX resources", func() { - _, err := s.Sync("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`name: "" -routes: -- id: route1 - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -- id: route2 - methods: - - GET - name: route2 - priority: 0 - service_id: svc2 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -- hosts: - - svc.com - id: svc2 - name: svc2 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteService("svc2") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - }) - }) - - ginkgo.Context("Test the dump command with label filter", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump APISIX resources", func() { - _, err := s.Sync("suites-basic/testdata/test-with-labels.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.DumpWithLabels(types.Labels{ - "a": "1", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - a: "1" - mode: partial -name: "" -routes: -- id: route1 - labels: - a: "1" - b: "2" - c: "3" - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -- id: route2 - labels: - a: "1" - b: "2" - methods: - - GET - name: route2 - priority: 0 - service_id: svc2 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - labels: - a: "1" - b: "2" - c: "3" - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -- hosts: - - svc.com - id: svc2 - labels: - a: "1" - b: "2" - name: svc2 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - out, err = s.DumpWithLabels(types.Labels{ - "c": "3", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - c: "3" - mode: partial -name: "" -routes: -- id: route1 - labels: - a: "1" - b: "2" - c: "3" - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - labels: - a: "1" - b: "2" - c: "3" - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - out, err = s.DumpWithLabels(types.Labels{ - "b": "2", - "c": "3", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - b: "2" - c: "3" - mode: partial -name: "" -routes: -- id: route1 - labels: - a: "1" - b: "2" - c: "3" - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - labels: - a: "1" - b: "2" - c: "3" - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - out, err = s.DumpWithLabels(types.Labels{ - "c": "1", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - c: "1" - mode: partial -name: "" -version: "" -`))) - - out, err = s.DumpWithLabels(types.Labels{ - "b": "2", - "c": "1", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - b: "2" - c: "1" - mode: partial -name: "" -version: "" -`))) - - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteService("svc2") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - }) - }) -}) diff --git a/test/cli/suites-basic/ping.go b/test/cli/suites-basic/ping.go deleted file mode 100644 index f4a461a7..00000000 --- a/test/cli/suites-basic/ping.go +++ /dev/null @@ -1,19 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc ping` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should connect to APISIX", func() { - output, err := s.Ping() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(output).To(gomega.Equal("Connected to backend successfully!\n")) - }) - }) -}) diff --git a/test/cli/suites-basic/sdk.go b/test/cli/suites-basic/sdk.go deleted file mode 100644 index 15228b9a..00000000 --- a/test/cli/suites-basic/sdk.go +++ /dev/null @@ -1,132 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("Route resource", func() { - var ( - err error - route *types.Route - ) - - // create service - _, err = s.CreateService(&types.Service{ - ID: "svc1", - Name: "svc1", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "ups1", - Name: "ups1", - Nodes: []types.UpstreamNode{ - { - Host: "foo.com", - Port: 80, - Weight: 1, - }, - }, - }, - }) - gomega.Expect(err).To(gomega.BeNil()) - - // utils - assertRouteEqual := func(expect, toBe *types.Route) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - gomega.Expect(expect.Name).To(gomega.Equal(toBe.Name)) - gomega.Expect(expect.Uri).To(gomega.Equal(toBe.Uri)) - gomega.Expect(expect.ServiceID).To(gomega.Equal(toBe.ServiceID)) - } - - // create route 1 - baseRoute1 := &types.Route{ - ID: "route1", - Name: "route1", - Uri: "/route1", - ServiceID: "svc1", - } - _, err = s.CreateRoute(baseRoute1) - gomega.Expect(err).To(gomega.BeNil(), "check route creation") - - // get route 1 - route, err = s.GetRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - assertRouteEqual(route, baseRoute1) - - // create route 2 - baseRoute2 := &types.Route{ - ID: "route2", - Name: "route2", - Uri: "/route2", - ServiceID: "svc1", - } - route, err = s.CreateRoute(baseRoute2) - gomega.Expect(err).To(gomega.BeNil()) - assertRouteEqual(route, baseRoute2) - - // test list - routes, err := s.ListRoute() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(routes)).To(gomega.Equal(2)) - var route1, route2 *types.Route - for _, r := range routes { - if r.ID == "route1" { - route1 = r - } else if r.ID == "route2" { - route2 = r - } - } - gomega.Expect(route1).NotTo(gomega.BeNil()) - gomega.Expect(route2).NotTo(gomega.BeNil()) - - assertRouteEqual(route1, baseRoute1) - assertRouteEqual(route2, baseRoute2) - - // update & get route 1 - baseRoute1 = &types.Route{ - ID: "route1", - Name: "route1", - Uri: "/route1-updated", - ServiceID: "svc1", - } - _, err = s.UpdateRoute(baseRoute1) - gomega.Expect(err).To(gomega.BeNil()) - - route, err = s.GetRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - assertRouteEqual(route, baseRoute1) - - // delete route 2 - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetRoute("route2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete route 1 - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetRoute("route1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - routes, err = s.ListRoute() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(routes)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-basic/sync.go b/test/cli/suites-basic/sync.go deleted file mode 100644 index 1ca9975a..00000000 --- a/test/cli/suites-basic/sync.go +++ /dev/null @@ -1,281 +0,0 @@ -package basic - -import ( - "net/http" - - "github.com/gavv/httpexpect/v2" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - var ( - service = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - service1 = &types.Service{ - ID: "svc1", - Name: "svc1", - Hosts: []string{ - "bar.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc", - } - - route1 = &types.Route{ - ID: "route1", - Name: "route1", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc1", - } - ) - - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - _, err = s.UpdateService(service1) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route1) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "bar.com"`) - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo.com"`) - - /* - service is deleted - service1 is updated - service2 is created - route is deleted - route1 is updated - route2 is created - */ - output, err := s.Sync("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 2, updated 2, deleted 2")) - - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("foo1.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo1.com"`) - expect.GET("/get").WithHost("svc.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "svc.com"`) - - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteService("svc2") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - expect.GET("/get").WithHost("foo1.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("svc.com").Expect().Status(http.StatusNotFound) - }) - }) - - ginkgo.Context("Test the sync command order", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo.com"`) - _, err = s.UpdateService(service1) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - - // Now we have two services and one route in APISIX - // route => svc, and svc1 - // we delete svc1 and update the route to reference svc1 - output, err := s.Sync("suites-basic/testdata/test2.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 0, updated 2, deleted 1")) - - expect.GET("/get").WithHost("svc.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "svc.com"`) - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - - err = s.DeleteRoute("route") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check svc delete") - }) - }) - - ginkgo.Context("Test sync in partial mode", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo.com"`) - _, err = s.UpdateService(service1) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - - output, err := s.Sync("suites-basic/testdata/test2.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 0, updated 2, deleted 1")) - - output, err = s.Sync("suites-basic/testdata/test-partial.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 2, updated 0, deleted 0")) - }) - }) - - ginkgo.Context("Test sync in multiple file mode", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - output, err := s.Sync("suites-basic/testdata/test.yaml", "suites-basic/testdata/test-partial.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 6, updated 0, deleted 0")) - }) - }) - - ginkgo.Context("Test sync with common labels", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - output, err := s.Sync("suites-basic/testdata/test-with-common-labels.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 4, updated 0, deleted 0")) - - out, err := s.DumpWithLabels(types.Labels{ - "a": "1", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - a: "1" - mode: partial -name: "" -routes: -- id: route1 - labels: - a: "1" - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -- id: route2 - labels: - a: "1" - methods: - - GET - name: route2 - priority: 0 - service_id: svc2 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - labels: - a: "1" - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -- hosts: - - svc.com - id: svc2 - labels: - a: "1" - name: svc2 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - out, err = s.DumpWithLabels(types.Labels{ - "c": "1", - }) - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`meta: - labels: - c: "1" - mode: partial -name: "" -version: "" -`))) - - }) - }) -}) diff --git a/test/cli/suites-basic/testdata/test-partial.yaml b/test/cli/suites-basic/testdata/test-partial.yaml deleted file mode 100644 index 8ad40d01..00000000 --- a/test/cli/suites-basic/testdata/test-partial.yaml +++ /dev/null @@ -1,31 +0,0 @@ -meta: - mode: partial -name: "" -routes: - - id: route3 - methods: - - GET - - PUT - name: route3 - priority: 0 - service_id: svc3 - status: 1 - uri: /get -services: - - hosts: - - foo1.com - id: svc3 - name: svc3 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: httpbin.org - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" diff --git a/test/cli/suites-basic/testdata/test-with-common-labels.yaml b/test/cli/suites-basic/testdata/test-with-common-labels.yaml deleted file mode 100644 index f0b7f48c..00000000 --- a/test/cli/suites-basic/testdata/test-with-common-labels.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: "test" -version: "1.0.0" -meta: - labels: - a: "1" -services: - - name: svc1 - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 - - name: svc2 - hosts: - - svc.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route1 - service_id: svc1 - uri: "/get" - methods: - - GET - - PUT - - name: route2 - service_id: svc2 - uri: "/get" - methods: - - GET diff --git a/test/cli/suites-basic/testdata/test-with-labels.yaml b/test/cli/suites-basic/testdata/test-with-labels.yaml deleted file mode 100644 index d741d73a..00000000 --- a/test/cli/suites-basic/testdata/test-with-labels.yaml +++ /dev/null @@ -1,49 +0,0 @@ -name: "test" -version: "1.0.0" -services: - - name: svc1 - labels: - a: "1" - b: "2" - c: "3" - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 - - name: svc2 - labels: - a: "1" - b: "2" - hosts: - - svc.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route1 - labels: - a: "1" - b: "2" - c: "3" - service_id: svc1 - uri: "/get" - methods: - - GET - - PUT - - name: route2 - labels: - a: "1" - b: "2" - service_id: svc2 - uri: "/get" - methods: - - GET diff --git a/test/cli/suites-basic/testdata/test.yaml b/test/cli/suites-basic/testdata/test.yaml deleted file mode 100644 index ac423652..00000000 --- a/test/cli/suites-basic/testdata/test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: "test" -version: "1.0.0" -services: - - name: svc1 - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 - - name: svc2 - hosts: - - svc.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route1 - service_id: svc1 - uri: "/get" - methods: - - GET - - PUT - - name: route2 - service_id: svc2 - uri: "/get" - methods: - - GET diff --git a/test/cli/suites-basic/testdata/test2.yaml b/test/cli/suites-basic/testdata/test2.yaml deleted file mode 100644 index 5a44d767..00000000 --- a/test/cli/suites-basic/testdata/test2.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: "test2" -version: "1.0.0" -services: - - name: svc1 - hosts: - - svc.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route - service_id: svc1 - uri: "/get" - methods: - - GET diff --git a/test/cli/suites-basic/validate.go b/test/cli/suites-basic/validate.go deleted file mode 100644 index 3a32d084..00000000 --- a/test/cli/suites-basic/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate schema", func() { - validateOutput, err := s.Validate("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: test, version: 1.0.0, routes: 2, services: 2.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/cli/suites-consumer-group/diff.go b/test/cli/suites-consumer-group/diff.go deleted file mode 100644 index 66e0d473..00000000 --- a/test/cli/suites-consumer-group/diff.go +++ /dev/null @@ -1,22 +0,0 @@ -package consumer_group - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` consumer group tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-consumer-group/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ consumer_group: "company_a" -+++ consumer: "jack" -Summary: create 2, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-consumer-group/dump.go b/test/cli/suites-consumer-group/dump.go deleted file mode 100644 index 3e4dd534..00000000 --- a/test/cli/suites-consumer-group/dump.go +++ /dev/null @@ -1,50 +0,0 @@ -package consumer_group - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` consumer group tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump consumer group resources", func() { - _, err := s.Sync("suites-consumer-group/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`consumer_groups: -- id: company_a - plugins: - limit-count: - allow_degradation: false - count: 200 - group: $consumer_group_id - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 -consumers: -- plugins: - key-auth: - header: apikey - hide_credentials: false - key: auth-one - query: apikey - username: jack -name: "" -version: "" -`)) - - err = s.DeleteConsumer("jack") - gomega.Expect(err).To(gomega.BeNil()) - err = s.DeleteConsumerGroup("company_a") - gomega.Expect(err).To(gomega.BeNil(), "check consumer group delete") - }) - }) -}) diff --git a/test/cli/suites-consumer-group/sdk.go b/test/cli/suites-consumer-group/sdk.go deleted file mode 100644 index 11bf8590..00000000 --- a/test/cli/suites-consumer-group/sdk.go +++ /dev/null @@ -1,126 +0,0 @@ -package consumer_group - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX consumerGroup SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("ConsumerGroup resource", func() { - var ( - err error - consumerGroup *types.ConsumerGroup - ) - - // utils - assertConsumerGroupEqual := func(expect, toBe *types.ConsumerGroup, plugins ...string) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - gomega.Expect(expect.Desc).To(gomega.Equal(toBe.Desc)) - for _, plugin := range plugins { - gomega.Expect(expect.Plugins[plugin]).NotTo(gomega.BeNil()) - } - } - - // create consumerGroup 1 - baseConsumerGroup1 := &types.ConsumerGroup{ - ID: "consumerGroup1", - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "time_window": 60, - "policy": "local", - "count": 100, - "key": "remote_addr", - "rejected_code": 503, - }, - }, - } - _, err = s.CreateConsumerGroup(baseConsumerGroup1) - gomega.Expect(err).To(gomega.BeNil()) - - // get consumerGroup 1 - consumerGroup, err = s.GetConsumerGroup("consumerGroup1") - gomega.Expect(err).To(gomega.BeNil()) - assertConsumerGroupEqual(consumerGroup, baseConsumerGroup1, "limit-count") - - // create consumerGroup 2 - baseConsumerGroup2 := &types.ConsumerGroup{ - ID: "consumerGroup2", - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "time_window": 60, - "policy": "local", - "count": 200, - "key": "remote_addr", - "rejected_code": 503, - }, - }, - } - consumerGroup, err = s.CreateConsumerGroup(baseConsumerGroup2) - gomega.Expect(err).To(gomega.BeNil()) - assertConsumerGroupEqual(consumerGroup, baseConsumerGroup2, "limit-count") - - // test list - consumerGroups, err := s.ListConsumerGroup() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(consumerGroups)).To(gomega.Equal(2)) - var consumerGroup1, consumerGroup2 *types.ConsumerGroup - for _, r := range consumerGroups { - if r.ID == "consumerGroup1" { - consumerGroup1 = r - } else if r.ID == "consumerGroup2" { - consumerGroup2 = r - } - } - gomega.Expect(consumerGroup1).NotTo(gomega.BeNil()) - gomega.Expect(consumerGroup2).NotTo(gomega.BeNil()) - - assertConsumerGroupEqual(consumerGroup1, baseConsumerGroup1, "limit-count") - assertConsumerGroupEqual(consumerGroup2, baseConsumerGroup2, "limit-count") - - // update & get consumerGroup 1 - baseConsumerGroup1 = &types.ConsumerGroup{ - ID: "consumerGroup1", - Plugins: types.Plugins{ - "key-auth": types.Plugin{ - "key": "auth-one", - }, - }, - } - _, err = s.UpdateConsumerGroup(baseConsumerGroup1) - gomega.Expect(err).To(gomega.BeNil()) - - consumerGroup, err = s.GetConsumerGroup("consumerGroup1") - gomega.Expect(err).To(gomega.BeNil()) - assertConsumerGroupEqual(consumerGroup, baseConsumerGroup1, "key-auth") - - // delete consumerGroup 2 - err = s.DeleteConsumerGroup("consumerGroup2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetConsumerGroup("consumerGroup2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete consumerGroup 1 - err = s.DeleteConsumerGroup("consumerGroup1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetConsumerGroup("consumerGroup1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - consumerGroups, err = s.ListConsumerGroup() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(consumerGroups)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-consumer-group/sync.go b/test/cli/suites-consumer-group/sync.go deleted file mode 100644 index f779fae1..00000000 --- a/test/cli/suites-consumer-group/sync.go +++ /dev/null @@ -1,119 +0,0 @@ -package consumer_group - -import ( - "net/http" - "time" - - "github.com/gavv/httpexpect/v2" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - var ( - id = "consumerGroup1" - user = "jack" - authKey = "auth-one" - - consumerGroup = &types.ConsumerGroup{ - ID: id, - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr", - }, - }, - } - - consumer = &types.Consumer{ - Username: user, - Plugins: types.Plugins{ - "key-auth": types.Plugin{ - "key": authKey, - }, - }, - GroupID: id, - } - - service = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc", - Plugins: types.Plugins{ - "key-auth": types.Plugin{}, - }, - } - ) - - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateConsumerGroup(consumerGroup) - gomega.Expect(err).To(gomega.BeNil(), "check consumerGroup update") - _, err = s.UpdateConsumer(consumer) - gomega.Expect(err).To(gomega.BeNil(), "check consumer update") - _, err = s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - time.Sleep(time.Second * 1) - - resp := expect.GET("/get").WithHeader("apikey", authKey). - WithHost("foo.com").Expect() - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("1") - - resp = expect.GET("/get").WithHeader("apikey", authKey). - WithHost("foo.com").Expect().Status(http.StatusOK) - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - resp = expect.GET("/get").WithHeader("apikey", authKey). - WithHost("foo.com").Expect() - resp.Status(http.StatusServiceUnavailable) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - err = s.DeleteRoute("route") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteConsumer(user) - gomega.Expect(err).To(gomega.BeNil(), "check consumer delete") - err = s.DeleteConsumerGroup(id) - gomega.Expect(err).To(gomega.BeNil(), "check consumerGroup delete") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - }) - }) -}) diff --git a/test/cli/suites-consumer-group/testdata/test.yaml b/test/cli/suites-consumer-group/testdata/test.yaml deleted file mode 100644 index 74adaefd..00000000 --- a/test/cli/suites-consumer-group/testdata/test.yaml +++ /dev/null @@ -1,18 +0,0 @@ -consumer_groups: - - id: company_a - plugins: - limit-count: - allow_degradation: false - count: 200 - group: $consumer_group_id - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 -consumers: - - plugins: - key-auth: - key: auth-one - username: jack diff --git a/test/cli/suites-consumer-group/validate.go b/test/cli/suites-consumer-group/validate.go deleted file mode 100644 index 49dd2d32..00000000 --- a/test/cli/suites-consumer-group/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package consumer_group - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` consumer group tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate consumer group schema", func() { - validateOutput, err := s.Validate("suites-consumer-group/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: , version: , consumers: 1, consumer_groups: 1.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/cli/suites-consumer/diff.go b/test/cli/suites-consumer/diff.go deleted file mode 100644 index c8a16ed6..00000000 --- a/test/cli/suites-consumer/diff.go +++ /dev/null @@ -1,21 +0,0 @@ -package consumer - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` consumer tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-consumer/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ consumer: "jack" -Summary: create 1, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-consumer/dump.go b/test/cli/suites-consumer/dump.go deleted file mode 100644 index 72b1f67f..00000000 --- a/test/cli/suites-consumer/dump.go +++ /dev/null @@ -1,44 +0,0 @@ -package consumer - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` consumer tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump consumer resources", func() { - _, err := s.Sync("suites-consumer/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`consumers: -- plugins: - key-auth: - header: apikey - hide_credentials: false - key: auth-one - query: apikey - limit-count: - allow_degradation: false - count: 2 - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 - username: jack -name: "" -version: "" -`)) - - err = s.DeleteConsumer("jack") - gomega.Expect(err).To(gomega.BeNil(), "check consumer delete") - }) - }) -}) diff --git a/test/cli/suites-consumer/sdk.go b/test/cli/suites-consumer/sdk.go deleted file mode 100644 index a7425b04..00000000 --- a/test/cli/suites-consumer/sdk.go +++ /dev/null @@ -1,107 +0,0 @@ -package consumer - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX consumer SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("Consumer resource", func() { - var ( - err error - consumer *types.Consumer - ) - - // utils - assertConsumerEqual := func(expect, toBe *types.Consumer, plugins ...string) { - gomega.Expect(expect.Username).To(gomega.Equal(toBe.Username)) - for _, plugin := range plugins { - gomega.Expect(expect.Plugins[plugin]).NotTo(gomega.BeNil()) - } - } - - // create consumer 1 - baseConsumer1 := &types.Consumer{ - Username: "consumer1", - } - _, err = s.CreateConsumer(baseConsumer1) - gomega.Expect(err).To(gomega.BeNil(), "error while creating consumer") - - // get consumer 1 - consumer, err = s.GetConsumer("consumer1") - gomega.Expect(err).To(gomega.BeNil()) - assertConsumerEqual(consumer, baseConsumer1) - - // create consumer 2 - baseConsumer2 := &types.Consumer{ - Username: "consumer2", - } - consumer, err = s.CreateConsumer(baseConsumer2) - gomega.Expect(err).To(gomega.BeNil()) - assertConsumerEqual(consumer, baseConsumer2) - - // test list - consumers, err := s.ListConsumer() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(consumers)).To(gomega.Equal(2)) - var consumer1, consumer2 *types.Consumer - for _, r := range consumers { - if r.Username == "consumer1" { - consumer1 = r - } else if r.Username == "consumer2" { - consumer2 = r - } - } - gomega.Expect(consumer1).NotTo(gomega.BeNil()) - gomega.Expect(consumer2).NotTo(gomega.BeNil()) - - assertConsumerEqual(consumer1, baseConsumer1) - assertConsumerEqual(consumer2, baseConsumer2) - - // update & get consumer 1 - baseConsumer1 = &types.Consumer{ - Username: "consumer1", - Plugins: types.Plugins{ - "key-auth": types.Plugin{ - "key": "auth-one", - }, - }, - } - _, err = s.UpdateConsumer(baseConsumer1) - gomega.Expect(err).To(gomega.BeNil()) - - consumer, err = s.GetConsumer("consumer1") - gomega.Expect(err).To(gomega.BeNil()) - assertConsumerEqual(consumer, baseConsumer1, "key-auth") - - // delete consumer 2 - err = s.DeleteConsumer("consumer2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetConsumer("consumer2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete consumer 1 - err = s.DeleteConsumer("consumer1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetConsumer("consumer1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - consumers, err = s.ListConsumer() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(consumers)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-consumer/sync.go b/test/cli/suites-consumer/sync.go deleted file mode 100644 index 63903ed6..00000000 --- a/test/cli/suites-consumer/sync.go +++ /dev/null @@ -1,108 +0,0 @@ -package consumer - -import ( - "net/http" - "time" - - "github.com/gavv/httpexpect/v2" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - var ( - user = "jack" - authKey = "auth-one" - - consumer = &types.Consumer{ - Username: user, - Plugins: types.Plugins{ - "key-auth": types.Plugin{ - "key": authKey, - }, - "limit-count": types.Plugin{ - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr", - "_vid": "suites-consumer", - }, - }, - } - - service = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc", - Plugins: types.Plugins{ - "key-auth": types.Plugin{}, - }, - } - ) - - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateConsumer(consumer) - gomega.Expect(err).To(gomega.BeNil(), "check consumer update") - _, err = s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - time.Sleep(time.Second * 1) - - resp := expect.GET("/get").WithHeader("apikey", authKey). - WithHost("foo.com").Expect() - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("1") - - resp = expect.GET("/get").WithHeader("apikey", authKey). - WithHost("foo.com").Expect().Status(http.StatusOK) - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - resp = expect.GET("/get").WithHeader("apikey", authKey). - WithHost("foo.com").Expect() - resp.Status(http.StatusServiceUnavailable) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - err = s.DeleteRoute("route") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteConsumer(user) - gomega.Expect(err).To(gomega.BeNil(), "check consumer delete") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - }) - }) -}) diff --git a/test/cli/suites-consumer/testdata/test.yaml b/test/cli/suites-consumer/testdata/test.yaml deleted file mode 100644 index f56cf049..00000000 --- a/test/cli/suites-consumer/testdata/test.yaml +++ /dev/null @@ -1,14 +0,0 @@ -consumers: - - plugins: - key-auth: - key: auth-one - limit-count: - allow_degradation: false - count: 2 - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 - username: jack diff --git a/test/cli/suites-consumer/validate.go b/test/cli/suites-consumer/validate.go deleted file mode 100644 index 956ce1e2..00000000 --- a/test/cli/suites-consumer/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package consumer - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` consumer tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate consumer schema", func() { - validateOutput, err := s.Validate("suites-consumer/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: , version: , consumers: 1.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/cli/suites-global-rule/diff.go b/test/cli/suites-global-rule/diff.go deleted file mode 100644 index 60a3a5cb..00000000 --- a/test/cli/suites-global-rule/diff.go +++ /dev/null @@ -1,21 +0,0 @@ -package global_rule - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` global rule tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-global-rule/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ global_rule: "1" -Summary: create 1, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-global-rule/dump.go b/test/cli/suites-global-rule/dump.go deleted file mode 100644 index e1bd6d53..00000000 --- a/test/cli/suites-global-rule/dump.go +++ /dev/null @@ -1,39 +0,0 @@ -package global_rule - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` global rule tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump global rule resources", func() { - _, err := s.Sync("suites-global-rule/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`global_rules: -- id: "1" - plugins: - limit-count: - allow_degradation: false - count: 2 - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 -name: "" -version: "" -`)) - - err = s.DeleteGlobalRule("1") - gomega.Expect(err).To(gomega.BeNil(), "check global rule delete") - }) - }) -}) diff --git a/test/cli/suites-global-rule/sdk.go b/test/cli/suites-global-rule/sdk.go deleted file mode 100644 index 51b42dc2..00000000 --- a/test/cli/suites-global-rule/sdk.go +++ /dev/null @@ -1,125 +0,0 @@ -package global_rule - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX globalRule SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("GlobalRule resource", func() { - var ( - err error - globalRule *types.GlobalRule - ) - - // utils - assertGlobalRuleEqual := func(expect, toBe *types.GlobalRule, plugins ...string) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - for _, plugin := range plugins { - gomega.Expect(expect.Plugins[plugin]).NotTo(gomega.BeNil()) - } - } - - // create globalRule 1 - baseGlobalRule1 := &types.GlobalRule{ - ID: "globalRule1", - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "time_window": 60, - "policy": "local", - "count": 100, - "key": "remote_addr", - "rejected_code": 503, - }, - }, - } - _, err = s.CreateGlobalRule(baseGlobalRule1) - gomega.Expect(err).To(gomega.BeNil()) - - // get globalRule 1 - globalRule, err = s.GetGlobalRule("globalRule1") - gomega.Expect(err).To(gomega.BeNil()) - assertGlobalRuleEqual(globalRule, baseGlobalRule1, "limit-count") - - // create globalRule 2 - baseGlobalRule2 := &types.GlobalRule{ - ID: "globalRule2", - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "time_window": 60, - "policy": "local", - "count": 200, - "key": "remote_addr", - "rejected_code": 503, - }, - }, - } - globalRule, err = s.CreateGlobalRule(baseGlobalRule2) - gomega.Expect(err).To(gomega.BeNil()) - assertGlobalRuleEqual(globalRule, baseGlobalRule2, "limit-count") - - // test list - globalRules, err := s.ListGlobalRule() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(globalRules)).To(gomega.Equal(2)) - var globalRule1, globalRule2 *types.GlobalRule - for _, r := range globalRules { - if r.ID == "globalRule1" { - globalRule1 = r - } else if r.ID == "globalRule2" { - globalRule2 = r - } - } - gomega.Expect(globalRule1).NotTo(gomega.BeNil()) - gomega.Expect(globalRule2).NotTo(gomega.BeNil()) - - assertGlobalRuleEqual(globalRule1, baseGlobalRule1, "limit-count") - assertGlobalRuleEqual(globalRule2, baseGlobalRule2, "limit-count") - - // update & get globalRule 1 - baseGlobalRule1 = &types.GlobalRule{ - ID: "globalRule1", - Plugins: types.Plugins{ - "key-auth": types.Plugin{ - "key": "auth-one", - }, - }, - } - _, err = s.UpdateGlobalRule(baseGlobalRule1) - gomega.Expect(err).To(gomega.BeNil()) - - globalRule, err = s.GetGlobalRule("globalRule1") - gomega.Expect(err).To(gomega.BeNil()) - assertGlobalRuleEqual(globalRule, baseGlobalRule1, "key-auth") - - // delete globalRule 2 - err = s.DeleteGlobalRule("globalRule2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetGlobalRule("globalRule2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete globalRule 1 - err = s.DeleteGlobalRule("globalRule1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetGlobalRule("globalRule1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - globalRules, err = s.ListGlobalRule() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(globalRules)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-global-rule/sync.go b/test/cli/suites-global-rule/sync.go deleted file mode 100644 index a7817043..00000000 --- a/test/cli/suites-global-rule/sync.go +++ /dev/null @@ -1,100 +0,0 @@ -package global_rule - -import ( - "net/http" - "time" - - "github.com/gavv/httpexpect/v2" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - var ( - id = "globalRule1" - - globalRule = &types.GlobalRule{ - ID: id, - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr", - }, - }, - } - - service = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc", - } - ) - - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateGlobalRule(globalRule) - gomega.Expect(err).To(gomega.BeNil(), "check globalRule update") - _, err = s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - time.Sleep(time.Second * 1) - - resp := expect.GET("/get"). - WithHost("foo.com").Expect() - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("1") - - resp = expect.GET("/get"). - WithHost("foo.com").Expect().Status(http.StatusOK) - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - resp = expect.GET("/get"). - WithHost("foo.com").Expect() - resp.Status(http.StatusServiceUnavailable) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - err = s.DeleteRoute("route") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteGlobalRule(id) - gomega.Expect(err).To(gomega.BeNil(), "check globalRule delete") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - }) - }) -}) diff --git a/test/cli/suites-global-rule/testdata/test.yaml b/test/cli/suites-global-rule/testdata/test.yaml deleted file mode 100644 index c797d0ad..00000000 --- a/test/cli/suites-global-rule/testdata/test.yaml +++ /dev/null @@ -1,14 +0,0 @@ -global_rules: - - id: "1" - plugins: - limit-count: - allow_degradation: false - count: 2 - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 -name: "" -version: "" diff --git a/test/cli/suites-global-rule/validate.go b/test/cli/suites-global-rule/validate.go deleted file mode 100644 index 7d1fdc47..00000000 --- a/test/cli/suites-global-rule/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package global_rule - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` global rule tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate global rule schema", func() { - validateOutput, err := s.Validate("suites-global-rule/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: , version: , global_rules: 1.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/cli/suites-plugin-config/diff.go b/test/cli/suites-plugin-config/diff.go deleted file mode 100644 index 52080ba1..00000000 --- a/test/cli/suites-plugin-config/diff.go +++ /dev/null @@ -1,21 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` plugin config tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-plugin-config/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ plugin_config: "1" -Summary: create 1, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-plugin-config/dump.go b/test/cli/suites-plugin-config/dump.go deleted file mode 100644 index d335efa9..00000000 --- a/test/cli/suites-plugin-config/dump.go +++ /dev/null @@ -1,40 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` plugin config tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump plugin config resources", func() { - _, err := s.Sync("suites-plugin-config/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`name: "" -plugin_configs: -- desc: enable limit-count plugin - id: "1" - plugins: - limit-count: - allow_degradation: false - count: 2 - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 -version: "" -`)) - - err = s.DeletePluginConfig("1") - gomega.Expect(err).To(gomega.BeNil(), "check plugin config delete") - }) - }) -}) diff --git a/test/cli/suites-plugin-config/sdk.go b/test/cli/suites-plugin-config/sdk.go deleted file mode 100644 index 97c69495..00000000 --- a/test/cli/suites-plugin-config/sdk.go +++ /dev/null @@ -1,126 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX pluginConfig SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("PluginConfig resource", func() { - var ( - err error - pluginConfig *types.PluginConfig - ) - - // utils - assertPluginConfigEqual := func(expect, toBe *types.PluginConfig, plugins ...string) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - gomega.Expect(expect.Desc).To(gomega.Equal(toBe.Desc)) - for _, plugin := range plugins { - gomega.Expect(expect.Plugins[plugin]).NotTo(gomega.BeNil()) - } - } - - // create pluginConfig 1 - basePluginConfig1 := &types.PluginConfig{ - ID: "pluginConfig1", - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "time_window": 60, - "policy": "local", - "count": 100, - "key": "remote_addr", - "rejected_code": 503, - }, - }, - } - _, err = s.CreatePluginConfig(basePluginConfig1) - gomega.Expect(err).To(gomega.BeNil()) - - // get pluginConfig 1 - pluginConfig, err = s.GetPluginConfig("pluginConfig1") - gomega.Expect(err).To(gomega.BeNil()) - assertPluginConfigEqual(pluginConfig, basePluginConfig1, "limit-count") - - // create pluginConfig 2 - basePluginConfig2 := &types.PluginConfig{ - ID: "pluginConfig2", - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "time_window": 60, - "policy": "local", - "count": 200, - "key": "remote_addr", - "rejected_code": 503, - }, - }, - } - pluginConfig, err = s.CreatePluginConfig(basePluginConfig2) - gomega.Expect(err).To(gomega.BeNil()) - assertPluginConfigEqual(pluginConfig, basePluginConfig2, "limit-count") - - // test list - pluginConfigs, err := s.ListPluginConfig() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(pluginConfigs)).To(gomega.Equal(2)) - var pluginConfig1, pluginConfig2 *types.PluginConfig - for _, r := range pluginConfigs { - if r.ID == "pluginConfig1" { - pluginConfig1 = r - } else if r.ID == "pluginConfig2" { - pluginConfig2 = r - } - } - gomega.Expect(pluginConfig1).NotTo(gomega.BeNil()) - gomega.Expect(pluginConfig2).NotTo(gomega.BeNil()) - - assertPluginConfigEqual(pluginConfig1, basePluginConfig1, "limit-count") - assertPluginConfigEqual(pluginConfig2, basePluginConfig2, "limit-count") - - // update & get pluginConfig 1 - basePluginConfig1 = &types.PluginConfig{ - ID: "pluginConfig1", - Plugins: types.Plugins{ - "key-auth": types.Plugin{ - "key": "auth-one", - }, - }, - } - _, err = s.UpdatePluginConfig(basePluginConfig1) - gomega.Expect(err).To(gomega.BeNil()) - - pluginConfig, err = s.GetPluginConfig("pluginConfig1") - gomega.Expect(err).To(gomega.BeNil()) - assertPluginConfigEqual(pluginConfig, basePluginConfig1, "key-auth") - - // delete pluginConfig 2 - err = s.DeletePluginConfig("pluginConfig2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetPluginConfig("pluginConfig2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete pluginConfig 1 - err = s.DeletePluginConfig("pluginConfig1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetPluginConfig("pluginConfig1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - pluginConfigs, err = s.ListPluginConfig() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(pluginConfigs)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-plugin-config/sync.go b/test/cli/suites-plugin-config/sync.go deleted file mode 100644 index b914538b..00000000 --- a/test/cli/suites-plugin-config/sync.go +++ /dev/null @@ -1,101 +0,0 @@ -package plugin_config - -import ( - "net/http" - "time" - - "github.com/gavv/httpexpect/v2" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - var ( - id = "pluginConfig1" - - pluginConfig = &types.PluginConfig{ - ID: id, - Plugins: types.Plugins{ - "limit-count": types.Plugin{ - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr", - }, - }, - } - - service = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc", - PluginConfigID: id, - } - ) - - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdatePluginConfig(pluginConfig) - gomega.Expect(err).To(gomega.BeNil(), "check pluginConfig update") - _, err = s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - time.Sleep(time.Second * 1) - - resp := expect.GET("/get"). - WithHost("foo.com").Expect() - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("1") - - resp = expect.GET("/get"). - WithHost("foo.com").Expect().Status(http.StatusOK) - resp.Status(http.StatusOK) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - resp = expect.GET("/get"). - WithHost("foo.com").Expect() - resp.Status(http.StatusServiceUnavailable) - resp.Header("X-Ratelimit-Remaining").IsEqual("0") - - err = s.DeleteRoute("route") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeletePluginConfig(id) - gomega.Expect(err).To(gomega.BeNil(), "check pluginConfig delete") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - }) - }) -}) diff --git a/test/cli/suites-plugin-config/testdata/test.yaml b/test/cli/suites-plugin-config/testdata/test.yaml deleted file mode 100644 index bce34d9b..00000000 --- a/test/cli/suites-plugin-config/testdata/test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -plugin_configs: - - desc: enable limit-count plugin - id: "1" - plugins: - limit-count: - allow_degradation: false - count: 2 - key: remote_addr - key_type: var - policy: local - rejected_code: 503 - show_limit_quota_header: true - time_window: 60 diff --git a/test/cli/suites-plugin-config/validate.go b/test/cli/suites-plugin-config/validate.go deleted file mode 100644 index 83443def..00000000 --- a/test/cli/suites-plugin-config/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` plugin config tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate plugin config schema", func() { - validateOutput, err := s.Validate("suites-plugin-config/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: , version: , plugin_configs: 1.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/cli/suites-plugin-metadata/diff.go b/test/cli/suites-plugin-metadata/diff.go deleted file mode 100644 index 34882d84..00000000 --- a/test/cli/suites-plugin-metadata/diff.go +++ /dev/null @@ -1,21 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` plugin metadata tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-plugin-metadata/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ plugin_metadata: "http-logger" -Summary: create 1, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-plugin-metadata/dump.go b/test/cli/suites-plugin-metadata/dump.go deleted file mode 100644 index 5a740c3c..00000000 --- a/test/cli/suites-plugin-metadata/dump.go +++ /dev/null @@ -1,33 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` plugin metadata tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump plugin metadata resources", func() { - _, err := s.Sync("suites-plugin-metadata/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`name: "" -plugin_metadatas: -- id: http-logger - log_format: - '@timestamp': $time_iso8601 - client_ip: $remote_addr - host: $host -version: "" -`)) - - err = s.DeletePluginConfig("1") - gomega.Expect(err).To(gomega.BeNil(), "check plugin metadata delete") - }) - }) -}) diff --git a/test/cli/suites-plugin-metadata/sdk.go b/test/cli/suites-plugin-metadata/sdk.go deleted file mode 100644 index 72c10ed4..00000000 --- a/test/cli/suites-plugin-metadata/sdk.go +++ /dev/null @@ -1,119 +0,0 @@ -package plugin_config - -import ( - "reflect" - - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX pluginMetadata SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("PluginMetadata resource", func() { - var ( - err error - pluginMetadata *types.PluginMetadata - ) - - // utils - assertLoggerPluginMetadataEqual := func(expect, toBe *types.PluginMetadata, plugins ...string) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - gomega.Expect(reflect.DeepEqual(expect.Config, toBe.Config)).To(gomega.BeTrue()) - } - - loggerConfig := map[string]interface{}{ - "log_format": map[string]interface{}{ - "host": "$host", - "@timestamp": "$time_iso8601", - "client_ip": "$remote_addr", - }, - } - - // create http-logger - baseHttpLogger := &types.PluginMetadata{ - ID: "http-logger", - Config: loggerConfig, - } - _, err = s.CreatePluginMetadata(baseHttpLogger) - gomega.Expect(err).To(gomega.BeNil()) - - // get http-logger - pluginMetadata, err = s.GetPluginMetadata("http-logger") - gomega.Expect(err).To(gomega.BeNil()) - assertLoggerPluginMetadataEqual(pluginMetadata, baseHttpLogger, "limit-count") - - // create skywalking-logger - baseSkywalkingLogger := &types.PluginMetadata{ - ID: "skywalking-logger", - Config: loggerConfig, - } - pluginMetadata, err = s.CreatePluginMetadata(baseSkywalkingLogger) - gomega.Expect(err).To(gomega.BeNil()) - assertLoggerPluginMetadataEqual(pluginMetadata, baseSkywalkingLogger, "limit-count") - - // test list - pluginMetadatas, err := s.ListPluginMetadata() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(pluginMetadatas)).To(gomega.Equal(2)) - var pluginMetadata1, pluginMetadata2 *types.PluginMetadata - for _, r := range pluginMetadatas { - if r.ID == "http-logger" { - pluginMetadata1 = r - } else if r.ID == "skywalking-logger" { - pluginMetadata2 = r - } - } - gomega.Expect(pluginMetadata1).NotTo(gomega.BeNil()) - gomega.Expect(pluginMetadata2).NotTo(gomega.BeNil()) - - assertLoggerPluginMetadataEqual(pluginMetadata1, baseHttpLogger, "limit-count") - assertLoggerPluginMetadataEqual(pluginMetadata2, baseSkywalkingLogger, "limit-count") - - // update & get http-logger - baseHttpLogger = &types.PluginMetadata{ - ID: "http-logger", - Config: map[string]interface{}{ - "log_format": map[string]interface{}{ - "host": "$host", - "@timestamp": "$any_changed_value", - "client_ip": "$remote_addr", - }, - }, - } - _, err = s.UpdatePluginMetadata(baseHttpLogger) - gomega.Expect(err).To(gomega.BeNil()) - - pluginMetadata, err = s.GetPluginMetadata("http-logger") - gomega.Expect(err).To(gomega.BeNil()) - assertLoggerPluginMetadataEqual(pluginMetadata, baseHttpLogger, "key-auth") - - // delete skywalking-logger - err = s.DeletePluginMetadata("skywalking-logger") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetPluginMetadata("skywalking-logger") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete http-logger - err = s.DeletePluginMetadata("http-logger") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetPluginMetadata("http-logger") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - pluginMetadatas, err = s.ListPluginMetadata() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(pluginMetadatas)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-plugin-metadata/testdata/test.yaml b/test/cli/suites-plugin-metadata/testdata/test.yaml deleted file mode 100644 index 3dae6223..00000000 --- a/test/cli/suites-plugin-metadata/testdata/test.yaml +++ /dev/null @@ -1,6 +0,0 @@ -plugin_metadatas: - - id: http-logger - log_format: - '@timestamp': $time_iso8601 - client_ip: $remote_addr - host: $host diff --git a/test/cli/suites-plugin-metadata/validate.go b/test/cli/suites-plugin-metadata/validate.go deleted file mode 100644 index f3079c00..00000000 --- a/test/cli/suites-plugin-metadata/validate.go +++ /dev/null @@ -1,20 +0,0 @@ -package plugin_config - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` plugin metadata tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate plugin metadata schema", func() { - ginkgo.Skip("APISIX doesn't support yet") - validateOutput, err := s.Validate("suites-plugin-metadata/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Get file content success: config name: , version: , plugin_configs: 1.\nValidate file content success\n")) - }) - }) -}) diff --git a/test/cli/suites-stream-route/diff.go b/test/cli/suites-stream-route/diff.go deleted file mode 100644 index 668a5acf..00000000 --- a/test/cli/suites-stream-route/diff.go +++ /dev/null @@ -1,22 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-stream-route/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ service: "svc1" -+++ stream_route: "1" -Summary: create 2, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/cli/suites-stream-route/dump.go b/test/cli/suites-stream-route/dump.go deleted file mode 100644 index b22a50d4..00000000 --- a/test/cli/suites-stream-route/dump.go +++ /dev/null @@ -1,48 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewScaffold() - ginkgo.It("should dump APISIX resources", func() { - _, err := s.Sync("suites-stream-route/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`name: "" -services: -- id: svc1 - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: 127.0.0.1 - port: 3306 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -stream_routes: -- id: "1" - server_port: 9100 - service_id: svc1 -version: "" -`)) - - err = s.DeleteStreamRoute("1") - gomega.Expect(err).To(gomega.BeNil(), "check stream_route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - }) - }) -}) diff --git a/test/cli/suites-stream-route/sdk.go b/test/cli/suites-stream-route/sdk.go deleted file mode 100644 index a7160ef5..00000000 --- a/test/cli/suites-stream-route/sdk.go +++ /dev/null @@ -1,128 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("Route resource", func() { - var ( - err error - route *types.StreamRoute - ) - - // create service - _, err = s.CreateService(&types.Service{ - ID: "svc1", - Name: "svc1", - Upstream: &types.Upstream{ - ID: "ups1", - Name: "ups1", - Nodes: []types.UpstreamNode{ - { - Host: "127.0.0.1", - Port: 3306, - Weight: 1, - }, - }, - }, - }) - gomega.Expect(err).To(gomega.BeNil()) - - // utils - assertStreamRouteEqual := func(expect, toBe *types.StreamRoute) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - gomega.Expect(expect.RemoteAddr).To(gomega.Equal(toBe.RemoteAddr)) - gomega.Expect(expect.ServerAddr).To(gomega.Equal(toBe.ServerAddr)) - gomega.Expect(expect.ServerPort).To(gomega.Equal(toBe.ServerPort)) - gomega.Expect(expect.ServiceID).To(gomega.Equal(toBe.ServiceID)) - } - - // create route 1 - baseStreamRoute1 := &types.StreamRoute{ - ID: "route1", - ServerPort: 9100, - ServiceID: "svc1", - } - _, err = s.CreateStreamRoute(baseStreamRoute1) - gomega.Expect(err).To(gomega.BeNil()) - - // get route 1 - route, err = s.GetStreamRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - assertStreamRouteEqual(route, baseStreamRoute1) - - // create route 2 - baseStreamRoute2 := &types.StreamRoute{ - ID: "route2", - ServerAddr: "127.0.0.10", - ServerPort: 9101, - ServiceID: "svc1", - } - route, err = s.CreateStreamRoute(baseStreamRoute2) - gomega.Expect(err).To(gomega.BeNil()) - assertStreamRouteEqual(route, baseStreamRoute2) - - // test list - routes, err := s.ListStreamRoute() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(routes)).To(gomega.Equal(2)) - var route1, route2 *types.StreamRoute - for _, r := range routes { - if r.ID == "route1" { - route1 = r - } else if r.ID == "route2" { - route2 = r - } - } - gomega.Expect(route1).NotTo(gomega.BeNil()) - gomega.Expect(route2).NotTo(gomega.BeNil()) - - assertStreamRouteEqual(route1, baseStreamRoute1) - assertStreamRouteEqual(route2, baseStreamRoute2) - - // update & get route 1 - baseStreamRoute1 = &types.StreamRoute{ - ID: "route1", - ServerPort: 9101, - ServiceID: "svc1", - } - _, err = s.UpdateStreamRoute(baseStreamRoute1) - gomega.Expect(err).To(gomega.BeNil()) - - route, err = s.GetStreamRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - assertStreamRouteEqual(route, baseStreamRoute1) - - // delete route 2 - err = s.DeleteStreamRoute("route2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetStreamRoute("route2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete route 1 - err = s.DeleteStreamRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetStreamRoute("route1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - routes, err = s.ListStreamRoute() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(routes)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/cli/suites-stream-route/sync.go b/test/cli/suites-stream-route/sync.go deleted file mode 100644 index 1ad880d6..00000000 --- a/test/cli/suites-stream-route/sync.go +++ /dev/null @@ -1,39 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should sync data to APISIX", func() { - output, err := s.Sync("suites-stream-route/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring(`creating service: "svc1" -creating stream_route: "1" -Summary: created 2, updated 0, deleted 0`)) - - sr, err := s.GetStreamRoute("1") - gomega.Expect(err).To(gomega.BeNil(), "check stream_route get") - gomega.Expect(sr.ID).To(gomega.Equal("1")) - gomega.Expect(sr.ServerPort).To(gomega.Equal(9100)) - gomega.Expect(sr.ServiceID).To(gomega.Equal("svc1")) - - svc, err := s.GetService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service get") - gomega.Expect(svc.ID).To(gomega.Equal("svc1")) - gomega.Expect(len(svc.Upstream.Nodes)).To(gomega.Equal(1)) - gomega.Expect(svc.Upstream.Nodes[0].Host).To(gomega.Equal("127.0.0.1")) - gomega.Expect(svc.Upstream.Nodes[0].Port).To(gomega.Equal(3306)) - - err = s.DeleteStreamRoute("1") - gomega.Expect(err).To(gomega.BeNil(), "check stream_route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - }) - }) -}) diff --git a/test/cli/suites-stream-route/testdata/test.yaml b/test/cli/suites-stream-route/testdata/test.yaml deleted file mode 100644 index 787466a1..00000000 --- a/test/cli/suites-stream-route/testdata/test.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: "test" -version: "1.0.0" -services: - - name: svc1 - upstream: - name: httpbin - nodes: - - host: 127.0.0.1 - port: 3306 - weight: 1 -stream_routes: - - id: "1" - server_port: 9100 - service_id: "svc1" diff --git a/test/cli/suites-stream-route/validate.go b/test/cli/suites-stream-route/validate.go deleted file mode 100644 index 54fa3086..00000000 --- a/test/cli/suites-stream-route/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewScaffold() - ginkgo.It("should validate schema", func() { - validateOutput, err := s.Validate("suites-stream-route/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: test, version: 1.0.0, services: 1, stream_routes: 1.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/cli/suites-usecase/sync-delete.go b/test/cli/suites-usecase/sync-delete.go deleted file mode 100644 index f3f375f4..00000000 --- a/test/cli/suites-usecase/sync-delete.go +++ /dev/null @@ -1,99 +0,0 @@ -package suites_usecase - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` should handle deletion", func() { - ginkgo.Context("Test the sync command", func() { - s := scaffold.NewScaffold() - ginkgo.It("sync should delete resources", func() { - out, err := s.Sync("suites-usecase/testdata/sync-delete-init.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(out).To(gomega.Equal(`creating service: "svc1" -creating route: "route1" -Summary: created 2, updated 0, deleted 0 -`)) - - out, err = s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`name: "" -routes: -- id: route1 - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - out, err = s.Sync("suites-usecase/testdata/sync-delete-updated.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(out).To(gomega.Equal(`creating service: "svc1_changed" -creating route: "route1_changed" -deleting route: "route1" -deleting service: "svc1" -Summary: created 2, updated 0, deleted 2 -`)) - - out, err = s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`name: "" -routes: -- id: route1_changed - methods: - - GET - - PUT - name: route1_changed - priority: 0 - service_id: svc1_changed - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1_changed - name: svc1_changed - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - }) - }) -}) diff --git a/test/cli/suites-usecase/testdata/sync-delete-init.yaml b/test/cli/suites-usecase/testdata/sync-delete-init.yaml deleted file mode 100644 index 40181324..00000000 --- a/test/cli/suites-usecase/testdata/sync-delete-init.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: "test" -version: "1.0.0" -services: - - name: svc1 - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route1 - service_id: svc1 - uri: "/get" - methods: - - GET - - PUT diff --git a/test/cli/suites-usecase/testdata/sync-delete-updated.yaml b/test/cli/suites-usecase/testdata/sync-delete-updated.yaml deleted file mode 100644 index 20b68668..00000000 --- a/test/cli/suites-usecase/testdata/sync-delete-updated.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: "test" -version: "1.0.1" -services: - - name: svc1_changed - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route1_changed - service_id: svc1_changed - uri: "/get" - methods: - - GET - - PUT diff --git a/test/config/ci_config.go b/test/config/ci_config.go deleted file mode 100644 index 1d9a1bbe..00000000 --- a/test/config/ci_config.go +++ /dev/null @@ -1,20 +0,0 @@ -package config - -import ( - "os" - "strings" -) - -var ( - TestUpstream = "httpbin.org" -) - -func ReplaceUpstream(conf string) string { - return strings.ReplaceAll(conf, "HTTPBIN_PLACEHOLDER", TestUpstream) -} - -func init() { - if os.Getenv("GITHUB_ACTIONS") == "true" { - TestUpstream = "httpbin" - } -} diff --git a/test/mtls/certs/README.md b/test/mtls/certs/README.md deleted file mode 100644 index 8e569a03..00000000 --- a/test/mtls/certs/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Test certificates - -How to generate: - -```bash -# CA -openssl genrsa -out ca.key 2048 -openssl req -new -sha256 -key ca.key -out ca.csr -subj "/CN=ROOTCA" -openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.cert - -# Server certs -openssl genrsa -out server.key 2048 -# 注意:CN 值中的 `test.com` 为我们要测试的域名/主机名。 -openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=127.0.0.1" -openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cert -CAkey ca.key -CAserial ca.srl -CAcreateserial -in server.csr -out server.cert - -# Client certs -openssl genrsa -out client.key 2048 -openssl req -new -sha256 -key client.key -out client.csr -subj "/CN=CLIENT" -openssl x509 -req -days 36500 -sha256 -extensions v3_req -CA ca.cert -CAkey ca.key -CAserial ca.srl -CAcreateserial -in client.csr -out client.cert - -# Concat CA -cat ca.cert > ca.ca -cat ca.key >> ca.ca -``` diff --git a/test/mtls/certs/ca.ca b/test/mtls/certs/ca.ca deleted file mode 100644 index 21473912..00000000 --- a/test/mtls/certs/ca.ca +++ /dev/null @@ -1,45 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICqzCCAZMCFDKwCVsi9K0gsy3mRI9WvFm1aA74MA0GCSqGSIb3DQEBCwUAMBEx -DzANBgNVBAMMBlJPT1RDQTAgFw0yMzA5MjEwNDEzMzVaGA8yMTIzMDgyODA0MTMz -NVowETEPMA0GA1UEAwwGUk9PVENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA3xYxQRQbE9V16poG1HrztRI8+3bm6POys1aEIIrpAwDqto3omeFzuJee -7/DMYERQzVPCtAduY1LktSzGB1nkgwR1tuvQMyuhiv26HAcPhz1d2FUTw5uDDTg3 -a6Qh5/8lWWrqNfxArWWd6jYX9PkHfTYJdu4vvHd/v/uKB9MHtKQ4oNetE7djLCEr -sl+rrgg3ngEffi0wLPqaTy5HrpFJ/b+r7ewNFv8L1r+v6uya5FXd2ZGMS/jtD6Ga -O+001EeeiJfPAT/syY8EHzwyh2/d3qqve9M6oY7gi7N9yjtsmXoGsaopELgvCGgJ -Q2I3mWhtRa+eeO4Q27esBPXELlp3NwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAQ -Pv4EVhNsYKrdXIw1QhAd1s9aTrFmGdmbz88pKdXBnascmiTfnNaLc7KjxLCz9Awr -Xc4TsGfagalmSlqh0zZVWuA7EdFyozdf3+pqbXGQdfWgupmBlJd6jrHEB2m9Ddqw -g1A3JuCvpL40bOcd/Cefgn6ZGlnoLuosgxIWvD2/rFKF2RnVfQXz41JPSUOsbuxv -oE9pXQJBGlpqH//UCooAiDZoc3QipQRQHAVNLWqg56nQT/8Sl9SJPqzhYpaGGCKD -qSsls282exVBY5FnKFeqK8kuwHDMuFH5T6wbEhoFqGNBpjuP3HMVerzPkH18reNW -fcGPRhogLVwHAh6lLHC7 ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQDfFjFBFBsT1XXq -mgbUevO1Ejz7dubo87KzVoQgiukDAOq2jeiZ4XO4l57v8MxgRFDNU8K0B25jUuS1 -LMYHWeSDBHW269AzK6GK/bocBw+HPV3YVRPDm4MNODdrpCHn/yVZauo1/ECtZZ3q -Nhf0+Qd9Ngl27i+8d3+/+4oH0we0pDig160Tt2MsISuyX6uuCDeeAR9+LTAs+ppP -LkeukUn9v6vt7A0W/wvWv6/q7JrkVd3ZkYxL+O0PoZo77TTUR56Il88BP+zJjwQf -PDKHb93eqq970zqhjuCLs33KO2yZegaxqikQuC8IaAlDYjeZaG1Fr5547hDbt6wE -9cQuWnc3AgMBAAECgf9PTJQ1jfASHM7NlfLXlN0MgG8s9Bcgt3/OP/K3nqk1aB4G -xmroSDvaYgsdP3tmvOH8GsobNIkIxW6G+GNsOYgBCpTNcQWRKvFBdu+xBtF+QVIa -lCNyZNUw3crVeIKU9R1Rq6+VgRV8jaKUnqXlbqA64fp7TC+rzs6ab8+g+GLE0vEb -KJ1K2cCDOCoAhPQ3tHpNSxz4xiNY14pz9ts69E8iZbPyRbmYYK3tBNKn4EC5TsGp -HZ57p3qOcktpZ+s0x6wxobtKTCEbCl0D03oDxPZvV1QHMeNBaTkRV2e2VWULcLVe -XrYxBpbvPzqZ2hxkfP6PtA+Nj6SLn2txK8Nl3AkCgYEA/aU4wFwBXKH0b+J+dtjY -v8hfQN03f6Rc7bnWICm2s8XJKqt8JcsCUDaybdrF5/19W7sS4iQljfpZ03GN+WXq -Mp6kj4G/iVgCf7mHBUNjzIsbFGk4WziF5YAqhAkl+L2j1JfmyNfVEnMJ6BQfY5c2 -coc1sVQK3yTjKPweGKFM8fMCgYEA4ShZYZpFX4pBVeoWx9vFbI/ySMipbK0+V1uo -3dQn1qwhKRV+5NsryNZZ68UK0vj2Gh7atomEtbMHfgvq9jFSgn4xWQlEIZtE3kNq -pq5BlPivN5KKOvp6+bK9GIxeS2j11c2WI5DVqVrAIZy/azuWKm2+gYWZgC8M36/4 -2cZgsq0CgYEA59wUmKKCDC9vxqcAe5uV5xo/GIgfp2qUGCt8dUIsZQEz9vFzNwjw -QGr/iDQoHP03jpbfmCD6w2JKomJ3Kbfj8gna2+P7No1hG2h46HLF08CFLI9SmPB+ -VWBnfPLv5wVTsY55xLana4oFUES0vNEJUIAQsNzmYzzQ+jIdQN6R+8kCgYAergLP -29eMFyij7kvAW9/g1RRsensrhcey25h88YWkKLwz/uvVf0/OA/couyaobKgMExw2 -NzLQF17z++e+OCZIBa3vteWEtsWBi6oioCfrhp9JcTxIoQzUrVg62hWXLOwif+rv -WbLjaqw3aEZk9C+YBHAq8etingahDDdaZT+T2QKBgAOabW4Tx+O4pj83iZEVOlbW -AEGJBSn6bcxeuoB3JtOeWo+cQRrdA/Rcb4UcNQ/Uawd5ATLWr206e1jWyx6f+Rjc -oTTcHDCGa9V8jwvSuLaja3ktxfhBOzITPYBVOM9hjpV1Xp6/Y01KOSehHvZJFGQQ -GCkkLKWVNzf594Q5nWWr ------END PRIVATE KEY----- diff --git a/test/mtls/certs/ca.cert b/test/mtls/certs/ca.cert deleted file mode 100644 index 2e4c0610..00000000 --- a/test/mtls/certs/ca.cert +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICqzCCAZMCFDKwCVsi9K0gsy3mRI9WvFm1aA74MA0GCSqGSIb3DQEBCwUAMBEx -DzANBgNVBAMMBlJPT1RDQTAgFw0yMzA5MjEwNDEzMzVaGA8yMTIzMDgyODA0MTMz -NVowETEPMA0GA1UEAwwGUk9PVENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA3xYxQRQbE9V16poG1HrztRI8+3bm6POys1aEIIrpAwDqto3omeFzuJee -7/DMYERQzVPCtAduY1LktSzGB1nkgwR1tuvQMyuhiv26HAcPhz1d2FUTw5uDDTg3 -a6Qh5/8lWWrqNfxArWWd6jYX9PkHfTYJdu4vvHd/v/uKB9MHtKQ4oNetE7djLCEr -sl+rrgg3ngEffi0wLPqaTy5HrpFJ/b+r7ewNFv8L1r+v6uya5FXd2ZGMS/jtD6Ga -O+001EeeiJfPAT/syY8EHzwyh2/d3qqve9M6oY7gi7N9yjtsmXoGsaopELgvCGgJ -Q2I3mWhtRa+eeO4Q27esBPXELlp3NwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAQ -Pv4EVhNsYKrdXIw1QhAd1s9aTrFmGdmbz88pKdXBnascmiTfnNaLc7KjxLCz9Awr -Xc4TsGfagalmSlqh0zZVWuA7EdFyozdf3+pqbXGQdfWgupmBlJd6jrHEB2m9Ddqw -g1A3JuCvpL40bOcd/Cefgn6ZGlnoLuosgxIWvD2/rFKF2RnVfQXz41JPSUOsbuxv -oE9pXQJBGlpqH//UCooAiDZoc3QipQRQHAVNLWqg56nQT/8Sl9SJPqzhYpaGGCKD -qSsls282exVBY5FnKFeqK8kuwHDMuFH5T6wbEhoFqGNBpjuP3HMVerzPkH18reNW -fcGPRhogLVwHAh6lLHC7 ------END CERTIFICATE----- diff --git a/test/mtls/certs/ca.key b/test/mtls/certs/ca.key deleted file mode 100644 index 966b040d..00000000 --- a/test/mtls/certs/ca.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQDfFjFBFBsT1XXq -mgbUevO1Ejz7dubo87KzVoQgiukDAOq2jeiZ4XO4l57v8MxgRFDNU8K0B25jUuS1 -LMYHWeSDBHW269AzK6GK/bocBw+HPV3YVRPDm4MNODdrpCHn/yVZauo1/ECtZZ3q -Nhf0+Qd9Ngl27i+8d3+/+4oH0we0pDig160Tt2MsISuyX6uuCDeeAR9+LTAs+ppP -LkeukUn9v6vt7A0W/wvWv6/q7JrkVd3ZkYxL+O0PoZo77TTUR56Il88BP+zJjwQf -PDKHb93eqq970zqhjuCLs33KO2yZegaxqikQuC8IaAlDYjeZaG1Fr5547hDbt6wE -9cQuWnc3AgMBAAECgf9PTJQ1jfASHM7NlfLXlN0MgG8s9Bcgt3/OP/K3nqk1aB4G -xmroSDvaYgsdP3tmvOH8GsobNIkIxW6G+GNsOYgBCpTNcQWRKvFBdu+xBtF+QVIa -lCNyZNUw3crVeIKU9R1Rq6+VgRV8jaKUnqXlbqA64fp7TC+rzs6ab8+g+GLE0vEb -KJ1K2cCDOCoAhPQ3tHpNSxz4xiNY14pz9ts69E8iZbPyRbmYYK3tBNKn4EC5TsGp -HZ57p3qOcktpZ+s0x6wxobtKTCEbCl0D03oDxPZvV1QHMeNBaTkRV2e2VWULcLVe -XrYxBpbvPzqZ2hxkfP6PtA+Nj6SLn2txK8Nl3AkCgYEA/aU4wFwBXKH0b+J+dtjY -v8hfQN03f6Rc7bnWICm2s8XJKqt8JcsCUDaybdrF5/19W7sS4iQljfpZ03GN+WXq -Mp6kj4G/iVgCf7mHBUNjzIsbFGk4WziF5YAqhAkl+L2j1JfmyNfVEnMJ6BQfY5c2 -coc1sVQK3yTjKPweGKFM8fMCgYEA4ShZYZpFX4pBVeoWx9vFbI/ySMipbK0+V1uo -3dQn1qwhKRV+5NsryNZZ68UK0vj2Gh7atomEtbMHfgvq9jFSgn4xWQlEIZtE3kNq -pq5BlPivN5KKOvp6+bK9GIxeS2j11c2WI5DVqVrAIZy/azuWKm2+gYWZgC8M36/4 -2cZgsq0CgYEA59wUmKKCDC9vxqcAe5uV5xo/GIgfp2qUGCt8dUIsZQEz9vFzNwjw -QGr/iDQoHP03jpbfmCD6w2JKomJ3Kbfj8gna2+P7No1hG2h46HLF08CFLI9SmPB+ -VWBnfPLv5wVTsY55xLana4oFUES0vNEJUIAQsNzmYzzQ+jIdQN6R+8kCgYAergLP -29eMFyij7kvAW9/g1RRsensrhcey25h88YWkKLwz/uvVf0/OA/couyaobKgMExw2 -NzLQF17z++e+OCZIBa3vteWEtsWBi6oioCfrhp9JcTxIoQzUrVg62hWXLOwif+rv -WbLjaqw3aEZk9C+YBHAq8etingahDDdaZT+T2QKBgAOabW4Tx+O4pj83iZEVOlbW -AEGJBSn6bcxeuoB3JtOeWo+cQRrdA/Rcb4UcNQ/Uawd5ATLWr206e1jWyx6f+Rjc -oTTcHDCGa9V8jwvSuLaja3ktxfhBOzITPYBVOM9hjpV1Xp6/Y01KOSehHvZJFGQQ -GCkkLKWVNzf594Q5nWWr ------END PRIVATE KEY----- diff --git a/test/mtls/certs/client.cert b/test/mtls/certs/client.cert deleted file mode 100644 index b1a2e0ca..00000000 --- a/test/mtls/certs/client.cert +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICqzCCAZMCFAxH2Lxy6ciPRt5JawsVxjQpYdZ2MA0GCSqGSIb3DQEBCwUAMBEx -DzANBgNVBAMMBlJPT1RDQTAgFw0yMzA5MjEwNDEzMzVaGA8yMTIzMDgyODA0MTMz -NVowETEPMA0GA1UEAwwGQ0xJRU5UMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAtV26SBDdLp9uvAX8UTmRz/nRVIi+ynzaUdUNe06Mbp81hurnNmW1lHLH -e7VE/BTxVt0jOt5tOjM/qnH/lSgzfo1tqJszKdQ6LM75L/4o2X6iFnzN+FVD7OWA -5IaXUybcniHbZmiojljRLJKfsN85IfrOvMXymbcigDytpIP2DFQlS0UUzvGUtfrt -uPmenEJZRYY4R/yMdwoKGGgHntD8fJAxliypcVvZiHRQ6UV6hpUKhCNCfK34+iD1 -s3fl0eDA/jfcDV+FceeJDazOO7+xdoVk8L89WarClwO9LTpHBVNABpjUIbITOPd+ -K+kM8Cj+6qRwyKP+NcP2vc0Tpe6fdwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAb -54hItlYWisAWT3np04Xtx0JcXuTJllw6FS1fPzvS+HCMcqvLEqvYjrJkw3WLipNi -lpZKormB/Gvh9yZ6hhbCrKXHIaAQKaEI0Y8awFnq5U4WcL8js5H4LC4H5va27uQU -Gv0giKEx9KlUnDgj6o7D4yxNQSvmMTog3n/bY7StCdwkYFbd+x2dnL0+FbAY47HP -QxY3PXX7DCsyFyoxn32bpMrp4j0vFtAl5TLN0rJwI/HZgyOt9/rpk13OoZ5l95Xq -DawvoEB1DLaDioglbV6G7mIFUgF7ivUuHZDBZI68+E2+wPMoTJlZHoaMfv2NXzNF -ANtkuB9e7NPOP03A1tQJ ------END CERTIFICATE----- diff --git a/test/mtls/certs/client.key b/test/mtls/certs/client.key deleted file mode 100644 index 449ac348..00000000 --- a/test/mtls/certs/client.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1XbpIEN0un268 -BfxROZHP+dFUiL7KfNpR1Q17ToxunzWG6uc2ZbWUcsd7tUT8FPFW3SM63m06Mz+q -cf+VKDN+jW2omzMp1Doszvkv/ijZfqIWfM34VUPs5YDkhpdTJtyeIdtmaKiOWNEs -kp+w3zkh+s68xfKZtyKAPK2kg/YMVCVLRRTO8ZS1+u24+Z6cQllFhjhH/Ix3CgoY -aAee0Px8kDGWLKlxW9mIdFDpRXqGlQqEI0J8rfj6IPWzd+XR4MD+N9wNX4Vx54kN -rM47v7F2hWTwvz1ZqsKXA70tOkcFU0AGmNQhshM4934r6QzwKP7qpHDIo/41w/a9 -zROl7p93AgMBAAECggEAUBf6QgUgiIxkDKjgCsbPu/bpGNni1+1XRLg/4eqIXfn1 -W4W1YfJ8w/gEXH9q3aAqDGBQRJkhx4gGX7FEW4yLYSH4AXO4nopYvp0Qsr8dLx+b -WS74juiovJ+F0mYVweTxnOv6xjkF5wcWKQb/es65nmXykq8gOf4EQHmLfZHBQSPP -k8HpcGklylyUkz0h5kYMxHo1KeyC0MAgtz521dpLEp6RlbXgDMvce7yLVDeE5HEh -XyxL20e2cfqkDnC1wbP8ns6f1uEaVeIbgA8ojyUXE9kVOEr85yqxgcZSc76o2aww -qpR4I+ThE8AlzxI881Oy18BoBi5zW1n6t8P9odmGkQKBgQD5VBUxEUbUAUa2m9SY -Gj3vp9Ko5SDX+735/6aA94CY89/tKUZ43MpDX1dNCYpC20Qr/HIpu4xbhSNfc8/j -dGco6jbi8vcZcBJf1nijEIs8EdFtNi6Fvgtz6TxAJgTgWjQjwDkhwGVYvFqVfijB -zZE9HEdSR9tXcXd964qplz9EQwKBgQC6OBkljAEiSpM20JCXc6uvaxSB7ChQ9gsk -rSbNv8UjZcnK/rWhnfaSE+Amc8arztsj6OL5lmCGbTb+EKDFpcLG6muEfc2DPms8 -RFMIfhsfrKGhfir8Z7Q/OxfIp5lY8AjjW4oTtdV3xSarTKW0U7a2yi1/h9IIEQvU -fvvVlPs+vQKBgQC8T086IgxWrygTX5qvzpIc4anvui6WMiKIIRhGq6hK3VaxLs11 -F30EaYvsOVpdqHHSrvYiou4daW8P9s56wj3v3wXZXxxV8BlQnRG8RxCCLAr660Mz -7BeGUhAZex2PvVFH9zSvUzq4THo2EnUB+xrJBwSvFmqJnr9bMX0nHX7BNQKBgGjw -nBswnqL4a6WEOzdeE+4y5cehkcauIPF2MlvYKfwcrG8cwrZlEkKXuMVykQ7Y7115 -LA+C0rpuaYMbU76N/wmhqR1s15eSYVQBHNm4VwiufBdJxmee+VB4uJp2+E/KVPfq -6hDZc4afL39xHiBU3K59mM45d/7bvFLvjHN8FWnxAoGBALgfQuz9KJ9HRN25SFva -9EI39kq2glqMfKV9G8zLB578jFCddv7aw99oxojBlqrgVroeBoZSM3jq9QNMxyYm -s7jntPCLfmo+0Z21ileDvAB2nsyiUxxJJsI5P89qVqORILljs/MlJNg486ZbFkkg -T+Eb0E9TMaiJ4f/AQCVSkI62 ------END PRIVATE KEY----- diff --git a/test/mtls/certs/server.cert b/test/mtls/certs/server.cert deleted file mode 100644 index 334a7d71..00000000 --- a/test/mtls/certs/server.cert +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICrjCCAZYCFAxH2Lxy6ciPRt5JawsVxjQpYdZ1MA0GCSqGSIb3DQEBCwUAMBEx -DzANBgNVBAMMBlJPT1RDQTAgFw0yMzA5MjEwNDEzMzVaGA8yMTIzMDgyODA0MTMz -NVowFDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEA4Vu5cj05Kcgvh1rNdSJ84dJ47v7PIPyzfXVIvAI3BWGMYYNr2os2 -h7v9005RgvhRaVGfzvWJS7I4HOu+l0SIpsRa8lWAQUOicmzUKEs7yN4ICNlbRX1O -imvZAwbKcLlB5urOY/hYiCUteASwVc1NW5asGbxmhshSEOj7aMnIcNkG/Fpt2WrK -2Hc+DIPWdb054iCXTtD5InWuAUzqQjedQkwJwrndpshjitz/wkRC1qQr69ok5sTy -XRsQbIpxuioKYCD1JvcR56q47mLNkibNIbDrphHb3CrZJtGrRWh7EWUzh/1MlyoN -t/gVFykGcffavFsgGhJwrCnFnNwdtE6miQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB -AQCrovF5LJmqCW4d8cruL4tfN8yocXFaxKQmEEtUnh+BqohGPyBYUumv0AujkFEq -pIj5t/N/tIDs+iWtxuDX99CPlh38I1d1gIY5udEglbEwa/ZiNyI4TD56iRn/6cVs -xRimRuxfDHZvsprga7I05kjDGxxOA3nJ9EOIakd9R6z0EvNF/HLh2in+JN7wLsdH -kUdor7m/Ur5CkzluKcCppyG0v7ysBQUmC1H01QfXzIBuq0G5XBpARQo7Us4GSCgo -OCy7vjON4SgmO8q5eFXiI6d1xKVSqvEumK2EMtbicqiIaWKSaUOQr0Ap576x64EF -swBNW0aFw8Cgc/WxJmvN/uyt ------END CERTIFICATE----- diff --git a/test/mtls/certs/server.key b/test/mtls/certs/server.key deleted file mode 100644 index 77bd8a94..00000000 --- a/test/mtls/certs/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDhW7lyPTkpyC+H -Ws11Inzh0nju/s8g/LN9dUi8AjcFYYxhg2vaizaHu/3TTlGC+FFpUZ/O9YlLsjgc -676XRIimxFryVYBBQ6JybNQoSzvI3ggI2VtFfU6Ka9kDBspwuUHm6s5j+FiIJS14 -BLBVzU1blqwZvGaGyFIQ6Ptoychw2Qb8Wm3ZasrYdz4Mg9Z1vTniIJdO0Pkida4B -TOpCN51CTAnCud2myGOK3P/CRELWpCvr2iTmxPJdGxBsinG6KgpgIPUm9xHnqrju -Ys2SJs0hsOumEdvcKtkm0atFaHsRZTOH/UyXKg23+BUXKQZx99q8WyAaEnCsKcWc -3B20TqaJAgMBAAECggEAGe6dgXou0aJj6LwPzCiwrOY0rRDEei/mAhvsx5isJun9 -lsuCDdMK7nXZHJeErPDg4mEGhhDOUJf0mcxdYUf9Pw51hjEFSg1LvEXOaIgWvDOj -gkwrsux7TRWhslXV1gCVZypyH0IYwurfR3Inrenq2s0U5v29YQlcWy6KHpnonyZ8 -3lY8uD1LAQ1/jTgdZe9ZRp3z5F5Oy/4eaAKEMOSQv6r4ZavX3TFEBiTGIZg08co2 -5f+lrfaENGS/DoYw9sW4qw6i+Kbpz4wImy9H0uJ1t/876EH/YxePZpaPzdVkx58l -0Hxotjj2eqs0cwQcLbQNp7bBFSzHKWBbqbzQnR0kcQKBgQDwciV5WtcSfT2btdbG -/Uy36ldIRIAvWkA2z7eO/pRnNrRwiPLkbNXVS16LCgsg5ofs1laKxs/hg8piBsOZ -pwNKo73H83wSuzxHXxX0dwdnJzDUpQc4Zdnms9qZHdSymnP782ucVTETQVytMcfc -zazUu02+umVQftthBEwcfgy/cQKBgQDv77kkstqBouOyNZ+2HlWBQq0lXFBiPILM -VI4DWeoYHBL00fHuW7CQzjf0D10tfyY/98eu7haOcI0ANyKJeVO0PWxLW0tsMVRF -LSYkCl+VJeXTQak/IJZTF72XVkjbI4BEm1rfIxJMrjIrRgjiMQ88i96JlulHiD4p -QUZz8Y38mQKBgQDf5jsVFUz/D5al/57/b3HYNSSPzBHdhloTcDuKsW+56Qv/Wtdr -dBlu5B8ms/SG74SVO0YdNmLvqcx0mb3cv9Pl1jHsFsRroOTRxS5plclT+f+TlBT0 -JIvTM5mUSV397qugaxzC1+W5+3x0Xs2ww+lzsvMDbwjW+zoHznudcJcoUQKBgG/5 -Z2UqFvFzDGJpxixfAbZ+YIHUECuYY85qE5Ca0LYJvAsHQ6ninoclxlUfKnQGJ9qo -j0cjE+HLwvH7ySFxPtAZ7kVIyO0oJFTYWjltoefgNN6fgRXV5nHG3ZMYEI48hir+ -ea0zF+IzVXR+jbzJ+nteJy/8/seEXMYyEWggrgvZAoGASQjD9J/MumttDB3R6pEI -SfCXYKmQPauoio3T6+IFCe978e14pcLHbzmIXj7u034xVShoV9wh00ygZIOQ9dp6 -NgVvQbe17QilWp84MevE7tGZfZ+VjULMNGbucPkG1AQ5teH2cSD4NDWumMFOh9qo -cTxhqk9hLes/YOmDV66F8NQ= ------END PRIVATE KEY----- diff --git a/test/mtls/mtls_test.go b/test/mtls/mtls_test.go deleted file mode 100644 index 0723b257..00000000 --- a/test/mtls/mtls_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package mtls - -import ( - "testing" - - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - _ "github.com/api7/adc/test/mtls/suites-basic" -) - -func TestADCMtls(t *testing.T) { - gomega.RegisterFailHandler(ginkgo.Fail) - ginkgo.RunSpecs(t, "ADC CLI test suites") -} diff --git a/test/mtls/suites-basic/diff.go b/test/mtls/suites-basic/diff.go deleted file mode 100644 index 09fcb848..00000000 --- a/test/mtls/suites-basic/diff.go +++ /dev/null @@ -1,24 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc diff` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("should return the diff result", func() { - out, err := s.Diff("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(`+++ service: "svc1" -+++ service: "svc2" -+++ route: "route1" -+++ route: "route2" -Summary: create 4, update 0, delete 0 -`)) - }) - }) -}) diff --git a/test/mtls/suites-basic/dump.go b/test/mtls/suites-basic/dump.go deleted file mode 100644 index f4aeac44..00000000 --- a/test/mtls/suites-basic/dump.go +++ /dev/null @@ -1,85 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc dump` tests", func() { - ginkgo.Context("Test the dump command", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("should dump APISIX resources", func() { - _, err := s.Sync("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - - out, err := s.Dump() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(out).To(gomega.Equal(config.ReplaceUpstream(`name: "" -routes: -- id: route1 - methods: - - GET - - PUT - name: route1 - priority: 0 - service_id: svc1 - status: 1 - uri: /get -- id: route2 - methods: - - GET - name: route2 - priority: 0 - service_id: svc2 - status: 1 - uri: /get -services: -- hosts: - - foo1.com - id: svc1 - name: svc1 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -- hosts: - - svc.com - id: svc2 - name: svc2 - upstream: - hash_on: vars - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - priority: 0 - weight: 1 - pass_host: pass - scheme: http - type: roundrobin -version: "" -`))) - - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteService("svc2") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/mtls/suites-basic/ping.go b/test/mtls/suites-basic/ping.go deleted file mode 100644 index f3db2784..00000000 --- a/test/mtls/suites-basic/ping.go +++ /dev/null @@ -1,19 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc ping` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("should connect to APISIX", func() { - output, err := s.Ping() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(output).To(gomega.Equal("Connected to backend successfully!\n")) - }) - }) -}) diff --git a/test/mtls/suites-basic/sdk.go b/test/mtls/suites-basic/sdk.go deleted file mode 100644 index ef0528f9..00000000 --- a/test/mtls/suites-basic/sdk.go +++ /dev/null @@ -1,132 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("adc APISIX SDK tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("Route resource", func() { - var ( - err error - route *types.Route - ) - - // create service - _, err = s.CreateService(&types.Service{ - ID: "svc1", - Name: "svc1", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "ups1", - Name: "ups1", - Nodes: []types.UpstreamNode{ - { - Host: "foo.com", - Port: 80, - Weight: 1, - }, - }, - }, - }) - gomega.Expect(err).To(gomega.BeNil()) - - // utils - assertRouteEqual := func(expect, toBe *types.Route) { - gomega.Expect(expect.ID).To(gomega.Equal(toBe.ID)) - gomega.Expect(expect.Name).To(gomega.Equal(toBe.Name)) - gomega.Expect(expect.Uri).To(gomega.Equal(toBe.Uri)) - gomega.Expect(expect.ServiceID).To(gomega.Equal(toBe.ServiceID)) - } - - // create route 1 - baseRoute1 := &types.Route{ - ID: "route1", - Name: "route1", - Uri: "/route1", - ServiceID: "svc1", - } - _, err = s.CreateRoute(baseRoute1) - gomega.Expect(err).To(gomega.BeNil()) - - // get route 1 - route, err = s.GetRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - assertRouteEqual(route, baseRoute1) - - // create route 2 - baseRoute2 := &types.Route{ - ID: "route2", - Name: "route2", - Uri: "/route2", - ServiceID: "svc1", - } - route, err = s.CreateRoute(baseRoute2) - gomega.Expect(err).To(gomega.BeNil()) - assertRouteEqual(route, baseRoute2) - - // test list - routes, err := s.ListRoute() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(routes)).To(gomega.Equal(2)) - var route1, route2 *types.Route - for _, r := range routes { - if r.ID == "route1" { - route1 = r - } else if r.ID == "route2" { - route2 = r - } - } - gomega.Expect(route1).NotTo(gomega.BeNil()) - gomega.Expect(route2).NotTo(gomega.BeNil()) - - assertRouteEqual(route1, baseRoute1) - assertRouteEqual(route2, baseRoute2) - - // update & get route 1 - baseRoute1 = &types.Route{ - ID: "route1", - Name: "route1", - Uri: "/route1-updated", - ServiceID: "svc1", - } - _, err = s.UpdateRoute(baseRoute1) - gomega.Expect(err).To(gomega.BeNil()) - - route, err = s.GetRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - assertRouteEqual(route, baseRoute1) - - // delete route 2 - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetRoute("route2") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // delete route 1 - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil()) - - _, err = s.GetRoute("route1") - gomega.Expect(err).To(gomega.Equal(apisix.ErrNotFound)) - - // final list - routes, err = s.ListRoute() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(len(routes)).To(gomega.Equal(0)) - - // delete service - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil()) - }) - }) -}) diff --git a/test/mtls/suites-basic/sync.go b/test/mtls/suites-basic/sync.go deleted file mode 100644 index d330436d..00000000 --- a/test/mtls/suites-basic/sync.go +++ /dev/null @@ -1,155 +0,0 @@ -package basic - -import ( - "net/http" - - "github.com/gavv/httpexpect/v2" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/test/config" - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc sync` tests", func() { - var ( - service = &types.Service{ - ID: "svc", - Name: "svc", - Hosts: []string{ - "foo.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - service1 = &types.Service{ - ID: "svc1", - Name: "svc1", - Hosts: []string{ - "bar.com", - }, - Upstream: &types.Upstream{ - ID: "httpbin", - Name: "httpbin", - Nodes: []types.UpstreamNode{ - { - Host: config.TestUpstream, - Port: 80, - Weight: 1, - }, - }, - }, - } - - route = &types.Route{ - ID: "route", - Name: "route", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc", - } - - route1 = &types.Route{ - ID: "route1", - Name: "route1", - Uri: "/get", - Methods: []string{ - "GET", - }, - ServiceID: "svc1", - } - ) - - ginkgo.Context("Basic functions", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - _, err = s.UpdateService(service1) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route1) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "bar.com"`) - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo.com"`) - - /* - service is deleted - service1 is updated - service2 is created - route is deleted - route1 is updated - route2 is created - */ - output, err := s.Sync("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 2, updated 2, deleted 2")) - - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("foo1.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo1.com"`) - expect.GET("/get").WithHost("svc.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "svc.com"`) - - err = s.DeleteRoute("route1") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteRoute("route2") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - err = s.DeleteService("svc2") - gomega.Expect(err).To(gomega.BeNil(), "check service delete") - expect.GET("/get").WithHost("foo1.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("svc.com").Expect().Status(http.StatusNotFound) - }) - }) - - ginkgo.Context("Test the sync command order", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("should sync data to APISIX", func() { - expect := httpexpect.Default(ginkgo.GinkgoT(), "http://127.0.0.1:9080") - - _, err := s.UpdateService(service) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - _, err = s.UpdateRoute(route) - gomega.Expect(err).To(gomega.BeNil(), "check route update") - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "foo.com"`) - _, err = s.UpdateService(service1) - gomega.Expect(err).To(gomega.BeNil(), "check service update") - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - - // Now we have two services and one route in APISIX - // route => svc, and svc1 - // we delete svc1 and update the route to reference svc1 - output, err := s.Sync("suites-basic/testdata/test2.yaml") - gomega.Expect(err).To(gomega.BeNil(), "check sync command") - gomega.Expect(output).To(gomega.ContainSubstring("Summary: created 0, updated 2, deleted 1")) - - expect.GET("/get").WithHost("svc.com").Expect().Status(http.StatusOK).Body().Contains(`"Host": "svc.com"`) - expect.GET("/get").WithHost("foo.com").Expect().Status(http.StatusNotFound) - expect.GET("/get").WithHost("bar.com").Expect().Status(http.StatusNotFound) - - err = s.DeleteRoute("route") - gomega.Expect(err).To(gomega.BeNil(), "check route delete") - err = s.DeleteService("svc1") - gomega.Expect(err).To(gomega.BeNil(), "check svc delete") - }) - }) -}) diff --git a/test/mtls/suites-basic/testdata/test.yaml b/test/mtls/suites-basic/testdata/test.yaml deleted file mode 100644 index ac423652..00000000 --- a/test/mtls/suites-basic/testdata/test.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: "test" -version: "1.0.0" -services: - - name: svc1 - hosts: - - foo1.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 - - name: svc2 - hosts: - - svc.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route1 - service_id: svc1 - uri: "/get" - methods: - - GET - - PUT - - name: route2 - service_id: svc2 - uri: "/get" - methods: - - GET diff --git a/test/mtls/suites-basic/testdata/test2.yaml b/test/mtls/suites-basic/testdata/test2.yaml deleted file mode 100644 index 5a44d767..00000000 --- a/test/mtls/suites-basic/testdata/test2.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: "test2" -version: "1.0.0" -services: - - name: svc1 - hosts: - - svc.com - upstream: - id: httpbin - name: httpbin - nodes: - - host: HTTPBIN_PLACEHOLDER - port: 80 - weight: 1 -routes: - - name: route - service_id: svc1 - uri: "/get" - methods: - - GET diff --git a/test/mtls/suites-basic/validate.go b/test/mtls/suites-basic/validate.go deleted file mode 100644 index 42c615f0..00000000 --- a/test/mtls/suites-basic/validate.go +++ /dev/null @@ -1,19 +0,0 @@ -package basic - -import ( - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/test/scaffold" -) - -var _ = ginkgo.Describe("`adc validate` tests", func() { - ginkgo.Context("Basic functions", func() { - s := scaffold.NewMtlsScaffold() - ginkgo.It("should validate schema", func() { - validateOutput, err := s.Validate("suites-basic/testdata/test.yaml") - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(validateOutput).To(gomega.Equal("Read configuration file successfully: config name: test, version: 1.0.0, routes: 2, services: 2.\nSuccessfully validated configuration file!\n")) - }) - }) -}) diff --git a/test/scaffold/scaffold.go b/test/scaffold/scaffold.go deleted file mode 100644 index a03bc9ca..00000000 --- a/test/scaffold/scaffold.go +++ /dev/null @@ -1,484 +0,0 @@ -package scaffold - -import ( - "bytes" - "context" - "os" - "os/exec" - "strings" - - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/api7/adc/pkg/api/apisix" - "github.com/api7/adc/pkg/api/apisix/types" - "github.com/api7/adc/pkg/common" - cmdconfig "github.com/api7/adc/pkg/config" - "github.com/api7/adc/test/config" -) - -type Scaffold struct { - cluster apisix.Cluster - - routes map[string]struct{} - services map[string]struct{} - consumers map[string]struct{} - globalRules map[string]struct{} - pluginConfigs map[string]struct{} - consumerGroups map[string]struct{} - pluginMetadatas map[string]struct{} - streamRoutes map[string]struct{} -} - -func NewScaffold() *Scaffold { - return newScaffold(cmdconfig.ClientConfig{ - Server: "http://127.0.0.1:9180", - Token: "edd1c9f034335f136f87ad84b625c8f1", - }) -} - -func NewMtlsScaffold() *Scaffold { - return newScaffold(cmdconfig.ClientConfig{ - Server: "https://127.0.0.1:9180", - Token: "edd1c9f034335f136f87ad84b625c8f1", - CAPath: "../mtls/certs/ca.ca", - Certificate: "../mtls/certs/client.cert", - CertificateKey: "../mtls/certs/client.key", - Insecure: true, - }) -} - -func newScaffold(conf cmdconfig.ClientConfig) *Scaffold { - cluster, clusterError := apisix.NewCluster(context.Background(), conf) - - s := &Scaffold{ - cluster: cluster, - - routes: map[string]struct{}{}, - services: map[string]struct{}{}, - consumers: map[string]struct{}{}, - globalRules: map[string]struct{}{}, - pluginConfigs: map[string]struct{}{}, - consumerGroups: map[string]struct{}{}, - pluginMetadatas: map[string]struct{}{}, - streamRoutes: map[string]struct{}{}, - } - - ginkgo.BeforeEach(func() { - gomega.Expect(clusterError).To(gomega.BeNil()) - err := s.Configure(conf) - gomega.Expect(err).To(gomega.BeNil()) - }) - - ginkgo.AfterEach(func() { - for route := range s.routes { - s.DeleteRoute(route) //nolint:all - } - for svc := range s.services { - s.DeleteService(svc) //nolint:all - } - for consumer := range s.consumers { - s.DeleteConsumer(consumer) //nolint:all - } - for globalRule := range s.globalRules { - s.DeleteGlobalRule(globalRule) //nolint:all - } - for pluginConfig := range s.pluginConfigs { - s.DeletePluginConfig(pluginConfig) //nolint:all - } - for consumerGroup := range s.consumerGroups { - s.DeleteConsumerGroup(consumerGroup) //nolint:all - } - for pluginMetadata := range s.pluginMetadatas { - s.DeletePluginMetadata(pluginMetadata) //nolint:all - } - for streamRoute := range s.streamRoutes { - s.DeleteStreamRoute(streamRoute) //nolint:all - } - }) - - return s -} - -func (s *Scaffold) AddRoutesFinalizer(routes ...string) { - for _, route := range routes { - s.routes[route] = struct{}{} - } -} - -func (s *Scaffold) AddServicesFinalizer(services ...string) { - for _, service := range services { - s.services[service] = struct{}{} - } -} - -func (s *Scaffold) AddConsumersFinalizer(consumers ...string) { - for _, consumer := range consumers { - s.consumers[consumer] = struct{}{} - } -} - -func (s *Scaffold) AddGlobalRulesFinalizer(globalRules ...string) { - for _, globalRule := range globalRules { - s.globalRules[globalRule] = struct{}{} - } -} - -func (s *Scaffold) AddPluginConfigsFinalizer(pluginConfigs ...string) { - for _, pluginConfig := range pluginConfigs { - s.pluginConfigs[pluginConfig] = struct{}{} - } -} - -func (s *Scaffold) AddConsumerGroupsFinalizer(consumerGroups ...string) { - for _, consumerGroup := range consumerGroups { - s.consumerGroups[consumerGroup] = struct{}{} - } -} - -func (s *Scaffold) AddPluginMetadatasFinalizer(pluginMetadatas ...string) { - for _, pluginMetadata := range pluginMetadatas { - s.pluginMetadatas[pluginMetadata] = struct{}{} - } -} - -func (s *Scaffold) AddStreamRoutesFinalizer(streamRoutes ...string) { - for _, streamRoute := range streamRoutes { - s.streamRoutes[streamRoute] = struct{}{} - } -} - -func (s *Scaffold) Configure(conf cmdconfig.ClientConfig) error { - key := conf.Token - input := key + "\n" - - args := []string{"configure", "--address", conf.Server, "-f"} - if conf.CAPath != "" { - args = append(args, "--capath", conf.CAPath, "--cert", conf.Certificate, "--cert-key", conf.CertificateKey) - if conf.Insecure { - args = append(args, "-k") - } - } - - _, err := s.ExecWithInput(input, args...) - return err -} - -func (s *Scaffold) Exec(args ...string) (string, error) { - var syncOutput bytes.Buffer - cmd := exec.Command("adc", args...) - cmd.Stdout = &syncOutput - err := cmd.Run() - - return syncOutput.String(), err -} - -func (s *Scaffold) ExecWithInput(input string, args ...string) (string, error) { - var syncOutput bytes.Buffer - cmd := exec.Command("adc", args...) - cmd.Stdout = &syncOutput - cmd.Stdin = strings.NewReader(input) - err := cmd.Run() - - return syncOutput.String(), err -} - -func (s *Scaffold) Ping() (string, error) { - return s.Exec("ping") -} - -func (s *Scaffold) Sync(files ...string) (string, error) { - var paths []string - - for _, path := range files { - conf, err := common.GetContentFromFile(path) - gomega.Expect(err).To(gomega.BeNil(), "load config from file "+path) - - for _, service := range conf.Services { - for j, node := range service.Upstream.Nodes { - if node.Host == "HTTPBIN_PLACEHOLDER" { - service.Upstream.Nodes[j].Host = config.TestUpstream - } - } - } - - for _, route := range conf.Routes { - s.AddRoutesFinalizer(route.ID) - } - - for _, service := range conf.Services { - s.AddServicesFinalizer(service.ID) - } - - for _, consumer := range conf.Consumers { - s.AddConsumersFinalizer(consumer.Username) - } - - for _, globalRule := range conf.GlobalRules { - s.AddGlobalRulesFinalizer(globalRule.ID) - } - - for _, pluginConfig := range conf.PluginConfigs { - s.AddPluginConfigsFinalizer(pluginConfig.ID) - } - - for _, consumerGroup := range conf.ConsumerGroups { - s.AddConsumerGroupsFinalizer(consumerGroup.ID) - } - - for _, pluginMetadata := range conf.PluginMetadatas { - s.AddPluginMetadatasFinalizer(pluginMetadata.ID) - } - - for _, streamRoute := range conf.StreamRoutes { - s.AddStreamRoutesFinalizer(streamRoute.ID) - } - - tmpFile := path + ".tmp" - err = common.SaveAPISIXConfiguration(tmpFile, conf) - gomega.Expect(err).To(gomega.BeNil(), "save temp file to "+tmpFile) - - paths = append(paths, tmpFile) - } - - defer func() { - for _, tmpFile := range paths { - err := os.Remove(tmpFile) - gomega.Expect(err).To(gomega.BeNil(), "delete temp file at "+tmpFile) - } - }() - args := []string{"sync"} - for _, path := range paths { - args = append(args, "-f", path) - } - - return s.Exec(args...) -} - -func (s *Scaffold) Dump() (string, error) { - return s.Exec("dump", "-o", "/dev/stdout") -} - -func (s *Scaffold) DumpWithLabels(labels types.Labels) (string, error) { - var l []string - for k, v := range labels { - l = append(l, k+"="+v) - } - return s.Exec("dump", "-o", "/dev/stdout", "-l", strings.Join(l, ",")) -} - -func (s *Scaffold) Diff(path string) (string, error) { - return s.Exec("diff", "-f", path) -} - -func (s *Scaffold) Validate(path string) (string, error) { - return s.Exec("validate", "-f", path) -} - -func (s *Scaffold) GetRoute(route string) (*types.Route, error) { - return s.cluster.Route().Get(context.Background(), route) -} - -func (s *Scaffold) ListRoute() ([]*types.Route, error) { - return s.cluster.Route().List(context.Background()) -} - -func (s *Scaffold) CreateRoute(route *types.Route) (*types.Route, error) { - s.routes[route.ID] = struct{}{} - - return s.cluster.Route().Create(context.Background(), route) -} - -func (s *Scaffold) UpdateRoute(route *types.Route) (*types.Route, error) { - s.routes[route.ID] = struct{}{} - - return s.cluster.Route().Update(context.Background(), route) -} - -func (s *Scaffold) DeleteRoute(id string) error { - delete(s.routes, id) - - return s.cluster.Route().Delete(context.Background(), id) -} - -func (s *Scaffold) GetService(service string) (*types.Service, error) { - return s.cluster.Service().Get(context.Background(), service) -} - -func (s *Scaffold) ListService() ([]*types.Service, error) { - return s.cluster.Service().List(context.Background()) -} - -func (s *Scaffold) CreateService(service *types.Service) (*types.Service, error) { - s.services[service.ID] = struct{}{} - - return s.cluster.Service().Create(context.Background(), service) -} - -func (s *Scaffold) UpdateService(service *types.Service) (*types.Service, error) { - s.services[service.ID] = struct{}{} - - return s.cluster.Service().Update(context.Background(), service) -} - -func (s *Scaffold) DeleteService(id string) error { - delete(s.services, id) - - return s.cluster.Service().Delete(context.Background(), id) -} - -func (s *Scaffold) GetConsumer(username string) (*types.Consumer, error) { - return s.cluster.Consumer().Get(context.Background(), username) -} - -func (s *Scaffold) ListConsumer() ([]*types.Consumer, error) { - return s.cluster.Consumer().List(context.Background()) -} - -func (s *Scaffold) CreateConsumer(consumer *types.Consumer) (*types.Consumer, error) { - s.consumers[consumer.Username] = struct{}{} - - return s.cluster.Consumer().Create(context.Background(), consumer) -} - -func (s *Scaffold) UpdateConsumer(consumer *types.Consumer) (*types.Consumer, error) { - s.consumers[consumer.Username] = struct{}{} - - return s.cluster.Consumer().Update(context.Background(), consumer) -} - -func (s *Scaffold) DeleteConsumer(id string) error { - delete(s.consumers, id) - - return s.cluster.Consumer().Delete(context.Background(), id) -} - -func (s *Scaffold) GetGlobalRule(id string) (*types.GlobalRule, error) { - return s.cluster.GlobalRule().Get(context.Background(), id) -} - -func (s *Scaffold) ListGlobalRule() ([]*types.GlobalRule, error) { - return s.cluster.GlobalRule().List(context.Background()) -} - -func (s *Scaffold) CreateGlobalRule(globalRule *types.GlobalRule) (*types.GlobalRule, error) { - s.globalRules[globalRule.ID] = struct{}{} - - return s.cluster.GlobalRule().Create(context.Background(), globalRule) -} - -func (s *Scaffold) UpdateGlobalRule(globalRule *types.GlobalRule) (*types.GlobalRule, error) { - s.globalRules[globalRule.ID] = struct{}{} - - return s.cluster.GlobalRule().Update(context.Background(), globalRule) -} - -func (s *Scaffold) DeleteGlobalRule(id string) error { - delete(s.globalRules, id) - - return s.cluster.GlobalRule().Delete(context.Background(), id) -} - -func (s *Scaffold) GetPluginConfig(id string) (*types.PluginConfig, error) { - return s.cluster.PluginConfig().Get(context.Background(), id) -} - -func (s *Scaffold) ListPluginConfig() ([]*types.PluginConfig, error) { - return s.cluster.PluginConfig().List(context.Background()) -} - -func (s *Scaffold) CreatePluginConfig(pluginConfig *types.PluginConfig) (*types.PluginConfig, error) { - s.pluginConfigs[pluginConfig.ID] = struct{}{} - - return s.cluster.PluginConfig().Create(context.Background(), pluginConfig) -} - -func (s *Scaffold) UpdatePluginConfig(pluginConfig *types.PluginConfig) (*types.PluginConfig, error) { - s.pluginConfigs[pluginConfig.ID] = struct{}{} - - return s.cluster.PluginConfig().Update(context.Background(), pluginConfig) -} - -func (s *Scaffold) DeletePluginConfig(id string) error { - delete(s.pluginConfigs, id) - - return s.cluster.PluginConfig().Delete(context.Background(), id) -} - -func (s *Scaffold) GetConsumerGroup(id string) (*types.ConsumerGroup, error) { - return s.cluster.ConsumerGroup().Get(context.Background(), id) -} - -func (s *Scaffold) ListConsumerGroup() ([]*types.ConsumerGroup, error) { - return s.cluster.ConsumerGroup().List(context.Background()) -} - -func (s *Scaffold) CreateConsumerGroup(consumerGroup *types.ConsumerGroup) (*types.ConsumerGroup, error) { - s.consumerGroups[consumerGroup.ID] = struct{}{} - - return s.cluster.ConsumerGroup().Create(context.Background(), consumerGroup) -} - -func (s *Scaffold) UpdateConsumerGroup(consumerGroup *types.ConsumerGroup) (*types.ConsumerGroup, error) { - s.consumerGroups[consumerGroup.ID] = struct{}{} - - return s.cluster.ConsumerGroup().Update(context.Background(), consumerGroup) -} - -func (s *Scaffold) DeleteConsumerGroup(id string) error { - delete(s.consumerGroups, id) - - return s.cluster.ConsumerGroup().Delete(context.Background(), id) -} - -func (s *Scaffold) GetPluginMetadata(id string) (*types.PluginMetadata, error) { - return s.cluster.PluginMetadata().Get(context.Background(), id) -} - -func (s *Scaffold) ListPluginMetadata() ([]*types.PluginMetadata, error) { - return s.cluster.PluginMetadata().List(context.Background()) -} - -func (s *Scaffold) CreatePluginMetadata(pluginMetadata *types.PluginMetadata) (*types.PluginMetadata, error) { - s.pluginMetadatas[pluginMetadata.ID] = struct{}{} - - return s.cluster.PluginMetadata().Create(context.Background(), pluginMetadata) -} - -func (s *Scaffold) UpdatePluginMetadata(pluginMetadata *types.PluginMetadata) (*types.PluginMetadata, error) { - s.pluginMetadatas[pluginMetadata.ID] = struct{}{} - - return s.cluster.PluginMetadata().Update(context.Background(), pluginMetadata) -} - -func (s *Scaffold) DeletePluginMetadata(id string) error { - delete(s.pluginMetadatas, id) - - return s.cluster.PluginMetadata().Delete(context.Background(), id) -} - -func (s *Scaffold) GetStreamRoute(id string) (*types.StreamRoute, error) { - return s.cluster.StreamRoute().Get(context.Background(), id) -} - -func (s *Scaffold) ListStreamRoute() ([]*types.StreamRoute, error) { - return s.cluster.StreamRoute().List(context.Background()) -} - -func (s *Scaffold) CreateStreamRoute(streamRoute *types.StreamRoute) (*types.StreamRoute, error) { - s.streamRoutes[streamRoute.ID] = struct{}{} - - return s.cluster.StreamRoute().Create(context.Background(), streamRoute) -} - -func (s *Scaffold) UpdateStreamRoute(streamRoute *types.StreamRoute) (*types.StreamRoute, error) { - s.streamRoutes[streamRoute.ID] = struct{}{} - - return s.cluster.StreamRoute().Update(context.Background(), streamRoute) -} - -func (s *Scaffold) DeleteStreamRoute(id string) error { - delete(s.streamRoutes, id) - - return s.cluster.StreamRoute().Delete(context.Background(), id) -} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..2217088a --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,39 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "rootDir": ".", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "es2015", + "module": "esnext", + "lib": [ + "es2020", + "dom" + ], + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "baseUrl": ".", + "paths": { + "@api7/adc-sdk": [ + "libs/sdk/src/index.ts" + ], + "@api7/adc-backend-api7": [ + "libs/backend-api7/src/index.ts" + ], + "@api7/adc-backend-apisix": [ + "libs/backend-apisix/src/index.ts" + ], + "@api7/adc-converter-openapi": [ + "libs/converter-openapi/src/index.ts" + ] + } + }, + "exclude": [ + "node_modules", + "tmp" + ] +} diff --git a/utils/goimports-reviser.sh b/utils/goimports-reviser.sh deleted file mode 100755 index 67f235b8..00000000 --- a/utils/goimports-reviser.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -go install github.com/incu6us/goimports-reviser/v2@latest - -PROJECT_NAME=github.com/api7/adc - -find . -name '*.go' -print0 | while IFS= read -r -d '' file; do - goimports-reviser -file-path "$file" -project-name "$PROJECT_NAME" -done diff --git a/utils/quickstart-mtls.sh b/utils/quickstart-mtls.sh deleted file mode 100755 index 52e2f165..00000000 --- a/utils/quickstart-mtls.sh +++ /dev/null @@ -1,185 +0,0 @@ -#!/bin/bash - -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -DEFAULT_ETCD_IMAGE_NAME="bitnami/etcd" -DEFAULT_ETCD_IMAGE_TAG="3.5.7" - -DEFAULT_APISIX_IMAGE_NAME="apache/apisix" -DEFAULT_APISIX_IMAGE_TAG="dev" - -DEFAULT_ETCD_LISTEN_HOST=0.0.0.0 -DEFAULT_ETCD_LISTEN_PORT=2379 - -DEFAULT_APISIX_PORT=9180 - -DEFAULT_ETCD_NAME="etcd-quickstart" -DEFAULT_APP_NAME="apisix-quickstart" -DEFAULT_NET_NAME="apisix-quickstart-net" -DEFAULT_PROMETHEUS_NAME="apisix-quickstart-prometheus" - -usage() { - echo "Runs a Docker based Apache APISIX." - echo - echo "See the document for more information:" - echo " https://docs.api7.ai/apisix/getting-started" - exit 0 -} - -echo_fail() { - printf "\e[31m✘ \e[0m$@\n" -} - -echo_pass() { - printf "\e[32m✔ \e[0m$@\n" -} - -echo_warning() { - printf "\e[33m⚠ $@\e[0m\n" -} - -ensure_docker() { - { - docker ps -q >/dev/null 2>&1 - } || { - return 1 - } -} - -ensure_curl() { - { - curl -h >/dev/null 2>&1 - } || { - return 1 - } -} - -install_apisix() { - - echo "Installing APISIX with the quickstart options." - echo "" - - echo "Creating bridge network ${DEFAULT_NET_NAME}." - - docker network create -d bridge $DEFAULT_NET_NAME && echo_pass "network ${DEFAULT_NET_NAME} created" || { - echo_fail "Create network failed!" - return 1 - } - - echo "" - - echo "Starting the container ${DEFAULT_ETCD_NAME}." - docker run -d \ - --name ${DEFAULT_ETCD_NAME} \ - --network=$DEFAULT_NET_NAME \ - -e ALLOW_NONE_AUTHENTICATION=yes \ - -e ETCD_ADVERTISE_CLIENT_URLS=http://${DEFAULT_ETCD_LISTEN_HOST}:${DEFAULT_ETCD_LISTEN_PORT} \ - ${DEFAULT_ETCD_IMAGE_NAME}:${DEFAULT_ETCD_IMAGE_TAG} && echo_pass "etcd is listening on ${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}" || { - echo_fail "Start etcd failed!" - return 1 - } - - echo "" - - APISIX_DEPLOYMENT_ETCD_HOST="[\"http://${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}\"]" - - echo "Starting the container ${DEFAULT_APP_NAME}." - docker run -d \ - --name ${DEFAULT_APP_NAME} \ - --network=$DEFAULT_NET_NAME \ - -v ./test/mtls/certs:/certs \ - -p9080:9080 -p9180:9180 -p9443:9443 -p9090:9092 \ - -e APISIX_DEPLOYMENT_ETCD_HOST=${APISIX_DEPLOYMENT_ETCD_HOST} \ - ${DEFAULT_APISIX_IMAGE_NAME}:${DEFAULT_APISIX_IMAGE_TAG} && validate_apisix && sleep 2 || { - echo_fail "Start APISIX failed!" - return 1 - } - - docker exec ${DEFAULT_APP_NAME} /bin/bash -c "echo ' -apisix: - enable_control: true - control: - ip: "0.0.0.0" - port: 9092 -deployment: - role: traditional - role_traditional: - config_provider: etcd - admin: - admin_key_required: false - allow_admin: - - 0.0.0.0/0 - https_admin: true - admin_api_mtls: - admin_ssl_cert: /certs/server.cert - admin_ssl_cert_key: /certs/server.key - admin_ssl_ca_cert: /certs/ca.ca -plugin_attr: - prometheus: - export_addr: - ip: 0.0.0.0 - port: 9091 - ' > /usr/local/apisix/conf/config.yaml" - docker exec ${DEFAULT_APP_NAME} apisix reload >>/dev/null 2>&1 - - echo_warning "WARNING: The Admin API key is currently disabled. You should turn on admin_key_required and set a strong Admin API key in production for security." - - echo "" -} - -destroy_apisix() { - echo "Destroying existing ${DEFAULT_APP_NAME} container, if any." - echo "" - docker rm -f $DEFAULT_APP_NAME >>/dev/null 2>&1 - docker rm -f $DEFAULT_ETCD_NAME >>/dev/null 2>&1 - docker rm -f $DEFAULT_PROMETHEUS_NAME >>/dev/null 2>&1 - docker network rm $DEFAULT_NET_NAME >>/dev/null 2>&1 - sleep 2 - - while docker container inspect $DEFAULT_APP_NAME >/dev/null 2>&1; do sleep 1; done && - while docker container inspect $DEFAULT_ETCD_NAME >/dev/null 2>&1; do sleep 1; done && - while docker container inspect $DEFAULT_PROMETHEUS_NAME >/dev/null 2>&1; do sleep 1; done && - while docker network inspect $DEFAULT_NET_NAME >/dev/null 2>&1; do sleep 1; done -} - -validate_apisix() { - local rv=0 - retry 30 curl "http://localhost:${DEFAULT_APISIX_PORT}/apisix/admin/services" >>/dev/null 2>&1 && echo_pass "APISIX is up" || rv=$? -} - -main() { - ensure_docker || { - echo_fail "Docker is not available, please install it first" - exit 1 - } - - ensure_curl || { - echo_fail "curl is not available, please install it first" - exit 1 - } - - destroy_apisix - - install_apisix || { - exit 1 - } - - echo_pass "APISIX is ready!" -} - -main "$@" diff --git a/utils/quickstart.sh b/utils/quickstart.sh deleted file mode 100644 index 5d73d540..00000000 --- a/utils/quickstart.sh +++ /dev/null @@ -1,184 +0,0 @@ -#!/bin/bash - -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -DEFAULT_ETCD_IMAGE_NAME="bitnami/etcd" -DEFAULT_ETCD_IMAGE_TAG="3.5.7" - -DEFAULT_APISIX_IMAGE_NAME="apache/apisix" -DEFAULT_APISIX_IMAGE_TAG="dev" - -DEFAULT_ETCD_LISTEN_HOST=0.0.0.0 -DEFAULT_ETCD_LISTEN_PORT=2379 - -DEFAULT_APISIX_PORT=9180 - -DEFAULT_ETCD_NAME="etcd-quickstart" -DEFAULT_APP_NAME="apisix-quickstart" -DEFAULT_NET_NAME="apisix-quickstart-net" -DEFAULT_PROMETHEUS_NAME="apisix-quickstart-prometheus" - -usage() { - echo "Runs a Docker based Apache APISIX." - echo - echo "See the document for more information:" - echo " https://docs.api7.ai/apisix/getting-started" - exit 0 -} - -echo_fail() { - printf "\e[31m✘ \e[0m$@\n" -} - -echo_pass() { - printf "\e[32m✔ \e[0m$@\n" -} - -echo_warning() { - printf "\e[33m⚠ $@\e[0m\n" -} - -ensure_docker() { - { - docker ps -q >/dev/null 2>&1 - } || { - return 1 - } -} - -ensure_curl() { - { - curl -h >/dev/null 2>&1 - } || { - return 1 - } -} - -install_apisix() { - - echo "Installing APISIX with the quickstart options." - echo "" - - echo "Creating bridge network ${DEFAULT_NET_NAME}." - - docker network create -d bridge $DEFAULT_NET_NAME && echo_pass "network ${DEFAULT_NET_NAME} created" || { - echo_fail "Create network failed!" - return 1 - } - - echo "" - - echo "Starting the container ${DEFAULT_ETCD_NAME}." - docker run -d \ - --name ${DEFAULT_ETCD_NAME} \ - --network=$DEFAULT_NET_NAME \ - -e ALLOW_NONE_AUTHENTICATION=yes \ - -e ETCD_ADVERTISE_CLIENT_URLS=http://${DEFAULT_ETCD_LISTEN_HOST}:${DEFAULT_ETCD_LISTEN_PORT} \ - ${DEFAULT_ETCD_IMAGE_NAME}:${DEFAULT_ETCD_IMAGE_TAG} && echo_pass "etcd is listening on ${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}" || { - echo_fail "Start etcd failed!" - return 1 - } - - echo "" - - APISIX_DEPLOYMENT_ETCD_HOST="[\"http://${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}\"]" - - echo "Starting the container ${DEFAULT_APP_NAME}." - docker run -d \ - --name ${DEFAULT_APP_NAME} \ - --network=$DEFAULT_NET_NAME \ - -p9080:9080 -p9180:9180 -p9443:9443 -p9090:9092 \ - -e APISIX_DEPLOYMENT_ETCD_HOST=${APISIX_DEPLOYMENT_ETCD_HOST} \ - ${DEFAULT_APISIX_IMAGE_NAME}:${DEFAULT_APISIX_IMAGE_TAG} && validate_apisix && sleep 2 || { - echo_fail "Start APISIX failed!" - return 1 - } - - docker exec ${DEFAULT_APP_NAME} /bin/bash -c "echo ' -apisix: - enable_control: true - control: - ip: "0.0.0.0" - port: 9092 - proxy_mode: http&stream - stream_proxy: # TCP/UDP L4 proxy - only: true # Enable L4 proxy only without L7 proxy. - tcp: - - addr: 9100 # Set the TCP proxy listening ports. - tls: true - - addr: "127.0.0.1:9101" - udp: # Set the UDP proxy listening ports. - - 9200 - - "127.0.0.1:9201" -deployment: - role: traditional - role_traditional: - config_provider: etcd - admin: - admin_key_required: false - allow_admin: - - 0.0.0.0/0 -plugin_attr: - prometheus: - export_addr: - ip: 0.0.0.0 - port: 9091 - ' > /usr/local/apisix/conf/config.yaml" - docker exec ${DEFAULT_APP_NAME} apisix reload >>/dev/null 2>&1 - - echo_warning "WARNING: The Admin API key is currently disabled. You should turn on admin_key_required and set a strong Admin API key in production for security." - - echo "" -} - -destroy_apisix() { - echo "Destroying existing ${DEFAULT_APP_NAME} container, if any." - echo "" - docker rm -f $DEFAULT_APP_NAME >>/dev/null 2>&1 - docker rm -f $DEFAULT_ETCD_NAME >>/dev/null 2>&1 - docker rm -f $DEFAULT_PROMETHEUS_NAME >>/dev/null 2>&1 - docker network rm $DEFAULT_NET_NAME >>/dev/null 2>&1 - sleep 2 -} - -validate_apisix() { - local rv=0 - retry 30 curl "http://localhost:${DEFAULT_APISIX_PORT}/apisix/admin/services" >>/dev/null 2>&1 && echo_pass "APISIX is up" || rv=$? -} - -main() { - ensure_docker || { - echo_fail "Docker is not available, please install it first" - exit 1 - } - - ensure_curl || { - echo_fail "curl is not available, please install it first" - exit 1 - } - - destroy_apisix - - install_apisix || { - exit 1 - } - - echo_pass "APISIX is ready!" -} - -main "$@" From 2804ab614fa9f0d65158ede31e56bf41004d784a Mon Sep 17 00:00:00 2001 From: Zeping Bai Date: Sun, 14 Jul 2024 21:01:01 +0800 Subject: [PATCH 2/3] feat: upgrade nx to 19.4.3 (#144) --- .eslintrc.json | 10 +- .github/workflows/e2e.yaml | 7 +- .github/workflows/release.yaml | 6 +- .github/workflows/unit.yaml | 4 +- .gitignore | 1 + .prettierignore | 3 +- apps/cli/scripts/download-node.ts | 2 +- .../e2e/default-value.e2e-spec.ts | 4 +- libs/backend-api7/e2e/ping.e2e-spec.ts | 8 +- libs/backend-api7/e2e/support/global-setup.ts | 4 +- .../e2e/sync-and-dump-1.e2e-spec.ts | 4 +- .../e2e/sync-and-dump-2.e2e-spec.ts | 4 +- libs/backend-api7/project.json | 3 +- package.json | 27 +- pnpm-lock.yaml | 707 +++++++++--------- tsconfig.base.json | 26 +- 16 files changed, 410 insertions(+), 410 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 0be733b7..46b63e71 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -24,12 +24,18 @@ { "files": ["*.ts", "*.tsx"], "extends": ["plugin:@nx/typescript"], - "rules": {} + "rules": { + "@typescript-eslint/no-extra-semi": "error", + "no-extra-semi": "off" + } }, { "files": ["*.js", "*.jsx"], "extends": ["plugin:@nx/javascript"], - "rules": {} + "rules": { + "@typescript-eslint/no-extra-semi": "error", + "no-extra-semi": "off" + } }, { "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 7a21adc0..a884f74f 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -6,6 +6,7 @@ on: pull_request: branches: - main + types: [opened, synchronize, reopened, labeled] jobs: apisix: runs-on: ubuntu-latest @@ -38,7 +39,7 @@ jobs: if: contains(github.event.pull_request.labels.*.name, 'test/api7') strategy: matrix: - version: [3.2.13.8, 3.2.14.0] + version: [3.2.13.0, 3.2.14.0] env: BACKEND_API7_DOWNLOAD_URL: https://run.api7.ai/api7-ee/api7-ee-v${{ matrix.version }}.tar.gz BACKEND_API7_LICENSE: ${{ secrets.BACKEND_API7_LICENSE }} @@ -49,9 +50,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 'lts/*' - - uses: pnpm/action-setup@v2 - with: - version: latest + - uses: pnpm/action-setup@v4 - name: Install dependencies run: pnpm install diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 09f20f9f..5025a6b1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -15,10 +15,10 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 'lts/*' - - uses: pnpm/action-setup@v2 - with: - version: latest + - uses: pnpm/action-setup@v4 - name: Build ADC + env: + NODE_VERSION: 20.15.1 run: | pnpm install NODE_ENV=production npx nx build cli diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index 61f2b711..44bbf623 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -14,9 +14,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 'lts/*' - - uses: pnpm/action-setup@v2 - with: - version: latest + - uses: pnpm/action-setup@v4 - name: Install dependencies run: pnpm install diff --git a/.gitignore b/.gitignore index 4b9a2e32..eb0820ea 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,6 @@ testem.log Thumbs.db .nx/cache +.nx/workspace-data .env api7-ee diff --git a/.prettierignore b/.prettierignore index d155fdbd..84da2cd7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ # Add files here to ignore them from prettier formatting /dist /coverage -/.nx/cache \ No newline at end of file +/.nx/cache +/.nx/workspace-data diff --git a/apps/cli/scripts/download-node.ts b/apps/cli/scripts/download-node.ts index 1c654193..c3fe415f 100644 --- a/apps/cli/scripts/download-node.ts +++ b/apps/cli/scripts/download-node.ts @@ -2,7 +2,7 @@ import { execSync } from 'child_process'; import { cpSync, existsSync, mkdirSync, rmSync, unlinkSync } from 'fs'; import { Listr } from 'listr2'; -const version = process.env.NODE_VERSION ?? '20.12.2'; +const version = process.env.NODE_VERSION ?? '20.15.1'; const tasks = new Listr([ { diff --git a/libs/backend-api7/e2e/default-value.e2e-spec.ts b/libs/backend-api7/e2e/default-value.e2e-spec.ts index e55c3a5e..27b8d32a 100644 --- a/libs/backend-api7/e2e/default-value.e2e-spec.ts +++ b/libs/backend-api7/e2e/default-value.e2e-spec.ts @@ -6,8 +6,8 @@ describe('Default Value', () => { beforeAll(() => { backend = new BackendAPI7({ - server: globalThis.server, - token: globalThis.token, + server: process.env.SERVER, + token: process.env.TOKEN, tlsSkipVerify: true, gatewayGroup: 'default', }); diff --git a/libs/backend-api7/e2e/ping.e2e-spec.ts b/libs/backend-api7/e2e/ping.e2e-spec.ts index c1cbbc4f..738404ba 100644 --- a/libs/backend-api7/e2e/ping.e2e-spec.ts +++ b/libs/backend-api7/e2e/ping.e2e-spec.ts @@ -3,8 +3,8 @@ import { BackendAPI7 } from '../src'; describe('Ping', () => { it('should success', async () => { const backend = new BackendAPI7({ - server: globalThis.server, - token: globalThis.token, + server: process.env.SERVER, + token: process.env.TOKEN, tlsSkipVerify: true, }); await backend.ping(); @@ -22,8 +22,8 @@ describe('Ping', () => { it('should failed (self-signed certificate)', async () => { const backend = new BackendAPI7({ - server: globalThis.server, - token: globalThis.token, + server: process.env.SERVER, + token: process.env.TOKEN, }); await expect(backend.ping()).rejects.toThrow('self-signed certificate'); }); diff --git a/libs/backend-api7/e2e/support/global-setup.ts b/libs/backend-api7/e2e/support/global-setup.ts index bd0d6b12..c0f30a6d 100644 --- a/libs/backend-api7/e2e/support/global-setup.ts +++ b/libs/backend-api7/e2e/support/global-setup.ts @@ -61,7 +61,7 @@ const generateToken = async () => { { validateStatus: () => true }, ); - globalThis.token = resp.data.value.token; + process.env.TOKEN = resp.data.value.token; }; export default async () => { @@ -69,5 +69,5 @@ export default async () => { await activateAPI7(); await generateToken(); - globalThis.server = 'https://localhost:7443'; + process.env.SERVER = 'https://localhost:7443'; }; diff --git a/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts b/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts index 8f724242..372625a3 100644 --- a/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts +++ b/libs/backend-api7/e2e/sync-and-dump-1.e2e-spec.ts @@ -17,8 +17,8 @@ describe('Sync and Dump - 1', () => { beforeAll(() => { backend = new BackendAPI7({ - server: globalThis.server, - token: globalThis.token, + server: process.env.SERVER, + token: process.env.TOKEN, tlsSkipVerify: true, gatewayGroup: 'default', }); diff --git a/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts b/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts index 0cd83ba5..4795309b 100644 --- a/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts +++ b/libs/backend-api7/e2e/sync-and-dump-2.e2e-spec.ts @@ -10,8 +10,8 @@ describe('Sync and Dump - 2', () => { beforeAll(() => { backend = new BackendAPI7({ - server: globalThis.server, - token: globalThis.token, + server: process.env.SERVER, + token: process.env.TOKEN, tlsSkipVerify: true, gatewayGroup: 'default', }); diff --git a/libs/backend-api7/project.json b/libs/backend-api7/project.json index a714e375..6a4c0f6b 100644 --- a/libs/backend-api7/project.json +++ b/libs/backend-api7/project.json @@ -26,7 +26,8 @@ ], "options": { "jestConfig": "libs/backend-api7/jest.config.e2e.ts", - "passWithNoTests": true + "passWithNoTests": true, + "runInBand": true } } }, diff --git a/package.json b/package.json index 2223f047..dd632927 100644 --- a/package.json +++ b/package.json @@ -5,19 +5,19 @@ "scripts": {}, "private": true, "devDependencies": { - "@nx/esbuild": "18.3.4", - "@nx/eslint": "18.3.4", - "@nx/eslint-plugin": "18.3.4", - "@nx/jest": "18.3.4", - "@nx/js": "18.3.4", - "@nx/node": "18.3.4", - "@nx/webpack": "18.3.4", - "@nx/workspace": "18.3.4", + "@nx/esbuild": "19.4.3", + "@nx/eslint": "19.4.3", + "@nx/eslint-plugin": "19.4.3", + "@nx/jest": "19.4.3", + "@nx/js": "19.4.3", + "@nx/node": "19.4.3", + "@nx/webpack": "19.4.3", + "@nx/workspace": "19.4.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.13", "@svgr/webpack": "^8.1.0", - "@swc-node/register": "~1.8.0", - "@swc/core": "~1.3.107", - "@swc/helpers": "~0.5.11", + "@swc-node/register": "1.9.2", + "@swc/core": "1.5.7", + "@swc/helpers": "0.5.12", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/deep-diff": "^1.0.5", "@types/jest": "^29.5.12", @@ -35,7 +35,7 @@ "eslint-config-prettier": "^9.1.0", "jest": "^29.7.0", "jest-environment-node": "^29.7.0", - "nx": "18.3.4", + "nx": "19.4.3", "openapi-types": "^12.1.3", "prettier": "^3.2.5", "qs": "^6.12.1", @@ -67,5 +67,6 @@ "winston": "^3.13.0", "yaml": "^2.4.2", "zod": "^3.23.8" - } + }, + "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e56f57d..697d1097 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,44 +70,44 @@ importers: version: 3.23.8 devDependencies: '@nx/esbuild': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) '@nx/eslint': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) '@nx/eslint-plugin': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) '@nx/jest': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) '@nx/js': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) '@nx/node': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) '@nx/webpack': - specifier: 18.3.4 - version: 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + specifier: 19.4.3 + version: 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) '@nx/workspace': - specifier: 18.3.4 - version: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + specifier: 19.4.3 + version: 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.13 - version: 0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + version: 0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) '@svgr/webpack': specifier: ^8.1.0 version: 8.1.0(typescript@5.4.5) '@swc-node/register': - specifier: ~1.8.0 - version: 1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5) + specifier: 1.9.2 + version: 1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5) '@swc/core': - specifier: ~1.3.107 - version: 1.3.107(@swc/helpers@0.5.11) + specifier: 1.5.7 + version: 1.5.7(@swc/helpers@0.5.12) '@swc/helpers': - specifier: ~0.5.11 - version: 0.5.11 + specifier: 0.5.12 + version: 0.5.12 '@trivago/prettier-plugin-sort-imports': specifier: ^4.3.0 version: 4.3.0(prettier@3.2.5) @@ -155,13 +155,13 @@ importers: version: 9.1.0(eslint@8.48.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + version: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) jest-environment-node: specifier: ^29.7.0 version: 29.7.0 nx: - specifier: 18.3.4 - version: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + specifier: 19.4.3 + version: 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) openapi-types: specifier: ^12.1.3 version: 12.1.3 @@ -173,19 +173,19 @@ importers: version: 6.12.1 ts-jest: specifier: ^29.1.3 - version: 29.1.3(@babel/core@7.24.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.6))(esbuild@0.19.12)(jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)))(typescript@5.4.5) + version: 29.1.3(@babel/core@7.24.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.6))(esbuild@0.19.12)(jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)))(typescript@5.4.5) ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5) + version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5) typescript: specifier: ^5.4.5 version: 5.4.5 url-loader: specifier: ^4.1.1 - version: 4.1.1(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + version: 4.1.1(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) webpack: specifier: ^5.91.0 - version: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + version: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) zod-to-json-schema: specifier: ^3.23.0 version: 3.23.0(zod@3.23.8) @@ -1178,49 +1178,49 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nrwl/devkit@18.3.4': - resolution: {integrity: sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==} + '@nrwl/devkit@19.4.3': + resolution: {integrity: sha512-1cu4h3aqYR0jgrurqw86ZeK94YYA2b11Klw2rBSvUaK5lEuQz47gImMvLjwkbVfthFp7swn1225DVP/seaAHpg==} - '@nrwl/esbuild@18.3.4': - resolution: {integrity: sha512-XVZdgNn4im0uRvPmQj8KRoS1KjCu0oiJ3AGDIAzlOAvy1gZZQvHPqzbhduIL6v38izMTQNKseXk9PCaY4B8YMQ==} + '@nrwl/esbuild@19.4.3': + resolution: {integrity: sha512-HFoN3yEEkJ+9jQQHu5Rph9G8Gzfx+x6LFn0DITmmbIT3MlUsC1CzNNAuyAxlD/vVngvL61LuBUGf0EuJ+wQjfA==} - '@nrwl/eslint-plugin-nx@18.3.4': - resolution: {integrity: sha512-IiBAxWgOZC4dIqnirpoRJ0YvDeR9HjlOyKna2CZoUj4Dr5uYOiNHbxVbfLPhsIenRLEbjttCxrA9Mm9k++bdjA==} + '@nrwl/eslint-plugin-nx@19.4.3': + resolution: {integrity: sha512-Nj6/kakhxwg87t41Q2C5qZz9gYr9//jMLw/tjjzrlbFVXzh48ZlM1ppK3cSTWs+oYBOGZbKmkUDx9PxT/nx0bg==} - '@nrwl/jest@18.3.4': - resolution: {integrity: sha512-pIYd8WBQz6DKfhIKkZn9VsNBPR0QGnbAdI9AfrQPoGfj19x3tzqLSjcg/O5UvIs6174U1b+0ccxWmQvFep22Kw==} + '@nrwl/jest@19.4.3': + resolution: {integrity: sha512-bxqXr6B71oDmNS0UeEsTS87AUxfa3fiHpefDmDvbJQBHMTC1aFrmZqo/r/pGnr4Xy3YbkJ3g3JagVq5+LfTILw==} - '@nrwl/js@18.3.4': - resolution: {integrity: sha512-oyiMoxzDVGQe5E4UFGO/WAOU211HEIdRxSEOfs1lPhvA8lKbUa0IWDqPOugNws/YHAr+vUTU3sZDJ3uU3RJuYQ==} + '@nrwl/js@19.4.3': + resolution: {integrity: sha512-eAEX9wCxF51sS1eB4lXvjpwC/vY3Gm8H6EdFbj3zV5ScHE97TtfWR5Py/TbE6lljfUOiSr6EzPGM4YKHKKC4uA==} - '@nrwl/node@18.3.4': - resolution: {integrity: sha512-33u3nST6w/mheKjWb+wkdS4EcReS3fqXOtRo50NFcDGwVQIIA+xpQwEh8idjXUcHmwIPqn1oG76qsexpEQdS8w==} + '@nrwl/node@19.4.3': + resolution: {integrity: sha512-tCwbOCbiRineXJatgjt6IzybAosiAdGm/L6oh4RJtBT7xAvOYofHYI6ypyA9MoH5okqEkiI9n7RtemmcI+kc/g==} - '@nrwl/tao@18.3.4': - resolution: {integrity: sha512-+7KsDYmGj1cvNaXZcjSYOPN1h17hsGFBtVX7MqnpJLLkQTUhKg2rQxqyluzshJ+RoDUVtYPGyHg1AizlB66RIA==} + '@nrwl/tao@19.4.3': + resolution: {integrity: sha512-edZQTC6M5lj1A8B0gmKCaYcyL8n/CPr0jZ9j3zlwwvUoPvdbCroskD0eb7wsc6l83y31I6af+q7eTbFsWeC0vg==} hasBin: true - '@nrwl/webpack@18.3.4': - resolution: {integrity: sha512-l4vWrum8nLBtDqeLM5BluKZScKSRegU3fvXmH4JJ+KY6ovVadZzcbM7SPiFKcBvIzzp034dajbp0iN/RJFSWlg==} + '@nrwl/webpack@19.4.3': + resolution: {integrity: sha512-agrAQaCbUQKojsE2U2h8cxjWD+hZl0qZqROkzH8WPmnKrR+FrRCGsyEuDhf7hg0xUQXmcytUZPI0mA+rbxiXlg==} - '@nrwl/workspace@18.3.4': - resolution: {integrity: sha512-ziPHZcSYj46aPYrRHaKu56/SmYCijLT5vIm/UaoWD5v5Fy5CRigO/ezUImsHGHMEZWfHt44s4jsv7QdJWAXe7w==} + '@nrwl/workspace@19.4.3': + resolution: {integrity: sha512-DsimNVxPA3dNMF2iWHV40TPoBxs54Q2xU+R2BfEEZALalqplQ9H3UrZ3KSYRlsIjkRCvH0QL6bOBy5sIbo5QFQ==} - '@nx/devkit@18.3.4': - resolution: {integrity: sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==} + '@nx/devkit@19.4.3': + resolution: {integrity: sha512-Yf+Is6QpwGVTUJacg1lEispC7wRZMF1Td1rlMK4m/quZCVGcJ4nPxma0fhsLs6qGIK3RYa1qoGEH1gsG8W3w1g==} peerDependencies: - nx: '>= 16 <= 19' + nx: '>= 17 <= 20' - '@nx/esbuild@18.3.4': - resolution: {integrity: sha512-W+rHeHzyARGp5wBsXQTcWWmooITdKrNSeRMXSL0Hx98jCmV+Mz7ErprNstltNSEB8d51ch4UifGdcMg/yc0dVQ==} + '@nx/esbuild@19.4.3': + resolution: {integrity: sha512-4TVejnuOrsapdkxkUDZBmhcnZWtAAaTDRvG9/hU1ln6HiY0aIU2tc5NmJtqODI3ujLYzX1swwJATwKNchEi4EQ==} peerDependencies: esbuild: ~0.19.2 peerDependenciesMeta: esbuild: optional: true - '@nx/eslint-plugin@18.3.4': - resolution: {integrity: sha512-rGQX/w+qexGWOLjHECZeZ2RDgaKEUvQW+zGBNlw/5u7tZcTYUpG10VG1j+BrwBZ1gT9YV1b/0IUYdwdEo1NNwA==} + '@nx/eslint-plugin@19.4.3': + resolution: {integrity: sha512-tpdld6pvv1OSBGcnhOtWNW28KhqX4HPQ0Ls1PrUILeJKKP26y9BUgzi9gufOP4ajkbJkjKYIiqSbcyW5d8FdQA==} peerDependencies: '@typescript-eslint/parser': ^6.13.2 || ^7.0.0 eslint-config-prettier: ^9.0.0 @@ -1228,96 +1228,97 @@ packages: eslint-config-prettier: optional: true - '@nx/eslint@18.3.4': - resolution: {integrity: sha512-PiAMeI87RD9pi/IvOUWwszNBvwaY/V8fqcUUKIi6uzrzCsaKTmelTlaeJbH/z7Ulzx3iFPx1w1tcagI0ZWDBaQ==} + '@nx/eslint@19.4.3': + resolution: {integrity: sha512-b0QAlWrn/P5QRfqS/Jp1SFZFMpmR1jKGM0Eno70+Jny96re6u6FegARzU6H6v1XcVymQpp9cRdxDX4lieL/bug==} peerDependencies: - js-yaml: 4.1.0 + '@zkochan/js-yaml': 0.0.7 + eslint: ^8.0.0 || ^9.0.0 peerDependenciesMeta: - js-yaml: + '@zkochan/js-yaml': optional: true - '@nx/jest@18.3.4': - resolution: {integrity: sha512-sWbrhz8RYZ7j1uUbyB7tvulnRncNwtnEEMIGYUrHSQB1of+aG+FA2VtI3KCoWrfzMc5EDl7DLpKY1VMW2ArWhQ==} + '@nx/jest@19.4.3': + resolution: {integrity: sha512-GPeMEaFXSQ73L5T8HNsWjrHJ0GQwetNj7ahZ2d1q7HCp8PvVGNEidkyBZu7nMmhDcBEzuT08eqfSejAsVQsrtw==} - '@nx/js@18.3.4': - resolution: {integrity: sha512-+MPacp/B09e5QwaFQBkev9pW862ZpmesqR2lUUnFAXUBKtjYVIAmhJWHOtevqC1om4OxvTsbluYHsbAkAUzlMA==} + '@nx/js@19.4.3': + resolution: {integrity: sha512-HZ/JhgfP6NzobzDyWgez1/DGYtKwtlUj4gGVDlyDzq1NpSACWrytHc6hNWxxfJilYWDscimv0o/X8+xIw7cKFw==} peerDependencies: verdaccio: ^5.0.4 peerDependenciesMeta: verdaccio: optional: true - '@nx/linter@18.3.4': - resolution: {integrity: sha512-hvgsd1JMjRykHheIJOwxDAYLcy6b9wLtBZGV4pdg1Q4himocgG9Rhi/V7ha+hPtV8oq+iIRuNnLPY+UcdK2ovQ==} + '@nx/linter@19.4.3': + resolution: {integrity: sha512-76G/zW31IULKKFiGCCta5tclTl+9+KF6Z2gfTHRayMM4qQxT4KE89JGMltUDZY5bM594mJqC5aiHH9AtVKHZnA==} - '@nx/node@18.3.4': - resolution: {integrity: sha512-w9eCtj820pUWsRsBal/qJ9czWw47K6UHmRZYpjIlOv5/7iwtq8NnFau9doYULsTu7caR27MExrUsKe+vmb4U2A==} + '@nx/node@19.4.3': + resolution: {integrity: sha512-pI7nbMdyR7bKwRA4AGSgc1cAiH51Il3rwPDwkpF2Ib4F/z03sewTt8677xUpO0ZaoaOqaZdXGK4rX1v5VbYK4g==} - '@nx/nx-darwin-arm64@18.3.4': - resolution: {integrity: sha512-MOGk9z4fIoOkJB68diH3bwoWrC8X9IzMNsz1mu0cbVfgCRAfIV3b+lMsiwQYzWal3UWW5DE5Rkss4F8whiV5Uw==} + '@nx/nx-darwin-arm64@19.4.3': + resolution: {integrity: sha512-aostkFmS8HPgnJS3Po55AqtU+O09LC4R79UBa/Pnxjtb7GGM3T7Gk8349RTc/wEWIRi1pS6Yk0GgT3FS59WF3g==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@nx/nx-darwin-x64@18.3.4': - resolution: {integrity: sha512-tSzPRnNB3QdPM+KYiIuRCUtyCwcuIRC95FfP0ZB3WvfDeNxJChEAChNqmCMDE4iFvZhGuze8WqkJuIVdte+lyQ==} + '@nx/nx-darwin-x64@19.4.3': + resolution: {integrity: sha512-aZUEHq0gn+OHYmN0tEQ4yQsx6l5tlCwl0EJIGUaps9o6XunjPnw5qKpmy/aw804HF6pqjSuWMqVWwh3RuAvSJQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@nx/nx-freebsd-x64@18.3.4': - resolution: {integrity: sha512-bjSPak/d+bcR95/pxHMRhnnpHc6MnrQcG6f5AjX15Esm4JdrdQKPBmG1RybuK0WKSyD5wgVhkAGc/QQUom9l8g==} + '@nx/nx-freebsd-x64@19.4.3': + resolution: {integrity: sha512-RDlLUoG1aT9u9Acz8jjsgoaRkge+uTOG11JYUjgDidJ/avB0zgLOpjhLUUH53NLgt5Fc53RDZqzfytzXB/lr9Q==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@nx/nx-linux-arm-gnueabihf@18.3.4': - resolution: {integrity: sha512-/1HnUL7jhH0S7PxJqf6R1pk3QlAU22GY89EQV9fd+RDUtp7IyzaTlkebijTIqfxlSjC4OO3bPizaxEaxdd3uKQ==} + '@nx/nx-linux-arm-gnueabihf@19.4.3': + resolution: {integrity: sha512-2hur4cKowYY1D+y017Yog8V2T0tlMkf/hzjjnyxxsbEXCBSo3mwzbNdaLzXh2kSP9f/d4nyHWJY0VJJed06dFw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@nx/nx-linux-arm64-gnu@18.3.4': - resolution: {integrity: sha512-g/2IaB2bZTKaBNPEf9LxtIXb1XHdhh3VO9PnePIrwkkixPMLN0dTxT5Sttt75lvLP3EU1AUR5w3Aaz2Q1mYtWA==} + '@nx/nx-linux-arm64-gnu@19.4.3': + resolution: {integrity: sha512-bf46gPM7R83+uhdkVeqd7LjU5p9OeXYzE3B66wOHWZag8LVAwvh73sUQU/G5kjyzYiYlow3R5K6Xo1ZlKcNaJg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@nx/nx-linux-arm64-musl@18.3.4': - resolution: {integrity: sha512-MgfKLoEF6I1cCS+0ooFLEjJSSVdCYyCT9Q96IHRJntAEL8u/0GR2OUoBoLC+q1lnbIkJr/uqTJxA2Jh+sJTIbA==} + '@nx/nx-linux-arm64-musl@19.4.3': + resolution: {integrity: sha512-BwjVuws2wTeaNiXsr5oc7vL/f+GY2nir45P5fHN2pvvHg672SkepYvTqLNPbmpl2R5oY0gAgXtzcq3oWIVz4yg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@nx/nx-linux-x64-gnu@18.3.4': - resolution: {integrity: sha512-vbHxv7m3gjthBvw50EYCtgyY0Zg5nVTaQtX+wRsmKybV2i7wHbw5zIe1aL4zHUm6TcPGbIQK+utVM+hyCqKHVA==} + '@nx/nx-linux-x64-gnu@19.4.3': + resolution: {integrity: sha512-7MT1Q+aH84p5QgmrfPqCm83GHJqJv7vuJd+6whdxvoritfh6YdlVH3P75TVByYNXd1qV/Hwx2+diWlwJ3mXiRg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@nx/nx-linux-x64-musl@18.3.4': - resolution: {integrity: sha512-qIJKJCYFRLVSALsvg3avjReOjuYk91Q0hFXMJ2KaEM1Y3tdzcFN0fKBiaHexgbFIUk8zJuS4dJObTqSYMXowbg==} + '@nx/nx-linux-x64-musl@19.4.3': + resolution: {integrity: sha512-LYLQct984GqPMvColo5JyXVsrmsI8vlO64NkUSdCuxgd+qkLbLWpjrH0fPmkaunylrKRBFfIk+2EOV4h/xPgtw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@nx/nx-win32-arm64-msvc@18.3.4': - resolution: {integrity: sha512-UxC8mRkFTPdZbKFprZkiBqVw8624xU38kI0xyooxKlFpt5lccTBwJ0B7+R8p1RoWyvh2DSyFI9VvfD7lczg1lA==} + '@nx/nx-win32-arm64-msvc@19.4.3': + resolution: {integrity: sha512-pDCZ/dqL2AZOghzP+wDFQsI6P407K4jvHif9L5UviRmLMBfiqwvjhfYdJOouRij/h42mkDjahynN2yls3aqyGg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@nx/nx-win32-x64-msvc@18.3.4': - resolution: {integrity: sha512-/RqEjNU9hxIBxRLafCNKoH3SaB2FShf+1ZnIYCdAoCZBxLJebDpnhiyrVs0lPnMj9248JbizEMdJj1+bs/bXig==} + '@nx/nx-win32-x64-msvc@19.4.3': + resolution: {integrity: sha512-rfttenQwx17D4vXchReaAuWRlxweoxNoYIBpiu8Wg47gNXX36dsTG8VZmJ3T96h7aLUT/lmZ9MmqoItzRQrjeQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@nx/webpack@18.3.4': - resolution: {integrity: sha512-bufCGJJ+KvZ90OOCzcDh3hpYz6Zc6b6iNffzaPsJuL5YEOFVdwyn91lWfu761bzBOCqs2DPJenEmAGhSNK+muQ==} + '@nx/webpack@19.4.3': + resolution: {integrity: sha512-93bQ7zk4eZRbOTv0wcXepGGGWKHkibMY5AHkjl86RyAn1GfMu6loO4WLKCLb82VRWNRqPhnYyKvqJ3iGVyhGTQ==} - '@nx/workspace@18.3.4': - resolution: {integrity: sha512-H5HmEOWb9wnrNXfI2DhK6AmMVz1snuJvjT2jcMf9kxlVW0pKGTFW+OyHfSYq6Ni3OGWb1f9O63erLYHo45zPeA==} + '@nx/workspace@19.4.3': + resolution: {integrity: sha512-IjhFOD4FIAghTof9yFgJGrv55nAFrgEkdaE+Fr3GxyeDCy8UBxioL0DJPZIzYsnL2EcDPyyBLFn7aIEAAGneWg==} '@phenomnomnominal/tsquery@5.0.1': resolution: {integrity: sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==} @@ -1459,77 +1460,77 @@ packages: '@swc/core': '>= 1.4.13' '@swc/types': '>= 0.1' - '@swc-node/register@1.8.0': - resolution: {integrity: sha512-8K3589HoBSmVmrEVrtr4K5sWEithpGDzcFGic81OW0A9sZY38IV5EGRODQWCk0SBDyLhaF+pid120vJAtsHo1A==} + '@swc-node/register@1.9.2': + resolution: {integrity: sha512-BBjg0QNuEEmJSoU/++JOXhrjWdu3PTyYeJWsvchsI0Aqtj8ICkz/DqlwtXbmZVZ5vuDPpTfFlwDBZe81zgShMA==} peerDependencies: - '@swc/core': '>= 1.3' + '@swc/core': '>= 1.4.13' typescript: '>= 4.3' - '@swc-node/sourcemap-support@0.4.0': - resolution: {integrity: sha512-weuRmYTO+4yOtHtPZHXlPdA1dJJJp3QOoZAFZ6uZidu992F2X5v1fQdnb26xs1o3Ex/e2sYhRyY5R6NGNuoATQ==} + '@swc-node/sourcemap-support@0.5.1': + resolution: {integrity: sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg==} - '@swc/core-darwin-arm64@1.3.107': - resolution: {integrity: sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==} + '@swc/core-darwin-arm64@1.5.7': + resolution: {integrity: sha512-bZLVHPTpH3h6yhwVl395k0Mtx8v6CGhq5r4KQdAoPbADU974Mauz1b6ViHAJ74O0IVE5vyy7tD3OpkQxL/vMDQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.3.107': - resolution: {integrity: sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==} + '@swc/core-darwin-x64@1.5.7': + resolution: {integrity: sha512-RpUyu2GsviwTc2qVajPL0l8nf2vKj5wzO3WkLSHAHEJbiUZk83NJrZd1RVbEknIMO7+Uyjh54hEh8R26jSByaw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.3.107': - resolution: {integrity: sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==} + '@swc/core-linux-arm-gnueabihf@1.5.7': + resolution: {integrity: sha512-cTZWTnCXLABOuvWiv6nQQM0hP6ZWEkzdgDvztgHI/+u/MvtzJBN5lBQ2lue/9sSFYLMqzqff5EHKlFtrJCA9dQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.3.107': - resolution: {integrity: sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==} + '@swc/core-linux-arm64-gnu@1.5.7': + resolution: {integrity: sha512-hoeTJFBiE/IJP30Be7djWF8Q5KVgkbDtjySmvYLg9P94bHg9TJPSQoC72tXx/oXOgXvElDe/GMybru0UxhKx4g==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.3.107': - resolution: {integrity: sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==} + '@swc/core-linux-arm64-musl@1.5.7': + resolution: {integrity: sha512-+NDhK+IFTiVK1/o7EXdCeF2hEzCiaRSrb9zD7X2Z7inwWlxAntcSuzZW7Y6BRqGQH89KA91qYgwbnjgTQ22PiQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.3.107': - resolution: {integrity: sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==} + '@swc/core-linux-x64-gnu@1.5.7': + resolution: {integrity: sha512-25GXpJmeFxKB+7pbY7YQLhWWjkYlR+kHz5I3j9WRl3Lp4v4UD67OGXwPe+DIcHqcouA1fhLhsgHJWtsaNOMBNg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.3.107': - resolution: {integrity: sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==} + '@swc/core-linux-x64-musl@1.5.7': + resolution: {integrity: sha512-0VN9Y5EAPBESmSPPsCJzplZHV26akC0sIgd3Hc/7S/1GkSMoeuVL+V9vt+F/cCuzr4VidzSkqftdP3qEIsXSpg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.3.107': - resolution: {integrity: sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==} + '@swc/core-win32-arm64-msvc@1.5.7': + resolution: {integrity: sha512-RtoNnstBwy5VloNCvmvYNApkTmuCe4sNcoYWpmY7C1+bPR+6SOo8im1G6/FpNem8AR5fcZCmXHWQ+EUmRWJyuA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.3.107': - resolution: {integrity: sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==} + '@swc/core-win32-ia32-msvc@1.5.7': + resolution: {integrity: sha512-Xm0TfvcmmspvQg1s4+USL3x8D+YPAfX2JHygvxAnCJ0EHun8cm2zvfNBcsTlnwYb0ybFWXXY129aq1wgFC9TpQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.3.107': - resolution: {integrity: sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==} + '@swc/core-win32-x64-msvc@1.5.7': + resolution: {integrity: sha512-tp43WfJLCsKLQKBmjmY/0vv1slVywR5Q4qKjF5OIY8QijaEW7/8VwPyUyVoJZEnDgv9jKtUTG5PzqtIYPZGnyg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.3.107': - resolution: {integrity: sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==} + '@swc/core@1.5.7': + resolution: {integrity: sha512-U4qJRBefIJNJDRCCiVtkfa/hpiZ7w0R6kASea+/KLp+vkus3zcLSB8Ub8SvKgTIxjWpwsKcZlPf5nrv4ls46SQ==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': ^0.5.0 @@ -1540,8 +1541,8 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.11': - resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} + '@swc/helpers@0.5.12': + resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} '@swc/types@0.1.7': resolution: {integrity: sha512-scHWahbHF0eyj3JsxG9CFJgFdFNaVQCNAimBlT6PzS3n/HptxqREjsm4OH6AN3lYcffZYSPxXW8ua2BEHp0lJQ==} @@ -1853,14 +1854,10 @@ packages: resolution: {integrity: sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==} engines: {node: '>=14.15.0'} - '@zkochan/js-yaml@0.0.6': - resolution: {integrity: sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==} + '@zkochan/js-yaml@0.0.7': + resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} hasBin: true - abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - deprecated: Use your platform's native atob() and btoa() methods instead - accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -2553,12 +2550,8 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv-expand@10.0.0: - resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} - engines: {node: '>=12'} - - dotenv@16.3.2: - resolution: {integrity: sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==} + dotenv-expand@11.0.6: + resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==} engines: {node: '>=12'} dotenv@16.4.5: @@ -2864,6 +2857,9 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + front-matter@4.0.2: + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -3690,8 +3686,8 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nx@18.3.4: - resolution: {integrity: sha512-7rOHRyxpnZGJ3pHnwmpoAMHt9hNuwibWhOhPBJDhJVcbQJtGfwcWWyV/iSEnVXwKZ2lfHVE3TwE+gXFdT/GFiw==} + nx@19.4.3: + resolution: {integrity: sha512-RmjV+bnMy7YecgbKYGkt5gVXQXf3Bxja2oOmdUd2EkPx1YbiBQfw6c/RtmgDL2cx2d28Pbq8xNo9zIumX8EiGA==} hasBin: true peerDependencies: '@swc-node/register': ^1.8.0 @@ -4417,11 +4413,11 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} - source-map-loader@3.0.2: - resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} - engines: {node: '>= 12.13.0'} + source-map-loader@5.0.0: + resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} + engines: {node: '>= 18.12.0'} peerDependencies: - webpack: ^5.0.0 + webpack: ^5.72.1 source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -4710,6 +4706,9 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -5947,7 +5946,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -5961,7 +5960,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -6143,15 +6142,15 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@nrwl/devkit@18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + '@nrwl/devkit@19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))': dependencies: - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) transitivePeerDependencies: - nx - '@nrwl/esbuild@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nrwl/esbuild@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: - '@nx/esbuild': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/esbuild': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -6165,9 +6164,9 @@ snapshots: - typescript - verdaccio - '@nrwl/eslint-plugin-nx@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nrwl/eslint-plugin-nx@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: - '@nx/eslint-plugin': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/eslint-plugin': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -6183,9 +6182,9 @@ snapshots: - typescript - verdaccio - '@nrwl/jest@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + '@nrwl/jest@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': dependencies: - '@nx/jest': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/jest': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -6201,9 +6200,9 @@ snapshots: - typescript - verdaccio - '@nrwl/js@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nrwl/js@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -6216,18 +6215,19 @@ snapshots: - typescript - verdaccio - '@nrwl/node@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + '@nrwl/node@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': dependencies: - '@nx/node': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/node': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' - '@swc/core' - '@swc/wasm' - '@types/node' + - '@zkochan/js-yaml' - babel-plugin-macros - debug - - js-yaml + - eslint - node-notifier - nx - supports-color @@ -6235,18 +6235,18 @@ snapshots: - typescript - verdaccio - '@nrwl/tao@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))': + '@nrwl/tao@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))': dependencies: - nx: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + nx: 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) tslib: 2.6.2 transitivePeerDependencies: - '@swc-node/register' - '@swc/core' - debug - '@nrwl/webpack@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nrwl/webpack@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: - '@nx/webpack': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nx/webpack': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) transitivePeerDependencies: - '@babel/traverse' - '@parcel/css' @@ -6275,31 +6275,32 @@ snapshots: - vue-template-compiler - webpack-cli - '@nrwl/workspace@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))': + '@nrwl/workspace@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))': dependencies: - '@nx/workspace': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + '@nx/workspace': 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) transitivePeerDependencies: - '@swc-node/register' - '@swc/core' - debug - '@nx/devkit@18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + '@nx/devkit@19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))': dependencies: - '@nrwl/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nrwl/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) ejs: 3.1.10 enquirer: 2.3.6 ignore: 5.3.1 - nx: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + minimatch: 9.0.3 + nx: 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) semver: 7.6.2 tmp: 0.2.3 tslib: 2.6.2 yargs-parser: 21.1.1 - '@nx/esbuild@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nx/esbuild@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: - '@nrwl/esbuild': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nrwl/esbuild': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) chalk: 4.1.2 fast-glob: 3.2.7 fs-extra: 11.2.0 @@ -6319,11 +6320,11 @@ snapshots: - typescript - verdaccio - '@nx/eslint-plugin@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nx/eslint-plugin@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: - '@nrwl/eslint-plugin-nx': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nrwl/eslint-plugin-nx': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@typescript-eslint/parser@6.21.0(eslint@8.48.0)(typescript@5.4.5))(eslint-config-prettier@9.1.0(eslint@8.48.0))(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) '@typescript-eslint/parser': 6.21.0(eslint@8.48.0)(typescript@5.4.5) '@typescript-eslint/type-utils': 7.10.0(eslint@8.48.0)(typescript@5.4.5) '@typescript-eslint/utils': 7.10.0(eslint@8.48.0)(typescript@5.4.5) @@ -6347,16 +6348,17 @@ snapshots: - typescript - verdaccio - '@nx/eslint@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + '@nx/eslint@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))': dependencies: - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) - '@nx/linter': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) + '@nx/linter': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) eslint: 8.48.0 + semver: 7.6.2 tslib: 2.6.2 typescript: 5.4.5 optionalDependencies: - js-yaml: 4.1.0 + '@zkochan/js-yaml': 0.0.7 transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -6368,17 +6370,17 @@ snapshots: - supports-color - verdaccio - '@nx/jest@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + '@nx/jest@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': dependencies: '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 - '@nrwl/jest': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nrwl/jest': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) '@phenomnomnominal/tsquery': 5.0.1(typescript@5.4.5) chalk: 4.1.2 identity-obj-proxy: 3.0.0 - jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) jest-resolve: 29.7.0 jest-util: 29.7.0 minimatch: 9.0.3 @@ -6400,7 +6402,7 @@ snapshots: - typescript - verdaccio - '@nx/js@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nx/js@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: '@babel/core': 7.24.6 '@babel/plugin-proposal-decorators': 7.24.6(@babel/core@7.24.6) @@ -6409,10 +6411,9 @@ snapshots: '@babel/preset-env': 7.24.6(@babel/core@7.24.6) '@babel/preset-typescript': 7.24.6(@babel/core@7.24.6) '@babel/runtime': 7.24.6 - '@nrwl/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/workspace': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) - '@phenomnomnominal/tsquery': 5.0.1(typescript@5.4.5) + '@nrwl/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/workspace': 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) babel-plugin-const-enum: 1.2.0(@babel/core@7.24.6) babel-plugin-macros: 2.8.0 babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.24.6)(@babel/traverse@7.24.6) @@ -6429,7 +6430,7 @@ snapshots: ora: 5.3.0 semver: 7.6.2 source-map-support: 0.5.19 - ts-node: 10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5) + ts-node: 10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5) tsconfig-paths: 4.2.0 tslib: 2.6.2 transitivePeerDependencies: @@ -6443,28 +6444,29 @@ snapshots: - supports-color - typescript - '@nx/linter@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))': + '@nx/linter@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))': dependencies: - '@nx/eslint': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nx/eslint': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' - '@swc/core' - '@swc/wasm' - '@types/node' + - '@zkochan/js-yaml' - debug - - js-yaml + - eslint - nx - supports-color - verdaccio - '@nx/node@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': + '@nx/node@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5)': dependencies: - '@nrwl/node': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/eslint': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(js-yaml@4.1.0)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/jest': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nrwl/node': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/eslint': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(@zkochan/js-yaml@0.0.7)(eslint@8.48.0)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/jest': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5))(typescript@5.4.5) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) tslib: 2.6.2 transitivePeerDependencies: - '@babel/traverse' @@ -6472,9 +6474,10 @@ snapshots: - '@swc/core' - '@swc/wasm' - '@types/node' + - '@zkochan/js-yaml' - babel-plugin-macros - debug - - js-yaml + - eslint - node-notifier - nx - supports-color @@ -6482,75 +6485,76 @@ snapshots: - typescript - verdaccio - '@nx/nx-darwin-arm64@18.3.4': + '@nx/nx-darwin-arm64@19.4.3': optional: true - '@nx/nx-darwin-x64@18.3.4': + '@nx/nx-darwin-x64@19.4.3': optional: true - '@nx/nx-freebsd-x64@18.3.4': + '@nx/nx-freebsd-x64@19.4.3': optional: true - '@nx/nx-linux-arm-gnueabihf@18.3.4': + '@nx/nx-linux-arm-gnueabihf@19.4.3': optional: true - '@nx/nx-linux-arm64-gnu@18.3.4': + '@nx/nx-linux-arm64-gnu@19.4.3': optional: true - '@nx/nx-linux-arm64-musl@18.3.4': + '@nx/nx-linux-arm64-musl@19.4.3': optional: true - '@nx/nx-linux-x64-gnu@18.3.4': + '@nx/nx-linux-x64-gnu@19.4.3': optional: true - '@nx/nx-linux-x64-musl@18.3.4': + '@nx/nx-linux-x64-musl@19.4.3': optional: true - '@nx/nx-win32-arm64-msvc@18.3.4': + '@nx/nx-win32-arm64-msvc@19.4.3': optional: true - '@nx/nx-win32-x64-msvc@18.3.4': + '@nx/nx-win32-x64-msvc@19.4.3': optional: true - '@nx/webpack@18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5)': + '@nx/webpack@19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5)': dependencies: '@babel/core': 7.24.6 - '@nrwl/webpack': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(esbuild@0.19.12)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) - '@nx/js': 18.3.4(@babel/traverse@7.24.6)(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)))(typescript@5.4.5) + '@nrwl/webpack': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(esbuild@0.19.12)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) + '@nx/js': 19.4.3(@babel/traverse@7.24.6)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.4.5) + '@phenomnomnominal/tsquery': 5.0.1(typescript@5.4.5) ajv: 8.14.0 autoprefixer: 10.4.19(postcss@8.4.38) - babel-loader: 9.1.3(@babel/core@7.24.6)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + babel-loader: 9.1.3(@babel/core@7.24.6)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) browserslist: 4.23.0 chalk: 4.1.2 - copy-webpack-plugin: 10.2.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - css-loader: 6.11.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - css-minimizer-webpack-plugin: 5.0.1(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - fork-ts-checker-webpack-plugin: 7.2.13(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + copy-webpack-plugin: 10.2.4(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + css-loader: 6.11.0(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + css-minimizer-webpack-plugin: 5.0.1(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + fork-ts-checker-webpack-plugin: 7.2.13(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) less: 4.1.3 - less-loader: 11.1.0(less@4.1.3)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - license-webpack-plugin: 4.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + less-loader: 11.1.0(less@4.1.3)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + license-webpack-plugin: 4.0.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) loader-utils: 2.0.4 - mini-css-extract-plugin: 2.4.7(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + mini-css-extract-plugin: 2.4.7(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) parse5: 4.0.0 postcss: 8.4.38 postcss-import: 14.1.0(postcss@8.4.38) - postcss-loader: 6.2.1(postcss@8.4.38)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + postcss-loader: 6.2.1(postcss@8.4.38)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) rxjs: 7.8.1 sass: 1.77.2 - sass-loader: 12.6.0(sass@1.77.2)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - source-map-loader: 3.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - style-loader: 3.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + sass-loader: 12.6.0(sass@1.77.2)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + source-map-loader: 5.0.0(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + style-loader: 3.3.4(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) stylus: 0.59.0 - stylus-loader: 7.1.3(stylus@0.59.0)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) - ts-loader: 9.5.1(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + stylus-loader: 7.1.3(stylus@0.59.0)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + terser-webpack-plugin: 5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + ts-loader: 9.5.1(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) tsconfig-paths-webpack-plugin: 4.0.0 tslib: 2.6.2 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) webpack-node-externals: 3.0.0 - webpack-subresource-integrity: 5.1.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + webpack-subresource-integrity: 5.1.0(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) transitivePeerDependencies: - '@babel/traverse' - '@parcel/css' @@ -6579,13 +6583,13 @@ snapshots: - vue-template-compiler - webpack-cli - '@nx/workspace@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))': + '@nx/workspace@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))': dependencies: - '@nrwl/workspace': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) - '@nx/devkit': 18.3.4(nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11))) + '@nrwl/workspace': 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) + '@nx/devkit': 19.4.3(nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12))) chalk: 4.1.2 enquirer: 2.3.6 - nx: 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + nx: 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) tslib: 2.6.2 yargs-parser: 21.1.1 transitivePeerDependencies: @@ -6598,7 +6602,7 @@ snapshots: esquery: 1.5.0 typescript: 5.4.5 - '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12))': dependencies: ansi-html-community: 0.0.8 core-js-pure: 3.37.1 @@ -6608,10 +6612,10 @@ snapshots: react-refresh: 0.10.0 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) optionalDependencies: type-fest: 3.13.1 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) '@readme/better-ajv-errors@1.6.0(ajv@8.14.0)': dependencies: @@ -6746,16 +6750,16 @@ snapshots: - supports-color - typescript - '@swc-node/core@1.13.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)': + '@swc-node/core@1.13.1(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)': dependencies: - '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@swc/core': 1.5.7(@swc/helpers@0.5.12) '@swc/types': 0.1.7 - '@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5)': + '@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5)': dependencies: - '@swc-node/core': 1.13.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7) - '@swc-node/sourcemap-support': 0.4.0 - '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@swc-node/core': 1.13.1(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7) + '@swc-node/sourcemap-support': 0.5.1 + '@swc/core': 1.5.7(@swc/helpers@0.5.12) colorette: 2.0.20 debug: 4.3.4 pirates: 4.0.6 @@ -6765,61 +6769,61 @@ snapshots: - '@swc/types' - supports-color - '@swc-node/sourcemap-support@0.4.0': + '@swc-node/sourcemap-support@0.5.1': dependencies: source-map-support: 0.5.21 - tslib: 2.6.2 + tslib: 2.6.3 - '@swc/core-darwin-arm64@1.3.107': + '@swc/core-darwin-arm64@1.5.7': optional: true - '@swc/core-darwin-x64@1.3.107': + '@swc/core-darwin-x64@1.5.7': optional: true - '@swc/core-linux-arm-gnueabihf@1.3.107': + '@swc/core-linux-arm-gnueabihf@1.5.7': optional: true - '@swc/core-linux-arm64-gnu@1.3.107': + '@swc/core-linux-arm64-gnu@1.5.7': optional: true - '@swc/core-linux-arm64-musl@1.3.107': + '@swc/core-linux-arm64-musl@1.5.7': optional: true - '@swc/core-linux-x64-gnu@1.3.107': + '@swc/core-linux-x64-gnu@1.5.7': optional: true - '@swc/core-linux-x64-musl@1.3.107': + '@swc/core-linux-x64-musl@1.5.7': optional: true - '@swc/core-win32-arm64-msvc@1.3.107': + '@swc/core-win32-arm64-msvc@1.5.7': optional: true - '@swc/core-win32-ia32-msvc@1.3.107': + '@swc/core-win32-ia32-msvc@1.5.7': optional: true - '@swc/core-win32-x64-msvc@1.3.107': + '@swc/core-win32-x64-msvc@1.5.7': optional: true - '@swc/core@1.3.107(@swc/helpers@0.5.11)': + '@swc/core@1.5.7(@swc/helpers@0.5.12)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.7 optionalDependencies: - '@swc/core-darwin-arm64': 1.3.107 - '@swc/core-darwin-x64': 1.3.107 - '@swc/core-linux-arm-gnueabihf': 1.3.107 - '@swc/core-linux-arm64-gnu': 1.3.107 - '@swc/core-linux-arm64-musl': 1.3.107 - '@swc/core-linux-x64-gnu': 1.3.107 - '@swc/core-linux-x64-musl': 1.3.107 - '@swc/core-win32-arm64-msvc': 1.3.107 - '@swc/core-win32-ia32-msvc': 1.3.107 - '@swc/core-win32-x64-msvc': 1.3.107 - '@swc/helpers': 0.5.11 + '@swc/core-darwin-arm64': 1.5.7 + '@swc/core-darwin-x64': 1.5.7 + '@swc/core-linux-arm-gnueabihf': 1.5.7 + '@swc/core-linux-arm64-gnu': 1.5.7 + '@swc/core-linux-arm64-musl': 1.5.7 + '@swc/core-linux-x64-gnu': 1.5.7 + '@swc/core-linux-x64-musl': 1.5.7 + '@swc/core-win32-arm64-msvc': 1.5.7 + '@swc/core-win32-ia32-msvc': 1.5.7 + '@swc/core-win32-x64-msvc': 1.5.7 + '@swc/helpers': 0.5.12 '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.11': + '@swc/helpers@0.5.12': dependencies: tslib: 2.6.2 @@ -7227,12 +7231,10 @@ snapshots: js-yaml: 3.14.1 tslib: 2.6.2 - '@zkochan/js-yaml@0.0.6': + '@zkochan/js-yaml@0.0.7': dependencies: argparse: 2.0.1 - abab@2.0.6: {} - accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -7363,12 +7365,12 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@9.1.3(@babel/core@7.24.6)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + babel-loader@9.1.3(@babel/core@7.24.6)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: '@babel/core': 7.24.6 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) babel-plugin-const-enum@1.2.0(@babel/core@7.24.6): dependencies: @@ -7707,7 +7709,7 @@ snapshots: dependencies: is-what: 3.14.1 - copy-webpack-plugin@10.2.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + copy-webpack-plugin@10.2.4(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: fast-glob: 3.3.2 glob-parent: 6.0.2 @@ -7715,7 +7717,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.2.0 serialize-javascript: 6.0.2 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) core-js-compat@3.37.1: dependencies: @@ -7750,13 +7752,13 @@ snapshots: optionalDependencies: typescript: 5.4.5 - create-jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + create-jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -7777,7 +7779,7 @@ snapshots: dependencies: postcss: 8.4.38 - css-loader@6.11.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + css-loader@6.11.0(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: icss-utils: 5.1.0(postcss@8.4.38) postcss: 8.4.38 @@ -7788,9 +7790,9 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.2 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) - css-minimizer-webpack-plugin@5.0.1(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + css-minimizer-webpack-plugin@5.0.1(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: '@jridgewell/trace-mapping': 0.3.25 cssnano: 6.1.2(postcss@8.4.38) @@ -7798,7 +7800,7 @@ snapshots: postcss: 8.4.38 schema-utils: 4.2.0 serialize-javascript: 6.0.2 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) optionalDependencies: esbuild: 0.19.12 @@ -7962,9 +7964,9 @@ snapshots: no-case: 3.0.4 tslib: 2.6.2 - dotenv-expand@10.0.0: {} - - dotenv@16.3.2: {} + dotenv-expand@11.0.6: + dependencies: + dotenv: 16.4.5 dotenv@16.4.5: {} @@ -8314,7 +8316,7 @@ snapshots: follow-redirects@1.15.6: {} - fork-ts-checker-webpack-plugin@7.2.13(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + fork-ts-checker-webpack-plugin@7.2.13(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: '@babel/code-frame': 7.24.6 chalk: 4.1.2 @@ -8329,7 +8331,7 @@ snapshots: semver: 7.6.2 tapable: 2.2.1 typescript: 5.4.5 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) form-data@4.0.0: dependencies: @@ -8343,6 +8345,10 @@ snapshots: fresh@0.5.2: {} + front-matter@4.0.2: + dependencies: + js-yaml: 3.14.1 + fs-constants@1.0.0: {} fs-extra@10.1.0: @@ -8699,16 +8705,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + jest-cli@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + create-jest: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -8718,7 +8724,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -8744,7 +8750,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 18.16.9 - ts-node: 10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5) + ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -8970,12 +8976,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)): + jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest-cli: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -9050,11 +9056,11 @@ snapshots: picocolors: 1.0.1 shell-quote: 1.8.1 - less-loader@11.1.0(less@4.1.3)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + less-loader@11.1.0(less@4.1.3)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: klona: 2.0.6 less: 4.1.3 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) less@4.1.3: dependencies: @@ -9077,11 +9083,11 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - license-webpack-plugin@4.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + license-webpack-plugin@4.0.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: webpack-sources: 3.2.3 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) lilconfig@3.1.1: {} @@ -9221,10 +9227,10 @@ snapshots: mimic-fn@2.1.0: {} - mini-css-extract-plugin@2.4.7(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + mini-css-extract-plugin@2.4.7(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: schema-utils: 4.2.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) minimalistic-assert@1.0.1: {} @@ -9305,26 +9311,26 @@ snapshots: dependencies: boolbase: 1.0.0 - nx@18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)): + nx@19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)): dependencies: - '@nrwl/tao': 18.3.4(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.3.107(@swc/helpers@0.5.11)) + '@nrwl/tao': 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) '@yarnpkg/lockfile': 1.1.0 '@yarnpkg/parsers': 3.0.0-rc.46 - '@zkochan/js-yaml': 0.0.6 + '@zkochan/js-yaml': 0.0.7 axios: 1.7.2 chalk: 4.1.2 cli-cursor: 3.1.0 cli-spinners: 2.6.1 cliui: 8.0.1 - dotenv: 16.3.2 - dotenv-expand: 10.0.0 + dotenv: 16.4.5 + dotenv-expand: 11.0.6 enquirer: 2.3.6 figures: 3.2.0 flat: 5.0.2 + front-matter: 4.0.2 fs-extra: 11.2.0 ignore: 5.3.1 jest-diff: 29.7.0 - js-yaml: 4.1.0 jsonc-parser: 3.2.0 lines-and-columns: 2.0.4 minimatch: 9.0.3 @@ -9342,18 +9348,18 @@ snapshots: yargs: 17.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@nx/nx-darwin-arm64': 18.3.4 - '@nx/nx-darwin-x64': 18.3.4 - '@nx/nx-freebsd-x64': 18.3.4 - '@nx/nx-linux-arm-gnueabihf': 18.3.4 - '@nx/nx-linux-arm64-gnu': 18.3.4 - '@nx/nx-linux-arm64-musl': 18.3.4 - '@nx/nx-linux-x64-gnu': 18.3.4 - '@nx/nx-linux-x64-musl': 18.3.4 - '@nx/nx-win32-arm64-msvc': 18.3.4 - '@nx/nx-win32-x64-msvc': 18.3.4 - '@swc-node/register': 1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5) - '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@nx/nx-darwin-arm64': 19.4.3 + '@nx/nx-darwin-x64': 19.4.3 + '@nx/nx-freebsd-x64': 19.4.3 + '@nx/nx-linux-arm-gnueabihf': 19.4.3 + '@nx/nx-linux-arm64-gnu': 19.4.3 + '@nx/nx-linux-arm64-musl': 19.4.3 + '@nx/nx-linux-x64-gnu': 19.4.3 + '@nx/nx-linux-x64-musl': 19.4.3 + '@nx/nx-win32-arm64-msvc': 19.4.3 + '@nx/nx-win32-x64-msvc': 19.4.3 + '@swc-node/register': 1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5) + '@swc/core': 1.5.7(@swc/helpers@0.5.12) transitivePeerDependencies: - debug @@ -9559,13 +9565,13 @@ snapshots: read-cache: 1.0.0 resolve: 1.22.8 - postcss-loader@6.2.1(postcss@8.4.38)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + postcss-loader@6.2.1(postcss@8.4.38)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 postcss: 8.4.38 semver: 7.6.2 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) postcss-merge-longhand@6.0.5(postcss@8.4.38): dependencies: @@ -9883,11 +9889,11 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@12.6.0(sass@1.77.2)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + sass-loader@12.6.0(sass@1.77.2)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: klona: 2.0.6 neo-async: 2.6.2 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) optionalDependencies: sass: 1.77.2 @@ -10043,12 +10049,11 @@ snapshots: source-map-js@1.2.0: {} - source-map-loader@3.0.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + source-map-loader@5.0.0(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: - abab: 2.0.6 iconv-lite: 0.6.3 source-map-js: 1.2.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) source-map-support@0.5.13: dependencies: @@ -10153,9 +10158,9 @@ snapshots: minimist: 1.2.8 through: 2.3.8 - style-loader@3.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + style-loader@3.3.4(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) stylehacks@6.1.1(postcss@8.4.38): dependencies: @@ -10163,12 +10168,12 @@ snapshots: postcss: 8.4.38 postcss-selector-parser: 6.1.0 - stylus-loader@7.1.3(stylus@0.59.0)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + stylus-loader@7.1.3(stylus@0.59.0)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: fast-glob: 3.3.2 normalize-path: 3.0.0 stylus: 0.59.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) stylus@0.59.0: dependencies: @@ -10216,16 +10221,16 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + terser-webpack-plugin@5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) optionalDependencies: - '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@swc/core': 1.5.7(@swc/helpers@0.5.12) esbuild: 0.19.12 terser@5.31.0: @@ -10267,11 +10272,11 @@ snapshots: dependencies: typescript: 5.4.5 - ts-jest@29.1.3(@babel/core@7.24.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.6))(esbuild@0.19.12)(jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)))(typescript@5.4.5): + ts-jest@29.1.3(@babel/core@7.24.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.6))(esbuild@0.19.12)(jest@29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)))(typescript@5.4.5): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5)) + jest: 29.7.0(@types/node@18.16.9)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -10286,7 +10291,7 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.24.6) esbuild: 0.19.12 - ts-loader@9.5.1(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + ts-loader@9.5.1(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.16.1 @@ -10294,9 +10299,9 @@ snapshots: semver: 7.6.2 source-map: 0.7.4 typescript: 5.4.5 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) - ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5): + ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -10314,9 +10319,9 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@swc/core': 1.5.7(@swc/helpers@0.5.12) - ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.11))(@types/node@18.16.9)(typescript@5.4.5): + ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -10334,7 +10339,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.3.107(@swc/helpers@0.5.11) + '@swc/core': 1.5.7(@swc/helpers@0.5.12) tsconfig-paths-webpack-plugin@4.0.0: dependencies: @@ -10350,6 +10355,8 @@ snapshots: tslib@2.6.2: {} + tslib@2.6.3: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -10397,12 +10404,12 @@ snapshots: dependencies: punycode: 2.3.1 - url-loader@4.1.1(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + url-loader@4.1.1(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) util-deprecate@1.0.2: {} @@ -10439,16 +10446,16 @@ snapshots: dependencies: defaults: 1.0.4 - webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) - webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -10478,10 +10485,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) ws: 8.17.0 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) transitivePeerDependencies: - bufferutil - debug @@ -10492,12 +10499,12 @@ snapshots: webpack-sources@3.2.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)): + webpack-subresource-integrity@5.1.0(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): dependencies: typed-assert: 1.0.9 - webpack: 5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12) + webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) - webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12): + webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.5 @@ -10520,7 +10527,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.3.107(@swc/helpers@0.5.11))(esbuild@0.19.12)) + terser-webpack-plugin: 5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: diff --git a/tsconfig.base.json b/tsconfig.base.json index 2217088a..30e04b41 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,30 +10,16 @@ "importHelpers": true, "target": "es2015", "module": "esnext", - "lib": [ - "es2020", - "dom" - ], + "lib": ["es2020", "dom"], "skipLibCheck": true, "skipDefaultLibCheck": true, "baseUrl": ".", "paths": { - "@api7/adc-sdk": [ - "libs/sdk/src/index.ts" - ], - "@api7/adc-backend-api7": [ - "libs/backend-api7/src/index.ts" - ], - "@api7/adc-backend-apisix": [ - "libs/backend-apisix/src/index.ts" - ], - "@api7/adc-converter-openapi": [ - "libs/converter-openapi/src/index.ts" - ] + "@api7/adc-backend-api7": ["libs/backend-api7/src/index.ts"], + "@api7/adc-backend-apisix": ["libs/backend-apisix/src/index.ts"], + "@api7/adc-converter-openapi": ["libs/converter-openapi/src/index.ts"], + "@api7/adc-sdk": ["libs/sdk/src/index.ts"] } }, - "exclude": [ - "node_modules", - "tmp" - ] + "exclude": ["node_modules", "tmp"] } From 0ad4dfbaba4fc61c7caddb3b0e04ddb0f36516e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:01:59 +0000 Subject: [PATCH 3/3] build(deps-dev): bump @pmmmwh/react-refresh-webpack-plugin Bumps [@pmmmwh/react-refresh-webpack-plugin](https://github.com/pmmmwh/react-refresh-webpack-plugin) from 0.5.13 to 0.5.15. - [Release notes](https://github.com/pmmmwh/react-refresh-webpack-plugin/releases) - [Changelog](https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/pmmmwh/react-refresh-webpack-plugin/compare/v0.5.13...v0.5.15) --- updated-dependencies: - dependency-name: "@pmmmwh/react-refresh-webpack-plugin" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index dd632927..f7ebb5c0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@nx/node": "19.4.3", "@nx/webpack": "19.4.3", "@nx/workspace": "19.4.3", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.13", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "@svgr/webpack": "^8.1.0", "@swc-node/register": "1.9.2", "@swc/core": "1.5.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 697d1097..dd095d4f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,8 +94,8 @@ importers: specifier: 19.4.3 version: 19.4.3(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.7(@swc/helpers@0.5.12)) '@pmmmwh/react-refresh-webpack-plugin': - specifier: ^0.5.13 - version: 0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + specifier: ^0.5.15 + version: 0.5.15(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) '@svgr/webpack': specifier: ^8.1.0 version: 8.1.0(typescript@5.4.5) @@ -1050,6 +1050,7 @@ packages: '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -1061,6 +1062,7 @@ packages: '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} @@ -1325,8 +1327,8 @@ packages: peerDependencies: typescript: ^3 || ^4 || ^5 - '@pmmmwh/react-refresh-webpack-plugin@0.5.13': - resolution: {integrity: sha512-odZVYXly+JwzYri9rKqqUAk0cY6zLpv4dxoKinhoJNShV36Gpxf+CyDIILJ4tYsJ1ZxIWs233Y39iVnynvDA/g==} + '@pmmmwh/react-refresh-webpack-plugin@0.5.15': + resolution: {integrity: sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==} engines: {node: '>= 10.13'} peerDependencies: '@types/webpack': 4.x || 5.x @@ -1934,6 +1936,11 @@ packages: engines: {'0': node >= 0.8.0} hasBin: true + ansi-html@0.0.9: + resolution: {integrity: sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==} + engines: {'0': node >= 0.8.0} + hasBin: true + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -6602,15 +6609,15 @@ snapshots: esquery: 1.5.0 typescript: 5.4.5 - '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.15(react-refresh@0.10.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(webpack@5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12))': dependencies: - ansi-html-community: 0.0.8 + ansi-html: 0.0.9 core-js-pure: 3.37.1 error-stack-parser: 2.1.4 html-entities: 2.5.2 loader-utils: 2.0.4 react-refresh: 0.10.0 - schema-utils: 3.3.0 + schema-utils: 4.2.0 source-map: 0.7.4 webpack: 5.91.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) optionalDependencies: @@ -7295,6 +7302,8 @@ snapshots: ansi-html-community@0.0.8: {} + ansi-html@0.0.9: {} + ansi-regex@5.0.1: {} ansi-regex@6.0.1: {}