From e5efcb5bb0cec707cfaa878bfe1b061c08108ca4 Mon Sep 17 00:00:00 2001 From: Bastean Date: Wed, 26 Jun 2024 23:10:01 -0300 Subject: [PATCH] chore: add codexgo v4.3.0 --- .air.toml | 63 + .devcontainer/devcontainer.json | 51 + .gitattributes | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 40 + .github/ISSUE_TEMPLATE/feature_request.md | 19 + .github/actions/setup/action.yml | 23 + .github/workflows/ci.yml | 56 + .github/workflows/release.yml | 64 + .github/workflows/upgrade.yml | 38 + .gitignore | 55 + .husky/commit-msg | 1 + .husky/pre-commit | 1 + .husky/pre-push | 1 + .prettierignore | 55 + .vscode/extensions.json | 3 + CHANGELOG.md | 344 + LICENSE | 21 + Makefile | 265 + README.md | 404 + assets/readme/desktop-dashboard.png | Bin 0 -> 46982 bytes assets/readme/desktop-home.png | Bin 0 -> 41440 bytes assets/readme/logo.png | Bin 0 -> 4820 bytes assets/readme/mail-confirm-account.png | Bin 0 -> 20860 bytes assets/readme/mobile-dashboard.png | Bin 0 -> 36906 bytes assets/readme/mobile-home.png | Bin 0 -> 33344 bytes assets/social-preview/social-preview.png | Bin 0 -> 9688 bytes cmd/codexgo/codex.go | 29 + deployments/.env.demo | 33 + deployments/.env.demo.test.acceptance | 1 + deployments/.env.demo.test.integration | 1 + deployments/.env.example | 33 + deployments/.env.example.demo | 33 + deployments/Dockerfile | 39 + deployments/docker-compose.yml | 77 + deployments/run.sh | 11 + go.mod | 78 + go.sum | 236 + package-lock.json | 16813 ++++++++++++++++ package.json | 138 + pkg/cmd/server/component/layout/index.templ | 71 + .../page/dashboard/form.delete.templ | 88 + .../page/dashboard/form.update.templ | 154 + .../page/dashboard/page.dashboard.templ | 112 + .../component/page/home/form.login.templ | 82 + .../component/page/home/form.register.templ | 148 + .../component/page/home/modal.terms.templ | 28 + .../component/page/home/page.home.templ | 71 + pkg/cmd/server/component/script/body.templ | 3 + .../component/script/fomantic/init.templ | 27 + pkg/cmd/server/component/script/head.templ | 3 + .../server/component/script/jquery/init.templ | 6 + .../component/script/storage/storage.templ | 57 + pkg/cmd/server/features/page/default.feature | 6 + pkg/cmd/server/features/user/create.feature | 21 + pkg/cmd/server/features/user/delete.feature | 30 + pkg/cmd/server/features/user/login.feature | 28 + pkg/cmd/server/features/user/update.feature | 30 + pkg/cmd/server/handler/page/dashboard.go | 39 + pkg/cmd/server/handler/page/default.go | 13 + pkg/cmd/server/handler/page/home.go | 12 + pkg/cmd/server/handler/user/create.go | 34 + pkg/cmd/server/handler/user/delete.go | 45 + pkg/cmd/server/handler/user/login.go | 55 + pkg/cmd/server/handler/user/update.go | 45 + pkg/cmd/server/handler/user/verify.go | 37 + pkg/cmd/server/middleware/authentication.go | 46 + pkg/cmd/server/middleware/cookie.go | 17 + pkg/cmd/server/middleware/error.go | 40 + pkg/cmd/server/middleware/limiter.go | 31 + pkg/cmd/server/middleware/panic.go | 14 + pkg/cmd/server/middleware/security.go | 28 + pkg/cmd/server/package.json | 7 + pkg/cmd/server/router/router.go | 33 + pkg/cmd/server/router/routes.go | 25 + pkg/cmd/server/server.go | 72 + pkg/cmd/server/server_test.go | 156 + .../server/service/authentication/jwt/jwt.go | 14 + .../service/communication/communication.go | 7 + .../service/communication/rabbitmq/binding.go | 15 + .../communication/rabbitmq/consumers.go | 7 + .../communication/rabbitmq/exchange.go | 11 + .../service/communication/rabbitmq/queues.go | 7 + .../communication/rabbitmq/rabbitmq.go | 57 + pkg/cmd/server/service/env/default.go | 48 + pkg/cmd/server/service/errors/errors.go | 35 + pkg/cmd/server/service/logger/logger.go | 13 + .../service/persistence/mongodb/mongodb.go | 30 + pkg/cmd/server/service/service.go | 163 + pkg/cmd/server/service/transport/smtp/smtp.go | 11 + pkg/cmd/server/service/user/consumer.go | 22 + pkg/cmd/server/service/user/handler.go | 92 + pkg/cmd/server/service/user/hashing.go | 7 + pkg/cmd/server/service/user/message.go | 17 + pkg/cmd/server/service/user/repository.go | 18 + pkg/cmd/server/service/user/transport.go | 21 + pkg/cmd/server/service/user/user.go | 35 + .../server/static/assets/apple-touch-icon.png | Bin 0 -> 1716 bytes pkg/cmd/server/static/assets/favicon.png | Bin 0 -> 1716 bytes pkg/cmd/server/static/assets/logo.png | Bin 0 -> 4820 bytes pkg/cmd/server/static/assets/pwa-any.png | Bin 0 -> 4419 bytes pkg/cmd/server/static/assets/pwa-maskable.png | Bin 0 -> 4419 bytes pkg/cmd/server/static/manifest.json | 27 + pkg/cmd/server/static/robots.txt | 2 + pkg/cmd/server/util/errs/binding.go | 26 + pkg/cmd/server/util/errs/missing.go | 15 + pkg/cmd/server/util/key/key.go | 9 + pkg/cmd/server/util/reply/reply.go | 11 + pkg/context/shared/domain/aggregates/root.go | 27 + pkg/context/shared/domain/errors/bubble.go | 52 + pkg/context/shared/domain/errors/bubbleup.go | 9 + pkg/context/shared/domain/errors/default.go | 5 + pkg/context/shared/domain/errors/exist.go | 31 + pkg/context/shared/domain/errors/failure.go | 16 + pkg/context/shared/domain/errors/internal.go | 16 + pkg/context/shared/domain/errors/invalid.go | 16 + pkg/context/shared/domain/errors/meta.go | 3 + pkg/context/shared/domain/errors/panic.go | 9 + pkg/context/shared/domain/errors/wrap.go | 17 + pkg/context/shared/domain/messages/broker.go | 9 + .../shared/domain/messages/consumer.go | 6 + pkg/context/shared/domain/messages/key.go | 74 + .../shared/domain/messages/key_test.go | 41 + pkg/context/shared/domain/messages/message.go | 18 + pkg/context/shared/domain/messages/queue.go | 19 + .../shared/domain/messages/recipient.go | 51 + .../shared/domain/messages/recipient_test.go | 39 + pkg/context/shared/domain/messages/router.go | 5 + pkg/context/shared/domain/models/handler.go | 9 + pkg/context/shared/domain/models/logger.go | 8 + pkg/context/shared/domain/models/transport.go | 5 + pkg/context/shared/domain/models/usecase.go | 5 + pkg/context/shared/domain/models/valueobj.go | 6 + pkg/context/shared/domain/services/context.go | 17 + pkg/context/shared/domain/services/create.go | 23 + pkg/context/shared/domain/types/empty.go | 3 + pkg/context/shared/domain/valueobjs/action.go | 45 + .../shared/domain/valueobjs/action.mother.go | 25 + .../shared/domain/valueobjs/action_test.go | 38 + .../shared/domain/valueobjs/command.go | 45 + .../shared/domain/valueobjs/command.mother.go | 33 + .../shared/domain/valueobjs/command_test.go | 57 + pkg/context/shared/domain/valueobjs/email.go | 42 + pkg/context/shared/domain/valueobjs/entity.go | 45 + .../shared/domain/valueobjs/entity.mother.go | 33 + .../shared/domain/valueobjs/entity_test.go | 57 + pkg/context/shared/domain/valueobjs/event.go | 45 + .../shared/domain/valueobjs/event.mother.go | 33 + .../shared/domain/valueobjs/event_test.go | 57 + pkg/context/shared/domain/valueobjs/id.go | 42 + .../shared/domain/valueobjs/organization.go | 45 + .../domain/valueobjs/organization.mother.go | 33 + .../domain/valueobjs/organization_test.go | 57 + .../shared/domain/valueobjs/service.go | 45 + .../shared/domain/valueobjs/service.mother.go | 33 + .../shared/domain/valueobjs/service_test.go | 57 + pkg/context/shared/domain/valueobjs/status.go | 44 + .../shared/domain/valueobjs/status.mother.go | 25 + .../shared/domain/valueobjs/status_test.go | 38 + pkg/context/shared/domain/valueobjs/type.go | 44 + .../shared/domain/valueobjs/type.mother.go | 25 + .../shared/domain/valueobjs/type_test.go | 38 + .../shared/domain/valueobjs/version.go | 42 + .../shared/domain/valueobjs/version.mother.go | 25 + .../shared/domain/valueobjs/version_test.go | 38 + .../infrastructure/authentications/jwt.go | 52 + .../communications/broker.mock.go | 35 + .../communications/consumer.mock.go | 20 + .../communications/rabbitmq.broker_test.go | 95 + .../infrastructure/communications/rabbitmq.go | 288 + .../shared/infrastructure/loggers/logger.go | 23 + .../infrastructure/loggers/logger.mock.go | 25 + .../infrastructure/persistences/mongo.go | 93 + .../shared/infrastructure/transports/smtp.go | 21 + .../user/application/create/command.go | 5 + .../application/create/command.handler.go | 37 + .../create/command.handler_test.go | 63 + .../user/application/create/command.mother.go | 19 + pkg/context/user/application/create/create.go | 22 + .../application/created/created.consumer.go | 49 + .../created/created.consumer_test.go | 67 + .../user/application/created/created.go | 22 + .../user/application/delete/command.go | 5 + .../application/delete/command.handler.go | 38 + .../delete/command.handler_test.go | 64 + .../user/application/delete/command.mother.go | 15 + pkg/context/user/application/delete/delete.go | 37 + pkg/context/user/application/login/login.go | 31 + pkg/context/user/application/login/query.go | 5 + .../user/application/login/query.handler.go | 40 + .../application/login/query.handler_test.go | 69 + .../user/application/login/query.mother.go | 15 + .../user/application/login/response.go | 6 + pkg/context/user/application/read/query.go | 5 + .../user/application/read/query.handler.go | 30 + .../application/read/query.handler_test.go | 59 + .../user/application/read/query.mother.go | 13 + pkg/context/user/application/read/read.go | 24 + pkg/context/user/application/read/response.go | 6 + .../user/application/update/command.go | 5 + .../application/update/command.handler.go | 52 + .../update/command.handler_test.go | 71 + .../user/application/update/command.mother.go | 21 + pkg/context/user/application/update/update.go | 43 + .../user/application/verify/command.go | 5 + .../application/verify/command.handler.go | 28 + .../verify/command.handler_test.go | 59 + .../user/application/verify/command.mother.go | 13 + pkg/context/user/application/verify/verify.go | 34 + .../user/domain/aggregate/user.mother.go | 26 + .../user/domain/aggregate/user.root.go | 91 + pkg/context/user/domain/event/created.go | 42 + .../user/domain/event/created.mother.go | 27 + pkg/context/user/domain/model/hashing.go | 6 + pkg/context/user/domain/model/repository.go | 18 + pkg/context/user/domain/service/password.go | 17 + pkg/context/user/domain/valueobj/email.go | 10 + .../user/domain/valueobj/email.mother.go | 25 + .../user/domain/valueobj/email_test.go | 38 + pkg/context/user/domain/valueobj/id.go | 10 + pkg/context/user/domain/valueobj/id.mother.go | 26 + pkg/context/user/domain/valueobj/id_test.go | 38 + pkg/context/user/domain/valueobj/password.go | 41 + .../user/domain/valueobj/password.mother.go | 25 + .../user/domain/valueobj/password_test.go | 38 + pkg/context/user/domain/valueobj/username.go | 45 + .../user/domain/valueobj/username.mother.go | 33 + .../user/domain/valueobj/username_test.go | 57 + pkg/context/user/domain/valueobj/verified.go | 38 + .../communication/mail/confirmation.go | 57 + .../communication/mail/confirmation.templ | 48 + .../mail/confirmation.transport_test.go | 49 + .../communication/terminal/confirmation.go | 35 + .../terminal/confirmation.transport_test.go | 54 + .../communication/transport.mock.go | 14 + .../infrastructure/cryptographic/bcrypt.go | 28 + .../cryptographic/bcrypt.hashing_test.go | 49 + .../cryptographic/hashing.mock.go | 19 + .../user/infrastructure/persistence/mongo.go | 215 + .../persistence/mongo.repository_test.go | 137 + .../persistence/repository.mock.go | 37 + scripts/copydeps/copydeps.go | 103 + scripts/syncenv/syncenv.go | 215 + scripts/upgrade/upgrade.go | 72 + test/.gitkeep | 0 244 files changed, 26698 insertions(+) create mode 100644 .air.toml create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/upgrade.yml create mode 100644 .gitignore create mode 100755 .husky/commit-msg create mode 100755 .husky/pre-commit create mode 100755 .husky/pre-push create mode 100644 .prettierignore create mode 100644 .vscode/extensions.json create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 assets/readme/desktop-dashboard.png create mode 100644 assets/readme/desktop-home.png create mode 100644 assets/readme/logo.png create mode 100644 assets/readme/mail-confirm-account.png create mode 100644 assets/readme/mobile-dashboard.png create mode 100644 assets/readme/mobile-home.png create mode 100644 assets/social-preview/social-preview.png create mode 100644 cmd/codexgo/codex.go create mode 100644 deployments/.env.demo create mode 100644 deployments/.env.demo.test.acceptance create mode 100644 deployments/.env.demo.test.integration create mode 100644 deployments/.env.example create mode 100644 deployments/.env.example.demo create mode 100644 deployments/Dockerfile create mode 100644 deployments/docker-compose.yml create mode 100644 deployments/run.sh create mode 100644 go.mod create mode 100644 go.sum create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pkg/cmd/server/component/layout/index.templ create mode 100644 pkg/cmd/server/component/page/dashboard/form.delete.templ create mode 100644 pkg/cmd/server/component/page/dashboard/form.update.templ create mode 100644 pkg/cmd/server/component/page/dashboard/page.dashboard.templ create mode 100644 pkg/cmd/server/component/page/home/form.login.templ create mode 100644 pkg/cmd/server/component/page/home/form.register.templ create mode 100644 pkg/cmd/server/component/page/home/modal.terms.templ create mode 100644 pkg/cmd/server/component/page/home/page.home.templ create mode 100644 pkg/cmd/server/component/script/body.templ create mode 100644 pkg/cmd/server/component/script/fomantic/init.templ create mode 100644 pkg/cmd/server/component/script/head.templ create mode 100644 pkg/cmd/server/component/script/jquery/init.templ create mode 100644 pkg/cmd/server/component/script/storage/storage.templ create mode 100644 pkg/cmd/server/features/page/default.feature create mode 100644 pkg/cmd/server/features/user/create.feature create mode 100644 pkg/cmd/server/features/user/delete.feature create mode 100644 pkg/cmd/server/features/user/login.feature create mode 100644 pkg/cmd/server/features/user/update.feature create mode 100644 pkg/cmd/server/handler/page/dashboard.go create mode 100644 pkg/cmd/server/handler/page/default.go create mode 100644 pkg/cmd/server/handler/page/home.go create mode 100644 pkg/cmd/server/handler/user/create.go create mode 100644 pkg/cmd/server/handler/user/delete.go create mode 100644 pkg/cmd/server/handler/user/login.go create mode 100644 pkg/cmd/server/handler/user/update.go create mode 100644 pkg/cmd/server/handler/user/verify.go create mode 100644 pkg/cmd/server/middleware/authentication.go create mode 100644 pkg/cmd/server/middleware/cookie.go create mode 100644 pkg/cmd/server/middleware/error.go create mode 100644 pkg/cmd/server/middleware/limiter.go create mode 100644 pkg/cmd/server/middleware/panic.go create mode 100644 pkg/cmd/server/middleware/security.go create mode 100644 pkg/cmd/server/package.json create mode 100644 pkg/cmd/server/router/router.go create mode 100644 pkg/cmd/server/router/routes.go create mode 100644 pkg/cmd/server/server.go create mode 100644 pkg/cmd/server/server_test.go create mode 100644 pkg/cmd/server/service/authentication/jwt/jwt.go create mode 100644 pkg/cmd/server/service/communication/communication.go create mode 100644 pkg/cmd/server/service/communication/rabbitmq/binding.go create mode 100644 pkg/cmd/server/service/communication/rabbitmq/consumers.go create mode 100644 pkg/cmd/server/service/communication/rabbitmq/exchange.go create mode 100644 pkg/cmd/server/service/communication/rabbitmq/queues.go create mode 100644 pkg/cmd/server/service/communication/rabbitmq/rabbitmq.go create mode 100644 pkg/cmd/server/service/env/default.go create mode 100644 pkg/cmd/server/service/errors/errors.go create mode 100644 pkg/cmd/server/service/logger/logger.go create mode 100644 pkg/cmd/server/service/persistence/mongodb/mongodb.go create mode 100644 pkg/cmd/server/service/service.go create mode 100644 pkg/cmd/server/service/transport/smtp/smtp.go create mode 100644 pkg/cmd/server/service/user/consumer.go create mode 100644 pkg/cmd/server/service/user/handler.go create mode 100644 pkg/cmd/server/service/user/hashing.go create mode 100644 pkg/cmd/server/service/user/message.go create mode 100644 pkg/cmd/server/service/user/repository.go create mode 100644 pkg/cmd/server/service/user/transport.go create mode 100644 pkg/cmd/server/service/user/user.go create mode 100644 pkg/cmd/server/static/assets/apple-touch-icon.png create mode 100644 pkg/cmd/server/static/assets/favicon.png create mode 100644 pkg/cmd/server/static/assets/logo.png create mode 100644 pkg/cmd/server/static/assets/pwa-any.png create mode 100644 pkg/cmd/server/static/assets/pwa-maskable.png create mode 100644 pkg/cmd/server/static/manifest.json create mode 100644 pkg/cmd/server/static/robots.txt create mode 100644 pkg/cmd/server/util/errs/binding.go create mode 100644 pkg/cmd/server/util/errs/missing.go create mode 100644 pkg/cmd/server/util/key/key.go create mode 100644 pkg/cmd/server/util/reply/reply.go create mode 100644 pkg/context/shared/domain/aggregates/root.go create mode 100644 pkg/context/shared/domain/errors/bubble.go create mode 100644 pkg/context/shared/domain/errors/bubbleup.go create mode 100644 pkg/context/shared/domain/errors/default.go create mode 100644 pkg/context/shared/domain/errors/exist.go create mode 100644 pkg/context/shared/domain/errors/failure.go create mode 100644 pkg/context/shared/domain/errors/internal.go create mode 100644 pkg/context/shared/domain/errors/invalid.go create mode 100644 pkg/context/shared/domain/errors/meta.go create mode 100644 pkg/context/shared/domain/errors/panic.go create mode 100644 pkg/context/shared/domain/errors/wrap.go create mode 100644 pkg/context/shared/domain/messages/broker.go create mode 100644 pkg/context/shared/domain/messages/consumer.go create mode 100644 pkg/context/shared/domain/messages/key.go create mode 100644 pkg/context/shared/domain/messages/key_test.go create mode 100644 pkg/context/shared/domain/messages/message.go create mode 100644 pkg/context/shared/domain/messages/queue.go create mode 100644 pkg/context/shared/domain/messages/recipient.go create mode 100644 pkg/context/shared/domain/messages/recipient_test.go create mode 100644 pkg/context/shared/domain/messages/router.go create mode 100644 pkg/context/shared/domain/models/handler.go create mode 100644 pkg/context/shared/domain/models/logger.go create mode 100644 pkg/context/shared/domain/models/transport.go create mode 100644 pkg/context/shared/domain/models/usecase.go create mode 100644 pkg/context/shared/domain/models/valueobj.go create mode 100644 pkg/context/shared/domain/services/context.go create mode 100644 pkg/context/shared/domain/services/create.go create mode 100644 pkg/context/shared/domain/types/empty.go create mode 100644 pkg/context/shared/domain/valueobjs/action.go create mode 100644 pkg/context/shared/domain/valueobjs/action.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/action_test.go create mode 100644 pkg/context/shared/domain/valueobjs/command.go create mode 100644 pkg/context/shared/domain/valueobjs/command.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/command_test.go create mode 100644 pkg/context/shared/domain/valueobjs/email.go create mode 100644 pkg/context/shared/domain/valueobjs/entity.go create mode 100644 pkg/context/shared/domain/valueobjs/entity.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/entity_test.go create mode 100644 pkg/context/shared/domain/valueobjs/event.go create mode 100644 pkg/context/shared/domain/valueobjs/event.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/event_test.go create mode 100644 pkg/context/shared/domain/valueobjs/id.go create mode 100644 pkg/context/shared/domain/valueobjs/organization.go create mode 100644 pkg/context/shared/domain/valueobjs/organization.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/organization_test.go create mode 100644 pkg/context/shared/domain/valueobjs/service.go create mode 100644 pkg/context/shared/domain/valueobjs/service.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/service_test.go create mode 100644 pkg/context/shared/domain/valueobjs/status.go create mode 100644 pkg/context/shared/domain/valueobjs/status.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/status_test.go create mode 100644 pkg/context/shared/domain/valueobjs/type.go create mode 100644 pkg/context/shared/domain/valueobjs/type.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/type_test.go create mode 100644 pkg/context/shared/domain/valueobjs/version.go create mode 100644 pkg/context/shared/domain/valueobjs/version.mother.go create mode 100644 pkg/context/shared/domain/valueobjs/version_test.go create mode 100644 pkg/context/shared/infrastructure/authentications/jwt.go create mode 100644 pkg/context/shared/infrastructure/communications/broker.mock.go create mode 100644 pkg/context/shared/infrastructure/communications/consumer.mock.go create mode 100644 pkg/context/shared/infrastructure/communications/rabbitmq.broker_test.go create mode 100644 pkg/context/shared/infrastructure/communications/rabbitmq.go create mode 100644 pkg/context/shared/infrastructure/loggers/logger.go create mode 100644 pkg/context/shared/infrastructure/loggers/logger.mock.go create mode 100644 pkg/context/shared/infrastructure/persistences/mongo.go create mode 100644 pkg/context/shared/infrastructure/transports/smtp.go create mode 100644 pkg/context/user/application/create/command.go create mode 100644 pkg/context/user/application/create/command.handler.go create mode 100644 pkg/context/user/application/create/command.handler_test.go create mode 100644 pkg/context/user/application/create/command.mother.go create mode 100644 pkg/context/user/application/create/create.go create mode 100644 pkg/context/user/application/created/created.consumer.go create mode 100644 pkg/context/user/application/created/created.consumer_test.go create mode 100644 pkg/context/user/application/created/created.go create mode 100644 pkg/context/user/application/delete/command.go create mode 100644 pkg/context/user/application/delete/command.handler.go create mode 100644 pkg/context/user/application/delete/command.handler_test.go create mode 100644 pkg/context/user/application/delete/command.mother.go create mode 100644 pkg/context/user/application/delete/delete.go create mode 100644 pkg/context/user/application/login/login.go create mode 100644 pkg/context/user/application/login/query.go create mode 100644 pkg/context/user/application/login/query.handler.go create mode 100644 pkg/context/user/application/login/query.handler_test.go create mode 100644 pkg/context/user/application/login/query.mother.go create mode 100644 pkg/context/user/application/login/response.go create mode 100644 pkg/context/user/application/read/query.go create mode 100644 pkg/context/user/application/read/query.handler.go create mode 100644 pkg/context/user/application/read/query.handler_test.go create mode 100644 pkg/context/user/application/read/query.mother.go create mode 100644 pkg/context/user/application/read/read.go create mode 100644 pkg/context/user/application/read/response.go create mode 100644 pkg/context/user/application/update/command.go create mode 100644 pkg/context/user/application/update/command.handler.go create mode 100644 pkg/context/user/application/update/command.handler_test.go create mode 100644 pkg/context/user/application/update/command.mother.go create mode 100644 pkg/context/user/application/update/update.go create mode 100644 pkg/context/user/application/verify/command.go create mode 100644 pkg/context/user/application/verify/command.handler.go create mode 100644 pkg/context/user/application/verify/command.handler_test.go create mode 100644 pkg/context/user/application/verify/command.mother.go create mode 100644 pkg/context/user/application/verify/verify.go create mode 100644 pkg/context/user/domain/aggregate/user.mother.go create mode 100644 pkg/context/user/domain/aggregate/user.root.go create mode 100644 pkg/context/user/domain/event/created.go create mode 100644 pkg/context/user/domain/event/created.mother.go create mode 100644 pkg/context/user/domain/model/hashing.go create mode 100644 pkg/context/user/domain/model/repository.go create mode 100644 pkg/context/user/domain/service/password.go create mode 100644 pkg/context/user/domain/valueobj/email.go create mode 100644 pkg/context/user/domain/valueobj/email.mother.go create mode 100644 pkg/context/user/domain/valueobj/email_test.go create mode 100644 pkg/context/user/domain/valueobj/id.go create mode 100644 pkg/context/user/domain/valueobj/id.mother.go create mode 100644 pkg/context/user/domain/valueobj/id_test.go create mode 100644 pkg/context/user/domain/valueobj/password.go create mode 100644 pkg/context/user/domain/valueobj/password.mother.go create mode 100644 pkg/context/user/domain/valueobj/password_test.go create mode 100644 pkg/context/user/domain/valueobj/username.go create mode 100644 pkg/context/user/domain/valueobj/username.mother.go create mode 100644 pkg/context/user/domain/valueobj/username_test.go create mode 100644 pkg/context/user/domain/valueobj/verified.go create mode 100644 pkg/context/user/infrastructure/communication/mail/confirmation.go create mode 100644 pkg/context/user/infrastructure/communication/mail/confirmation.templ create mode 100644 pkg/context/user/infrastructure/communication/mail/confirmation.transport_test.go create mode 100644 pkg/context/user/infrastructure/communication/terminal/confirmation.go create mode 100644 pkg/context/user/infrastructure/communication/terminal/confirmation.transport_test.go create mode 100644 pkg/context/user/infrastructure/communication/transport.mock.go create mode 100644 pkg/context/user/infrastructure/cryptographic/bcrypt.go create mode 100644 pkg/context/user/infrastructure/cryptographic/bcrypt.hashing_test.go create mode 100644 pkg/context/user/infrastructure/cryptographic/hashing.mock.go create mode 100644 pkg/context/user/infrastructure/persistence/mongo.go create mode 100644 pkg/context/user/infrastructure/persistence/mongo.repository_test.go create mode 100644 pkg/context/user/infrastructure/persistence/repository.mock.go create mode 100644 scripts/copydeps/copydeps.go create mode 100644 scripts/syncenv/syncenv.go create mode 100644 scripts/upgrade/upgrade.go create mode 100644 test/.gitkeep diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..26569b4 --- /dev/null +++ b/.air.toml @@ -0,0 +1,63 @@ +root = "." +tmp_dir = "tmp" +testdata_dir = "testdata" + +[build] + pre_cmd = ["make generate-required"] + cmd = "go build -buildvcs=false -o ./tmp/main ./cmd/codexgo" + post_cmd = [] + bin = "./tmp/main" + full_bin = "" + args_bin = [] + include_ext = [ + "go", + "html", + "templ", + "tmpl", + "tpl" + ] + exclude_dir = [] + include_dir = [ + "pkg" + ] + include_file = [] + exclude_file = [] + exclude_regex = [ + "^.*_test\\.go$", + "^.*\\.mother\\.go$", + "^.*\\.mock\\.go$", + "^.*_templ\\.go$" + ] + exclude_unchanged = true + follow_symlink = true + log = "air.log" + poll = false + poll_interval = 1000 + delay = 1000 + stop_on_error = false + send_interrupt = true + kill_delay = 1000 + rerun = false + rerun_delay = 1000 + +[log] + time = true + main_only = false + +[color] + main = "magenta" + watcher = "cyan" + build = "yellow" + runner = "green" + +[misc] + clean_on_exit = true + +[screen] + clear_on_rebuild = true + keep_scroll = true + +[proxy] + enabled = true + proxy_port = 8090 + app_port = 8080 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..78e4f65 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,51 @@ +{ + "name": "codexgo", + "image": "mcr.microsoft.com/devcontainers/base:bookworm", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/go:1": {}, + "ghcr.io/devcontainers/features/node:1": {} + }, + "postCreateCommand": "make init", + "customizations": { + "vscode": { + "extensions": [ + "a-h.templ", + "aaron-bond.better-comments", + "CucumberOpen.cucumber-official", + "esbenp.prettier-vscode", + "github.vscode-github-actions", + "golang.go", + "Gruntfuggly.todo-tree", + "ms-azuretools.vscode-docker", + "ms-vscode.makefile-tools", + "redhat.vscode-yaml", + "streetsidesoftware.code-spell-checker", + "tamasfe.even-better-toml", + "thejltres.fomantic-ui-snippets" + ], + "settings": { + "[cucumber]": { + "editor.defaultFormatter": "CucumberOpen.cucumber-official" + }, + "[go]": { + "editor.defaultFormatter": "golang.go" + }, + "[templ]": { + "editor.defaultFormatter": "a-h.templ" + }, + "cSpell.language": "en,lorem", + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "emmet.includeLanguages": { + "templ": "html" + }, + "go.toolsManagement.autoUpdate": true, + "gopls": { + "ui.semanticTokens": true + } + } + } + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..9b77ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "" +labels: "" +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..2bc5d5f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "" +labels: "" +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..8812e98 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,23 @@ +name: Setup + +description: Setup & Caching Dependencies + +runs: + using: "composite" + + steps: + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + cache-dependency-path: "go.sum" + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version-file: "package.json" + cache: "npm" + + - name: Install Dependencies + shell: ${{ runner.os == 'Windows' && 'pwsh' || 'bash' }} + run: make init diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..64f9d8f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: [main, ci/**] + pull_request: + branches: [main] + +jobs: + TruffleHog: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Secret Scanning + uses: trufflesecurity/trufflehog@main + with: + extra_args: --only-verified + + Lint: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + + - name: Check + run: make lint-check + + Test: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + + - name: Unit + run: make test-unit + + - name: Upload Report + uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: Test Report + path: ./test/report + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c5bddab --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,64 @@ +name: Release + +on: + workflow_dispatch: + inputs: + status: + type: choice + description: Status + options: + - alpha + - beta + - stable + default: stable + + bump: + type: choice + description: Bump + options: + - patch + - minor + - major + - auto + default: auto + + dry: + type: boolean + description: Dry Release + default: false + +jobs: + Generate: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup + uses: ./.github/actions/setup + + - name: Import GPG Key + id: import-gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }} + git_config_global: true + git_user_signingkey: true + git_commit_gpgsign: true + git_tag_gpgsign: true + + - name: Generate Release + run: OPTIONS="$STATUS_ARG $BUMP_ARG $DRY_ARG" make release-ci + env: + STATUS_ARG: ${{ fromJSON('{"alpha":"--preRelease=alpha", "beta":"--preRelease=beta", "stable":""}')[github.event.inputs.status] }} + BUMP_ARG: ${{ fromJSON('{"patch":"-i patch", "minor":"-i minor", "major":"-i major", "auto":""}')[github.event.inputs.bump] }} + DRY_ARG: ${{ github.event.inputs.dry == 'true' && '-d' || '' }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GIT_AUTHOR_NAME: ${{ steps.import-gpg.outputs.name }} + GIT_AUTHOR_EMAIL: ${{ steps.import-gpg.outputs.email }} + GIT_COMMITTER_NAME: ${{ steps.import-gpg.outputs.name }} + GIT_COMMITTER_EMAIL: ${{ steps.import-gpg.outputs.email }} diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml new file mode 100644 index 0000000..97e2934 --- /dev/null +++ b/.github/workflows/upgrade.yml @@ -0,0 +1,38 @@ +name: Upgrade + +on: + workflow_dispatch: + +jobs: + Dependencies: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup + uses: ./.github/actions/setup + + - name: Import GPG Key + id: import-gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }} + git_config_global: true + git_user_signingkey: true + git_commit_gpgsign: true + git_tag_gpgsign: true + + - name: Upgrade Dependencies + run: | + make upgrade + git push origin main + env: + GIT_AUTHOR_NAME: ${{ steps.import-gpg.outputs.name }} + GIT_AUTHOR_EMAIL: ${{ steps.import-gpg.outputs.email }} + GIT_COMMITTER_NAME: ${{ steps.import-gpg.outputs.name }} + GIT_COMMITTER_EMAIL: ${{ steps.import-gpg.outputs.email }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e139505 --- /dev/null +++ b/.gitignore @@ -0,0 +1,55 @@ +#* Editor Directories & Files +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*.local + +#* Package Managers +vendor +node_modules +package +pnpm-lock.yaml +yarn.lock +go.work.sum + +#* Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +#* ENV +.env* +.env.* +!.env.demo* +!.env.example* +.npmrc + +#* Production +dist +dist-ssr +build + +#* Tooling +vite.config.js.timestamp-* +vite.config.ts.timestamp-* +*_templ.txt +*_templ.go + +#* Misc +ignore* +report* +temp* +tmp* + +#* Repository diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..0398b7a --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit ${1} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..1524ad6 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx --no -- lint-staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..c5c13ca --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1 @@ +make leak-check diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e139505 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,55 @@ +#* Editor Directories & Files +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*.local + +#* Package Managers +vendor +node_modules +package +pnpm-lock.yaml +yarn.lock +go.work.sum + +#* Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +#* ENV +.env* +.env.* +!.env.demo* +!.env.example* +.npmrc + +#* Production +dist +dist-ssr +build + +#* Tooling +vite.config.js.timestamp-* +vite.config.ts.timestamp-* +*_templ.txt +*_templ.go + +#* Misc +ignore* +report* +temp* +tmp* + +#* Repository diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..082bc94 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["ms-vscode-remote.remote-containers"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..61defd0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,344 @@ +# Changelog + +## [4.3.0](https://github.com/bastean/codexgo/compare/v4.2.1...v4.3.0) (2024-06-26) + +### Chores + +- **deps:** upgrade dependencies ([bce72d8](https://github.com/bastean/codexgo/commit/bce72d872897ad527dd3e435fe45fe5f93300d81)) +- **deps:** upgrade jwt to v5 ([18054b7](https://github.com/bastean/codexgo/commit/18054b747b8250e7a169359dc1f54287328d0edc)) + +### Documentation + +- add scanners ([a34ea61](https://github.com/bastean/codexgo/commit/a34ea616ff1425c3053115df604e64ef9609e283)) + +### New Features + +- add trivy and osv scanners ([6fe3c53](https://github.com/bastean/codexgo/commit/6fe3c53255d3fb68dbbda985a0c15e7b9b68ae5c)) + +### Refactors + +- **context:** change domain message components ([157ef5b](https://github.com/bastean/codexgo/commit/157ef5bcd3a54c9784ddcdd6339e9223da735a38)) +- **context:** rename variables in value objects ([c8f46a1](https://github.com/bastean/codexgo/commit/c8f46a1e857c25d38a72da5a9f30273c8dc64e3d)) +- **scripts:** change panic on error ([4577790](https://github.com/bastean/codexgo/commit/4577790af035df66e18aa96ee8289c30409a1687)) +- **server:** rename service logs ([65bd07c](https://github.com/bastean/codexgo/commit/65bd07c900686326c91d5fba163c501348a8589e)) +- **server:** reorganize services ([f559715](https://github.com/bastean/codexgo/commit/f5597153bd121cce16d77aecba2cf0e268bcf1ce)) + +### Tests + +- **context:** add assertion for duplication error in mongo ([201fd53](https://github.com/bastean/codexgo/commit/201fd53f5970546dddaf62c2b21d50a35841052c)) +- **context:** add assertion for omitted json errors ([a08ea38](https://github.com/bastean/codexgo/commit/a08ea38efd3fa0d191505f48de571a234c9b2217)) + +## [4.2.1](https://github.com/bastean/codexgo/compare/v4.2.0...v4.2.1) (2024-06-19) + +### Bug Fixes + +- add boolean format verb in strings ([e317911](https://github.com/bastean/codexgo/commit/e317911d22e1798a4bc0a1c917089b06d8228a35)) +- remove default format verb in strings ([57beb5e](https://github.com/bastean/codexgo/commit/57beb5e3339a6ac9685e47bd2c798fdcccf619b2)) + +## [4.2.0](https://github.com/bastean/codexgo/compare/v4.1.1...v4.2.0) (2024-06-17) + +### Chores + +- **deps:** upgrade dependencies ([7b19ee7](https://github.com/bastean/codexgo/commit/7b19ee7dee15d14a7fad328977d090c031ef2964)) + +### New Features + +- **context:** add handling of omitted errors ([f70c276](https://github.com/bastean/codexgo/commit/f70c2767a368859560c1c2986411384ee36ae92e)) + +### Refactors + +- add default format verb to strings ([c459caa](https://github.com/bastean/codexgo/commit/c459caaf4c6fb7c5aede730caad10ab0093e3a3e)) +- change panic on error ([1fc83fa](https://github.com/bastean/codexgo/commit/1fc83fa7b6433059e098bbe8a21762b7eb95000a)) +- **context:** remove notify module ([064815f](https://github.com/bastean/codexgo/commit/064815f9efb7b75638c4f2d88efe1a27cacecc80)) +- **context:** remove redundant details from type names ([27a666a](https://github.com/bastean/codexgo/commit/27a666a11bf7232df9d9ba34c20f1d15e03abade)) + +### Tests + +- **context:** add handling of unexpected errors in mothers to avoid flaky tests ([2fbea22](https://github.com/bastean/codexgo/commit/2fbea223fada03944b1deb6a1b83f2f7df879e93)) + +## [4.1.1](https://github.com/bastean/codexgo/compare/v4.1.0...v4.1.1) (2024-06-12) + +### Bug Fixes + +- **makefile:** add pipefail to return an error when a test fails ([5b4c26e](https://github.com/bastean/codexgo/commit/5b4c26e4621fec259eb1b6cfa0bd263534b50588)) + +## [4.1.0](https://github.com/bastean/codexgo/compare/v4.0.0...v4.1.0) (2024-06-10) + +### Chores + +- **deps:** upgrade dependencies ([93fc426](https://github.com/bastean/codexgo/commit/93fc4264ee2f2937e07e9a8f9c96156df4dfb4f1)) + +### Documentation + +- **readme:** add basic layers workflow ([b6f6d5d](https://github.com/bastean/codexgo/commit/b6f6d5d0f8b6bb75759a8a0744f86bd5abc44893)) + +### New Features + +- **makefile:** add tee in test rules ([1d21d7a](https://github.com/bastean/codexgo/commit/1d21d7a31fac96db750c9266e181f93765ca1089)) + +### Bug Fixes + +- **dockerfile:** update air module name ([71bc376](https://github.com/bastean/codexgo/commit/71bc376c7a6e834d16eaf10684806ca652bd3e51)) + +### Refactors + +- add type alias ([f55bb9d](https://github.com/bastean/codexgo/commit/f55bb9d1fbe3c933c1bb48e6885270c92e69beee)) +- **context:** add pointer to search criteria type ([8648a18](https://github.com/bastean/codexgo/commit/8648a184464c355d5b6216a4cb6a2f58c4bc1b95)) +- **context:** change empty type from struct to interface ([4e5dcf0](https://github.com/bastean/codexgo/commit/4e5dcf0152a5eec35818edf90030ae449037e0a2)) +- **context:** change errors in shared module ([47fe621](https://github.com/bastean/codexgo/commit/47fe62172d93792694beef6df3145502690e8d6a)) +- **context:** change parameters to use primitive type in user module ([db8fc5b](https://github.com/bastean/codexgo/commit/db8fc5b12b24968677f2a452e703a5af3192020f)) +- **context:** change updates in user module ([ba294af](https://github.com/bastean/codexgo/commit/ba294aff99ac5a6d35b2701531d97bf16f6191fd)) +- squash struct fields ([8ccc22c](https://github.com/bastean/codexgo/commit/8ccc22c0ed57d02e1717d723462673ee1ebc55d3)) + +### Tests + +- **context:** add more explicit test case names ([5027a31](https://github.com/bastean/codexgo/commit/5027a31a0fafcdbcac17ad09a04b557e145c5a55)) + +## [4.0.0](https://github.com/bastean/codexgo/compare/v3.0.1...v4.0.0) (2024-05-28) + +### ⚠ BREAKING CHANGES + +- **server:** decouple service initializations +- **context:** change notification system workflow +- **server:** change acceptance tests to work with the new ui +- **server:** add fomantic-ui +- **server:** change error handling from panic to wrapped errors +- **context:** change package names in shared module +- **context:** change integration tests to check for wrapped errors instead of panic +- **context:** change unit tests to check for wrapped errors instead of panic +- **context:** change error handling from panic to wrapped errors + +### Chores + +- change air config ([a3b2f94](https://github.com/bastean/codexgo/commit/a3b2f94cb9f1f5b69ab719ae09001bac8382e7b2)) +- change git ignore list ([122f7b2](https://github.com/bastean/codexgo/commit/122f7b2ba448cf79e741e5c7c3e3b7283a2dcaaf)) +- change go version in mod file ([95ac107](https://github.com/bastean/codexgo/commit/95ac107f7b6dee5618ec459c911ca07440521c04)) +- change makefile rules ([285b30b](https://github.com/bastean/codexgo/commit/285b30b7e1c88503681279e097d4a6e0abf8154c)) +- **deps:** upgrade dependencies ([f804fc8](https://github.com/bastean/codexgo/commit/f804fc8c0e22f660407e80c826811455d9d13303)) + +### Documentation + +- **readme:** add updated screenshots ([ffd6b17](https://github.com/bastean/codexgo/commit/ffd6b175b19292111ba5bff3611322c7ee8cdbb6)) +- **readme:** change description ([122b14c](https://github.com/bastean/codexgo/commit/122b14c69174c5de0f9cc0cac54e4b55ea3a25d2)) + +### New Features + +- **air:** enable live-reloading on the browser ([7714c38](https://github.com/bastean/codexgo/commit/7714c38cf0a74ef07dff07d232b98f462070a71a)) +- **context:** add json marshal error handler to error bubble ([68819fe](https://github.com/bastean/codexgo/commit/68819fe9521117adb43504105b90f91abecebce4)) +- **context:** add new terminal transport port adapter to notify module ([28fd1fe](https://github.com/bastean/codexgo/commit/28fd1fe865666b65f25d93e1f7b1895f2b40a998)) +- **scripts:** add copy-deps script ([79e2d73](https://github.com/bastean/codexgo/commit/79e2d73989674b9a2d62841db87da89ef4bd8564)) +- **server:** add accepts cookies nag ([682c370](https://github.com/bastean/codexgo/commit/682c370366139911fa145defa9127310503d86e9)) +- **server:** add cookies cleaning ([30d4b9a](https://github.com/bastean/codexgo/commit/30d4b9aca620098e1ed4b9c0c0501003570f8007)) +- **server:** add fomantic-ui ([738bf51](https://github.com/bastean/codexgo/commit/738bf5140c0dcb310c4effcbd3659720ba2c20a5)) +- **server:** add log files ([141001a](https://github.com/bastean/codexgo/commit/141001a2d0bf0269a25030597c45d3a2b7c2f891)) +- **server:** add missing error handlers ([99938f6](https://github.com/bastean/codexgo/commit/99938f6a5c00a47a5d4471d07354fe565a6c50f5)) +- **server:** add popup to inform about account status ([77eb4a9](https://github.com/bastean/codexgo/commit/77eb4a917e1487a3736da346064befa0ff3d35c8)) + +### Bug Fixes + +- add missing pointers ([b6b9343](https://github.com/bastean/codexgo/commit/b6b934305caecf51eea9c058e84d75cfaa2d353f)) +- **deps:** upgrade dependencies ([4574f31](https://github.com/bastean/codexgo/commit/4574f3147dff657eb1672a97d0acea236bf8d5c4)) +- **server:** add json unmarshal type error handler ([eb9eeb5](https://github.com/bastean/codexgo/commit/eb9eeb5e4442caf2abd07c294cd68a0c9db8f9b9)) + +### Refactors + +- add field names at struct initialization ([55c5de3](https://github.com/bastean/codexgo/commit/55c5de3902be83a9066b9867e9493ad4ebab6f87)) +- **context:** change error handling from panic to wrapped errors ([ec3245c](https://github.com/bastean/codexgo/commit/ec3245c9caf81562cfb8c2ae61aa16ee34b4d5e6)) +- **context:** change exchange to router in broker model ([be19870](https://github.com/bastean/codexgo/commit/be198705206d0f0370b73dc5ed8bb9cf1c3d3572)) +- **context:** change notification system workflow ([f7ec73c](https://github.com/bastean/codexgo/commit/f7ec73cd038337f63b792f1f2623285b7e0eb854)) +- **context:** change package names in shared module ([e26da1e](https://github.com/bastean/codexgo/commit/e26da1e123692559f9a84a2ad3f0e53c4a1b1743)) +- **context:** change time format in errors ([23362e4](https://github.com/bastean/codexgo/commit/23362e40d7ea9fafe2b83a120f3bbc6652644057)) +- **context:** change type name of shared errors ([61c9b93](https://github.com/bastean/codexgo/commit/61c9b93fcec544e6d95c4e3ecd76a0f49ba38e43)) +- **context:** rename folders using plural names instead of the prefix s in shared module ([fe5aabf](https://github.com/bastean/codexgo/commit/fe5aabf7b3815b60a48382076f7f453e065f15f2)) +- **context:** rename packages using plural names in shared module ([db66e1d](https://github.com/bastean/codexgo/commit/db66e1d7e9c62ed8600f048512c37ec30bcba4ef)) +- **makefile:** add MAKE variable to rules with a recursive recipe ([75e31a8](https://github.com/bastean/codexgo/commit/75e31a84e9b58b1723270bce738162240628e21d)) +- **makefile:** change target names of test rules ([f078581](https://github.com/bastean/codexgo/commit/f0785811deabe3ab8343185cb14ca97933c6341c)) +- rename files using flatcase ([28d3e5f](https://github.com/bastean/codexgo/commit/28d3e5fafad4cdbeed23a8c6bb51724e6e6f746e)) +- **scripts:** change commit message on upgrade script ([9b257a2](https://github.com/bastean/codexgo/commit/9b257a223f1c2cc22dfe9c7dc5b1764389f3f8f1)) +- **server:** add ui class in jquery component selectors ([3c1743e](https://github.com/bastean/codexgo/commit/3c1743ed59d0e35fe5b4d2e2e7d9a4e62066a4a5)) +- **server:** change broker service components to individual files ([93d3c29](https://github.com/bastean/codexgo/commit/93d3c29e4669be36e5ed2f196fdb4e1183314a72)) +- **server:** change error handling from panic to wrapped errors ([1e3d766](https://github.com/bastean/codexgo/commit/1e3d766be194b64960e302b83318a9929f104e5c)) +- **server:** change error messages in services ([0f6a21e](https://github.com/bastean/codexgo/commit/0f6a21e98a89347d721f32b611fdacd6b405430d)) +- **server:** decouple service initializations ([61961d2](https://github.com/bastean/codexgo/commit/61961d2327948df5b9611ff75034c28d9f34a859)) + +### Tests + +- **context:** add spaces between definitions in setup test ([4318ea2](https://github.com/bastean/codexgo/commit/4318ea27f6cac20154489c55917b9d639bd54fe0)) +- **context:** change integration tests to check for wrapped errors instead of panic ([6bb93ac](https://github.com/bastean/codexgo/commit/6bb93ac315e0049a2407952406e8349886274b2a)) +- **context:** change time on expected error messages ([56137d6](https://github.com/bastean/codexgo/commit/56137d695a6663e18272db70c1d98dcf97c03140)) +- **context:** change unit tests to check for wrapped errors instead of panic ([971b9de](https://github.com/bastean/codexgo/commit/971b9de188f998f9b6ecdec5435375be46789c9f)) +- **server:** change acceptance tests to work with the new ui ([8df4c59](https://github.com/bastean/codexgo/commit/8df4c59c9c280d7c7a55285fa662b682b2df9469)) + +## [3.0.1](https://github.com/bastean/codexgo/compare/v3.0.0...v3.0.1) (2024-04-08) + +### Bug Fixes + +- **deps:** upgrade dependencies ([bd92cf7](https://github.com/bastean/codexgo/commit/bd92cf74fced77cb9011171e60f15d687ddc94f7)) +- **makefile:** add phony target ([3c33a90](https://github.com/bastean/codexgo/commit/3c33a9005396067e0a2c130d78648a41bc677f73)) +- **makefile:** remove init-ci rule ([3311e14](https://github.com/bastean/codexgo/commit/3311e144798aaff6dfe34df1c2c8aa9751f3ca68)) + +### Refactors + +- **makefile:** change rules order ([95c6170](https://github.com/bastean/codexgo/commit/95c6170e4719d0702efe2ed75197c42cd3103494)) + +## [3.0.0](https://github.com/bastean/codexgo/compare/v2.0.1...v3.0.0) (2024-04-04) + +### Documentation + +- **readme:** add features ([6d36f5d](https://github.com/bastean/codexgo/commit/6d36f5d75dfc6e1e3cf9cb50ca01d6f7ab1a4b7a)) + +### New Features + +- add account confirmation via email ([66f7b6e](https://github.com/bastean/codexgo/commit/66f7b6eda53e2f3ea897603c032e85f51fe6cf83)) +- add event-driven architecture using rabbitmq ([1fd11cb](https://github.com/bastean/codexgo/commit/1fd11cb1b1b9096dc2aafccd2da8982d6d041279)) +- add example env demo file ([c288d3c](https://github.com/bastean/codexgo/commit/c288d3ccdd7e4d348b915f22c9fa0236df7de247)) +- add gracefully close infrastructure connections ([fb91c9a](https://github.com/bastean/codexgo/commit/fb91c9a569d7f2d6be67e983e19bb53ec5cb5191)) + +### Bug Fixes + +- **deps:** upgrade dependencies ([a27389f](https://github.com/bastean/codexgo/commit/a27389f51cfc9b37d6b11dcc1013c2e02be84ea4)) +- remove files generated by templ ([0adc9c6](https://github.com/bastean/codexgo/commit/0adc9c6cd570b672f8f9719ee0f3691b895804fd)) + +### Refactors + +- change env handling in context to app ([f395933](https://github.com/bastean/codexgo/commit/f395933288ba2ad6fbb95eb683faa7195ebad890)) +- change templ components ([e54c18a](https://github.com/bastean/codexgo/commit/e54c18a8421a1f5a62bb7ef0e2e58f90f2b50f4b)) + +### Tests + +- add individual execution of unit, integration and acceptance tests ([4dc646f](https://github.com/bastean/codexgo/commit/4dc646f52794c0ad80803ce95900c0bf402029fd)) +- remove shared value objects ([618ab5c](https://github.com/bastean/codexgo/commit/618ab5c990f2c54ec574380f54778fb64757ea2a)) + +## [2.0.1](https://github.com/bastean/codexgo/compare/v2.0.0...v2.0.1) (2024-03-13) + +### Bug Fixes + +- **deps:** upgrade dependencies ([4e3f621](https://github.com/bastean/codexgo/commit/4e3f621bf8b3833ef2cd4d7bbe877cf5d38a81ac)) + +### Refactors + +- change domain models ([f80911a](https://github.com/bastean/codexgo/commit/f80911acf48a9bfb115d3328a7834babfa123b02)) + +## [2.0.0](https://github.com/bastean/codexgo/compare/v1.5.0...v2.0.0) (2024-03-02) + +### ⚠ BREAKING CHANGES + +- add standard project layout + +### Bug Fixes + +- **deps:** upgrade dependencies ([f11b15f](https://github.com/bastean/codexgo/commit/f11b15f77899ab50ae0ac744dd84346cb71a7760)) + +### Refactors + +- add standard project layout ([307089c](https://github.com/bastean/codexgo/commit/307089c56975716fb6788e6fafd06ffa8b42f620)) + +## [1.5.0](https://github.com/bastean/codexgo/compare/v1.4.0...v1.5.0) (2024-02-18) + +### New Features + +- add script to sync .env\* files ([e7fcc0b](https://github.com/bastean/codexgo/commit/e7fcc0b6355e5abf00a97526e7becb111cdf2dda)) + +### Bug Fixes + +- **deps:** upgrade dependencies ([fecaafa](https://github.com/bastean/codexgo/commit/fecaafa9bf35e6a5fa71ae0468845bd32bef26ea)) + +## [1.4.0](https://github.com/bastean/codexgo/compare/v1.3.1...v1.4.0) (2024-02-15) + +### New Features + +- add commit message types to include in the changelog ([db06cf9](https://github.com/bastean/codexgo/commit/db06cf95d6d637f097a6745d04302b8f272a50a6)) + +### Bug Fixes + +- **deps:** upgrade dependencies ([80c2256](https://github.com/bastean/codexgo/commit/80c22563516b5da15ea07475fbc94c4fcbffd5c6)) + +## [1.3.1](https://github.com/bastean/codexgo/compare/v1.3.0...v1.3.1) (2024-02-14) + +### Bug Fixes + +- **actions:** upgrade go setup action ([da7bc21](https://github.com/bastean/codexgo/commit/da7bc213a052d088efaac6b20c5ec5ad92f4d037)) +- change live reload ([2f97bdb](https://github.com/bastean/codexgo/commit/2f97bdbe0675a747ac4eddcfe99632dcf0803b0f)) + +## [1.3.0](https://github.com/bastean/codexgo/compare/v1.2.0...v1.3.0) (2024-02-06) + +### Features + +- **actions:** add upgrade workflow ([e2d62d4](https://github.com/bastean/codexgo/commit/e2d62d4d76e56e0dfaabe0cbd474ef23ee1e5687)) +- add script to upgrade dependencies ([a7cd088](https://github.com/bastean/codexgo/commit/a7cd088099d336526e00c6187a835f9938e48a55)) +- **backend:** add secure middleware ([370db08](https://github.com/bastean/codexgo/commit/370db087b6df9ce1aaa3fa2e5589abc9756ec9b2)) + +### Bug Fixes + +- **actions:** add commit push to upgrade workflow ([9ea06db](https://github.com/bastean/codexgo/commit/9ea06dbfdb1f2af22f187734d757fe2ad8b0e88a)) +- **deps:** upgrade dependencies ([811345c](https://github.com/bastean/codexgo/commit/811345c603d2b07ed76f83620fc6386ea90d1861)) +- **deps:** upgrade dependencies ([c99be30](https://github.com/bastean/codexgo/commit/c99be30ae77f766ca09dece90a627f657f8458c3)) + +## [1.2.0](https://github.com/bastean/codexgo/compare/v1.1.0...v1.2.0) (2024-01-28) + +### Features + +- **backend:** add rate limiter middleware ([a6c1b2b](https://github.com/bastean/codexgo/commit/a6c1b2b2a484d0b4ac63364b76c1ba18f8c3e4b3)) + +### Bug Fixes + +- **deps:** upgrade modules dependencies ([d9851aa](https://github.com/bastean/codexgo/commit/d9851aaeb9ff510148935043ab446bea52e3dc26)) +- remove go vet from lint-staged ([3869cbf](https://github.com/bastean/codexgo/commit/3869cbf84fb83bc105b16be0fb6f1a03ab830e9f)) + +## [1.1.0](https://github.com/bastean/codexgo/compare/v1.0.0...v1.1.0) (2024-01-22) + +### Features + +- **actions:** add brew setup ([ef7a00d](https://github.com/bastean/codexgo/commit/ef7a00de57e7cf524223f6e4ced5f7bf2ad71e55)) +- add go vet on lint-staged ([8c52de4](https://github.com/bastean/codexgo/commit/8c52de4ace6d34c2174fe8f03c35e84b6a4040a5)) +- add upx to compress binaries ([9d4e926](https://github.com/bastean/codexgo/commit/9d4e926a3b764f6fe2e49009fb69adc127acb7ea)) +- **devcontainer:** add brew to simplify installation of tools ([8c77ed4](https://github.com/bastean/codexgo/commit/8c77ed45692b6303fcb6235b4ddb612d4e175505)) +- **makefile:** add go mod tidy on lint rule ([d203639](https://github.com/bastean/codexgo/commit/d203639765560e1e375b77b9759bed581c2176ab)) + +### Bug Fixes + +- **docker:** add optimization to compose ([0730183](https://github.com/bastean/codexgo/commit/0730183bfbc522f0c5278a733e63b346fbe41044)) + +## [1.0.0](https://github.com/bastean/codexgo/compare/v0.1.1...v1.0.0) (2024-01-17) + +### ⚠ BREAKING CHANGES + +- **readme:** Ready for v1 + +### Features + +- add codexgo logos ([7ff0641](https://github.com/bastean/codexgo/commit/7ff0641a5db2df5f180242e3d05d93c1ba0cfc92)) +- add trufflehog scan on lint-staged ([bdc473c](https://github.com/bastean/codexgo/commit/bdc473c56a446e0268c1ed02222af2a37185c244)) +- **ci:** add tests job to workflow ([c033f54](https://github.com/bastean/codexgo/commit/c033f5429ddbe07f55281a921b706e6820537ffa)) +- **devcontainer:** add cucumber extension ([fabb6d8](https://github.com/bastean/codexgo/commit/fabb6d8e990b87a71f0152cdeb43b6c28f3cd878)) +- **docker:** add production compose ([4963296](https://github.com/bastean/codexgo/commit/49632964e8a238fd676204cd4eb0bff03a959ac7)) + +### Bug Fixes + +- **backend:** add responsive to alerts ([fc1e4c8](https://github.com/bastean/codexgo/commit/fc1e4c80ba071edc7bfaf39a43bae4aaad8f5b1d)) + +### Documentation + +- **readme:** add contributing section ([54e95f6](https://github.com/bastean/codexgo/commit/54e95f65a5deddd73e1021ad520c848a75ca29cc)) + +## [0.1.1](https://github.com/bastean/codexgo/compare/v0.1.0...v0.1.1) (2024-01-07) + +## 0.1.0 (2024-01-07) + +### Features + +- **backend:** add basis to use htmx with tailwindcss ([5f260b5](https://github.com/bastean/codexgo/commit/5f260b5a594c0eaa50324d04f715f614145f7adc)) +- **backend:** add crud endpoints ([08957ba](https://github.com/bastean/codexgo/commit/08957ba38446d9c3d52e75d225f5b77c1541c7f3)) +- **backend:** add development dockerfile ([a8cef51](https://github.com/bastean/codexgo/commit/a8cef51c8f158fac22c71a567441b3efb49abfc8)) +- **backend:** add pwa ([a370906](https://github.com/bastean/codexgo/commit/a3709064ba027b9c2e60cd157de55c95e41802a3)) +- **context|backend:** add authentication to protected endpoints ([b582c11](https://github.com/bastean/codexgo/commit/b582c112f11e9a206fd037a2ecd7ea2bafa252ce)) +- **context|backend:** add password hashing ([8264127](https://github.com/bastean/codexgo/commit/82641276144048800e1a99ac0806f3a1402f98a7)) +- **context:** add basis to run use cases ([fa28f1f](https://github.com/bastean/codexgo/commit/fa28f1f87471e6c84dccdcebd35fc198bb46b96d)) +- **context:** add crud use cases ([d503539](https://github.com/bastean/codexgo/commit/d5035397c7485e2826e4ed4cf594db2f32ef7145)) +- **context:** add mongo repository adapter ([f25a793](https://github.com/bastean/codexgo/commit/f25a7931fab225edebc6e03a4f6f0a124a8ab05d)) +- **devcontainer:** add prettier extension ([d833c1b](https://github.com/bastean/codexgo/commit/d833c1b62defdc6407534662358c89396948e7dc)) + +### Bug Fixes + +- **ci:** upgrade actions ([3054b85](https://github.com/bastean/codexgo/commit/3054b85e405293668d0e2647b584e7cb0f815710)) +- **release:** change manifest path ([05918f0](https://github.com/bastean/codexgo/commit/05918f0961fed086665c3e8572efcee5cdf9a025)) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3c8a03b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Bastean + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d09e669 --- /dev/null +++ b/Makefile @@ -0,0 +1,265 @@ +.PHONY: * + +#*------------VARS------------ + +#*______URL______ + +github = https://github.com/bastean/codexgo + +#*______Go______ + +go-tidy = go mod tidy -e + +#*______Node______ + +npx = npx --no -- +npm-ci = npm ci --legacy-peer-deps + +release-it = ${npx} release-it -V +release-it-dry = ${npx} release-it -V -d --no-git.requireCleanWorkingDir + +#*______Bash______ + +bash = bash -o pipefail -c + +#*______Git______ + +git-reset-hard = git reset --hard HEAD + +#*______Docker______ + +compose = cd deployments/ && docker compose +compose-env = ${compose} --env-file + +#*------------RULES------------ + +#*______Upgrades______ + +upgrade-managers: + #? sudo apt update && sudo apt upgrade -y + npm upgrade -g + +upgrade-go: + go get -t -u ./... + +upgrade-node: + ${npx} ncu --root -ws -u + rm -f package-lock.json + npm i --legacy-peer-deps + +upgrade-reset: + ${git-reset-hard} + ${npm-ci} + +upgrade: + go run ./scripts/upgrade + +#*______Dependencies______ + +install-tools: + curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sudo sh -s -- -b /usr/local/bin v3.63.11 + curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin v0.52.2 + go install github.com/google/osv-scanner/cmd/osv-scanner@latest + go install honnef.co/go/tools/cmd/staticcheck@latest + go install github.com/a-h/templ/cmd/templ@latest + +install-deps: + go mod download + ${npm-ci} + +copy-deps: + go run ./scripts/copydeps + +#*______Generators______ + +generate-required: + go generate ./... + templ generate + +#*______Initializations______ + +init: upgrade-managers install-tools install-deps copy-deps generate-required + +init-zero: + git init + $(MAKE) init + ${npx} husky install + +#*______Linters/Formatters______ + +lint: generate-required + go mod tidy + gofmt -l -s -w . + ${npx} prettier --ignore-unknown --write . + templ fmt . + +lint-check: + staticcheck ./... + ${npx} prettier --check . + +#*______Scanners______ + +leak-check: + sudo trufflehog git file://. --only-verified + trivy repo --scanners secret . + +leak-remote-check: + sudo trufflehog git ${github} --only-verified + trivy repo --scanners secret ${github} + +vuln-check: + osv-scanner --call-analysis=all -r . + trivy repo --scanners vuln . + +misconfig-check: + trivy repo --scanners misconfig . + +scan-leaks: leak-check leak-remote-check + +scan-vulns: vuln-check + +scan-misconfigs: misconfig-check + +scans: scan-leaks scan-vulns scan-misconfigs + +#*______Tests______ + +test-sut: + air + +test-clean: generate-required + go clean -testcache + cd test/ && mkdir -p report + +test-codegen: + ${npx} playwright codegen http://localhost:8080 + +test-sync: upgrade-go + ${npx} concurrently -s first -k --names 'SUT,TEST' '$(MAKE) test-sut' '${npx} wait-on -l http-get://localhost:8080 && $(TEST_SYNC)' + +test-unit: test-clean + ${bash} 'go test -v -cover ./pkg/context/... -run TestUnit.* |& tee test/report/unit.report.log' + +test-integration: test-clean + ${bash} 'go test -v -cover ./pkg/context/... -run TestIntegration.* |& tee test/report/integration.report.log' + +test-acceptance-sync: + ${bash} 'TEST_URL="http://localhost:8080" go test -v -cover ./pkg/cmd/... -run TestAcceptance.* |& tee test/report/acceptance.report.log' + +test-acceptance: test-clean + TEST_SYNC="$(MAKE) test-acceptance-sync" $(MAKE) test-sync + +tests-sync: + ${bash} 'TEST_URL="http://localhost:8080" go test -v -cover ./... |& tee test/report/report.log' + +tests: test-clean + TEST_SYNC="$(MAKE) tests-sync" $(MAKE) test-sync + +#*______Releases______ + +release: + ${release-it} + +release-alpha: + ${release-it} --preRelease=alpha + +release-beta: + ${release-it} --preRelease=beta + +release-ci: + ${release-it} --ci $(OPTIONS) + +release-dry: + ${release-it-dry} + +release-dry-version: + ${release-it-dry} --release-version + +release-dry-changelog: + ${release-it-dry} --changelog + +#*______Builds______ + +build: generate-required lint + rm -rf build/ + go build -ldflags="-s -w" -o build/codexgo ./cmd/codexgo + +#*______ENVs______ + +sync-env-reset: + ${git-reset-hard} + +sync-env: + cd deployments && go run ../scripts/syncenv + +#*______Git______ + +commit: + ${npx} cz + +WARNING-git-forget: + git rm -r --cached . + git add . + +WARNING-git-genesis: + git clean -e .env*-fdx + ${git-reset-hard} + $(MAKE) init + +#*______Docker______ + +docker-usage: + docker system df + +docker-it: + docker exec -it $(ID) bash + +compose-dev-down: + ${compose-env} .env.dev down + docker volume rm codexgo-database-dev -f + +compose-dev: compose-dev-down + ${compose-env} .env.dev up + +compose-test-down: + ${compose-env} .env.test down + docker volume rm codexgo-database-test -f + +compose-test-integration: compose-test-down + ${compose-env} .env.test --env-file .env.demo.test.integration up --exit-code-from server + +compose-test-acceptance: compose-test-down + ${compose-env} .env.test --env-file .env.demo.test.acceptance up --exit-code-from server + +compose-tests: compose-test-down + ${compose-env} .env.test up --exit-code-from server + +compose-prod-down: + ${compose-env} .env.prod down + +compose-prod: compose-prod-down + ${compose-env} .env.prod up + +demo-down: + ${compose-env} .env.demo down + +demo: demo-down + ${compose-env} .env.demo up + +compose-down: compose-dev-down compose-test-down compose-prod-down demo-down + +WARNING-docker-prune-soft: + docker system prune + $(MAKE) compose-down + $(MAKE) docker-usage + +WARNING-docker-prune-hard: + docker system prune --volumes -a + $(MAKE) compose-down + $(MAKE) docker-usage + +#*______Fixes______ + +fix-local-playwright: + go get -u github.com/playwright-community/playwright-go + go run github.com/playwright-community/playwright-go/cmd/playwright@latest install chromium --with-deps diff --git a/README.md b/README.md new file mode 100644 index 0000000..75f5577 --- /dev/null +++ b/README.md @@ -0,0 +1,404 @@ +

+ + + +[![logo readme](assets/readme/logo.png)](https://github.com/bastean/codexgo) + +

+ +
+ +> Example CRUD project applying Hexagonal Architecture, Domain-Driven Design (DDD), Event-Driven Architecture (EDA), Command Query Responsibility Segregation (CQRS), Behavior-Driven Development (BDD), Continuous Integration (CI), and more... in Go. + +
+ +
+ +
+ +[![license MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![go report card](https://goreportcard.com/badge/github.com/bastean/codexgo)](https://goreportcard.com/report/github.com/bastean/codexgo) +[![commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](https://github.com/commitizen/cz-cli) +[![release it](https://img.shields.io/badge/%F0%9F%93%A6%F0%9F%9A%80-release--it-orange.svg)](https://github.com/release-it/release-it) + +
+ +
+ +[![upgrade workflow](https://github.com/bastean/codexgo/actions/workflows/upgrade.yml/badge.svg)](https://github.com/bastean/codexgo/actions/workflows/upgrade.yml) +[![ci workflow](https://github.com/bastean/codexgo/actions/workflows/ci.yml/badge.svg)](https://github.com/bastean/codexgo/actions/workflows/ci.yml) +[![release workflow](https://github.com/bastean/codexgo/actions/workflows/release.yml/badge.svg)](https://github.com/bastean/codexgo/actions/workflows/release.yml) + +
+ +
+ +[![go reference](https://pkg.go.dev/badge/github.com/bastean/codexgo.svg)](https://pkg.go.dev/github.com/bastean/codexgo) +[![github release](https://img.shields.io/github/v/release/bastean/codexgo.svg)](https://github.com/bastean/codexgo/releases) + +
+ +## Showcase + +
+ + + + + + + + + + + +
+ +## Usage (Demo) + +> [!NOTE] +> +> - [System Requirements](#locally) +> - In the Demo version, the link to confirm the account is sent through the Terminal. +> - _"Hi \, please confirm your account through this link: \"_ +> - You can define your own **SMTP** configuration in the [.env.demo](deployments/.env.demo) file by simply modifying the **SERVER_SMTP\_\*** variables, then you will receive the links by mail. + +```bash +make demo +``` + +## Features + +### Project Layout + +- Based on [Standard Go Project Layout](https://github.com/golang-standards/project-layout). + +### Git + +- Hooks managed by [husky](https://github.com/typicode/husky): + - Pre-Push: + - Scanning Repository for leaks using [TruffleHog CLI](https://github.com/trufflesecurity/trufflehog) and [Trivy](https://github.com/aquasecurity/trivy) + - Pre-Commit: [lint-staged](https://github.com/lint-staged/lint-staged) + - Scanning files for leaks using [TruffleHog CLI](https://github.com/trufflesecurity/trufflehog?tab=readme-ov-file#8-scan-individual-files-or-directories) + - Formatting + - Commit-Msg: [commitlint](https://github.com/conventional-changelog/commitlint) + - Check [Conventional Commits](https://www.conventionalcommits.org) rules +- Commit message helper using [Commitizen](https://github.com/commitizen/cz-cli). + - Interactive prompt that allows you to write commits following the [Conventional Commits](https://www.conventionalcommits.org) rules: + ```bash + make commit + ``` + +### Linting/Formatting Tools + +- Go: **staticcheck** and **gofmt**. +- templ: **templ fmt**. +- Gherkin: **Cucumber extension**. +- Others: **Prettier cli/extension**. + +### Scanners + +- [TruffleHog CLI](https://github.com/trufflesecurity/trufflehog): Secrets. +- [Trivy](https://github.com/aquasecurity/trivy): Secrets, Vulnerabilities and Misconfigurations. +- [OSV-Scanner](https://github.com/google/osv-scanner): Vulnerabilities. + +### Testing Packages + +- Random data generator: [Gofakeit](https://github.com/brianvoe/gofakeit). +- Unit/Integration: [Testify](https://github.com/stretchr/testify). +- Acceptance: [Testify](https://github.com/stretchr/testify), [Godog (Cucumber)](https://github.com/cucumber/godog) and [Playwright](https://github.com/playwright-community/playwright-go). + +### Releases + +- Automatically managed by [Release It!](https://github.com/release-it/release-it): + - Before/After Hooks for: + - Linting + - Testing + - Bump version based on [Conventional Commits](https://www.conventionalcommits.org) and [SemVer](https://semver.org/): + - CHANGELOG generator + - Commits and Tags generator + - GitHub Releases + +### GitHub + +- Actions for: + - Setup Languages and Dependencies +- Workflows running: + - Automatically (Triggered by **Push** or **Pull requests**): + - Secrets Scanning ([TruffleHog Action](https://github.com/trufflesecurity/trufflehog?tab=readme-ov-file#octocat-trufflehog-github-action)) + - Linting + - Testing + - Manually (Using the **Actions tab** on GitHub): + - Upgrade Dependencies + - Automate Release +- Issue Templates **(Defaults)**. + +### Devcontainer + +- Multiple Features already pre-configured: + - Go + - Node + - Docker in Docker +- Extensions and their respective settings to work with: + - Go + - templ + - Cucumber + - Gherkin + - Prettier + - Better Comments + - Todo Tree + - cSpell + +### Docker + +- Dockerfile + - **Multi-stage builds**: + - Development + - Testing + - Build + - Production +- Compose + - Switched by ENVs. + +### Message Broker + +- Routing Key based on [AsyncAPI Topic Definition](https://github.com/fmvilas/topic-definition). + +### Security + +- Form validation at the client using [Fomantic - Form Validation](https://fomantic-ui.com/behaviors/form.html). + - On the server, the validations are performed using the **Value Objects** defined in the **Context**. +- Data **authentication** via **JWT** managed by **Session Cookies**. +- Account confirmation via **Mail** or **Terminal**. +- Password hashing using [Bcrypt](https://pkg.go.dev/golang.org/x/crypto/bcrypt). +- Requests **Rate Limiting**. +- Server log files. + +### Scripts + +- [syncenv](scripts/syncenv/syncenv.go) + - Synchronize all **.env\*** files in the directory using an **.env** model. +- [copydeps](scripts/copydeps/copydeps.go) + - Copies the files required by the browser dependencies from the **node_modules** folder and places them inside the **static** folder on the server. +- [upgrade](scripts/upgrade/upgrade.go) + - Perform the following steps to upgrade the project: + 1. Upgrade Go and Node dependencies. + 2. Linting and Testing. + 3. Commit changes. +- [run](deployments/run.sh) + - Display the logs and redirect them to a file whose name depends on the time at which the service was run. + - Used in Production Image. + +## Basic Workflow (Domain > (Infrastructure | Application) > Presentation) + +### Context (Domain, Infrastructure & Application) > (Modules) + +- Domain + - Value Objects + - Mother Creators + - Unit Tests + - Messages (Event/Command) + - Mother Creators + - Aggregates + - Aggregate Root + - Mother Creators + - Models (Ports) + - Repository + - Broker +- Infrastructure + - Persistence + - Repository Mocks + - Adapters + - Integration Tests + - Communication + - Broker Mocks + - Adapters + - Integration Tests +- Application + - Commands + - Mother Creators + - Querys/Responses + - Mother Creators + - Handlers/Consumers + - Inputs & Outputs + - Uses Cases + - Unit Tests + +### App (Presentation) > (Server) + +- Presentation + - Services (Modules) + - Templates + - Handlers + - Routes + - Features (Gherkin) + - Acceptance Tests + +## First Steps + +### Clone + +#### HTTPS + +```bash +git clone https://github.com/bastean/codexgo.git && cd codexgo +``` + +#### SSH + +```bash +git clone git@github.com:bastean/codexgo.git && cd codexgo +``` + +### Initialize + +#### Dev Container (recommended) + +1. System Requirements + + - [Docker](https://docs.docker.com/get-docker) + + - [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + +2. Start VS Code + + ```bash + code . + ``` + +3. Open Command Palette + + - Ctrl+Shift+P + +4. Run + + ```txt + Dev Containers: Reopen in Container + ``` + +#### Locally + +1. System Requirements + + - [Go](https://go.dev/doc/install) + - [Node](https://nodejs.org/en/download) + - [Make](https://www.gnu.org/software/make) + - [Docker](https://docs.docker.com/get-docker) + +2. Run + + ```bash + make init + ``` + +#### ZIP + +1. [System Requirements](#locally) + +2. Run + + ```bash + make init-zero + ``` + +### GitHub Repository + +#### Settings + +##### Actions + +- General + + - Workflow permissions + + - [x] Read and write permissions + +##### Secrets and variables + +- Actions + + - New repository secret + + - BOT_GPG_PRIVATE_KEY + + ```bash + gpg --armor --export-secret-key [Pub_Key_ID (*-BOT)] + ``` + + - BOT_GPG_PASSPHRASE + +### Run + +#### ENVs + +> [!IMPORTANT] +> Before running it, you must set the following environment variables and rename the file to **.env.(dev|test|prod)**. +> +> - [.env.example](deployments/.env.example) + +> [!TIP] +> You can check the demo file to see which values you can use. +> +> - [.env.example.demo](deployments/.env.example.demo) + +#### Development + +```bash +make compose-dev +``` + +#### Tests + +##### Unit + +```bash +make test-unit +``` + +##### Integration + +```bash +make compose-test-integration +``` + +##### Acceptance + +```bash +make compose-test-acceptance +``` + +##### Unit/Integration/Acceptance + +```bash +make compose-tests +``` + +#### Production + +```bash +make compose-prod +``` + +## Tech Stack + +#### Base + +- [Go](https://go.dev) +- [templ](https://templ.guide) + - [Fomantic-UI](https://fomantic-ui.com) +- [RabbitMQ](https://www.rabbitmq.com/tutorials/tutorial-one-go) +- [MongoDB](https://www.mongodb.com/docs/drivers/go) + +#### Please see + +- [go.mod](go.mod) +- [package.json](package.json) + +## Contributing + +- Contributions and Feedback are always welcome! + - [Open a new issue](https://github.com/bastean/codexgo/issues/new/choose) + +## License + +- [MIT](LICENSE) diff --git a/assets/readme/desktop-dashboard.png b/assets/readme/desktop-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..d95966c5c5d7dbb0b54ebd7fd9d45124875ce015 GIT binary patch literal 46982 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

V=bi!8JjMzKxG-(f{#{|HA(NGY7Q6= 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 literal 0 HcmV?d00001 diff --git a/assets/readme/desktop-home.png b/assets/readme/desktop-home.png new file mode 100644 index 0000000000000000000000000000000000000000..4862636cf19a57247c980c2a6f2effa1d4b97b48 GIT binary patch literal 41440 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 literal 0 HcmV?d00001 diff --git a/assets/readme/logo.png b/assets/readme/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..52ba8b9d21f0e60ba34dec197e2a6451c01307a6 GIT binary patch literal 4820 zcmXw-cQl+`*TzS034%l!C3;CDYV;c2FnaGXN|a#qPV@*8g6J}O66MiCM0tc!2O|li z*HNQ~88i4M@3X$&I`>}JK4)M1thLX7cdC)0_HAl5Y5)LmTUSTJ1OOn~B4~3;5`vN} z-f-hUam+gi(E-x?72MwnMA- zgUeVP?tk`GLL?Y3aX3N;dx9YJ8TlKrgmh%herUyZXyw0}|120R)_!=+Zg_=o^q-4x z3!&2P|5JjI(7|qG)nRyrP)11qvlE*CJsMv9PY_}hz0nL|9F~D5hNb{|dU`Id`}glZ z7W*G6DypigU@+K-fFl7X0xt;o{Us(QCN?%RGczlz^e<&)4gaI(FBAWrCe-#UL6V8+HadDl~nWzh$gE#P4(BJ|8L%cIsQn>O6*@+M~Z zWNkL}UF|}}0oDkYx~7=Au5VJ4_)9p9qb9XdbHB2jp^x(XxkdzvGu$I5u5mt1_M9kJ z{e|-iU@cLTzld5HrV`tzE%Sk18tm2ElCfUt z2Sx|7cXs2XvU6Ko7pGJZKr&rZ3XcZ`$E2bwq+!4Ioxe1hwp2Fdn19-Uwf2_otY$d-p)BytTFSkPBRU0p$XWfhKAJmSk}U#ENj>8 znU#qZhSJ;W)L<)*?5#es+7L637JZNr?^Jt$kB}lAO(3t_vj#Ft8?kCBU2d0;v}*ny zOP)76zkk8dWd6NJRHoMz`bOC%C#eyxcc+?auI64S-LI5+{xv1~=+lZl*rIR0h@XMB z-uG+cMOS|^D+r0xf#RhAGq91-{DJETk2}??XMNV4UP6}*uRZ`?b;Zuo`evNcX@x)cmaTJwf&i3g!Z&2mEfFlRG#!m$(rxin?x6m z*5s5M+!-4bjNAL36)kTvNNN=)Bz1JaSuyG*ik>m$5j|3~)O0tjo^T-7VUze5gaTZ! zdu_>tV(iO;xAw}O2_S+Gd82vfk?IfYu~IwzT1r-ti6-D*2UiTVlX-3;3|=FzaZFJB}Q z)@glBL?tcGx}wt_t*lNy)vs@|G!w8Ax6+YDPXRib{T^K^&Aa*3+RAeWhVE`^D4<*>aa0}|Cp9*(Ia{VZ0#7)*r~fi5n(46oCsOs)y-sdx59Io}~ zD+fy0J8QZ}gF(COpH$SKEC;`^gm|~r)e=`r1?8z>FWqk6Q>56;1#u?e%QCu!dH*NL z@=y1NkDSU?XWrYbesmB0Qb8}Y5o-csnyhBxwmYBNfs%nNi+EmN8zQ&lP_ccn-(GTj z05I*Sq-`<@^aEscH?QjZP6i7%zDb8}4~4x3AV7{plR}P1DLJ=s%z8Hb9Dbk8^865I z6-7y9#hIxC=lOa8{(Yw6d9t%pl_X-Z{Lq?aHSK}@czT6V-?OGBA4+3JEi21~w~^>AWFLvo2Yp+hqUMKTRM zG)iHCFmMW;=_8g^HxkxjWr4MUL3>@i-360L^Ccc6)2%_SsJk=UTfY&Ao(34dGnuU$ zs`!{oyde|ilf#ze*+QFDxgxA-Cc~my{kRd-0ST(=RdkVV&?QmOecHe9Vep}-YCr~8 z&Rn*($lx=i*{~a+xqaeHml+a$&yie-41GM8`2F61&bxsoiGcg8z2@GoX+M*eZmT4V zAd@B)7CwyA3{1c2B&%<3)D)pDDK@kBr$UppGt*NGjDXU#BK0PX9~9Z@p9*g@FgieT zH;8js=7ItCO^ERT@Ud?4eJxZH3gL%6#)%6m{lg zF-H6OyX@Hv6JX=`spW02vdCF)&fMn6!=py6Lh$n=Wq!uQi^6U`N;*6LzA zz(U;$H=uk(*tzdLPvBcRq*^cEygm-g%@~0Pj#~hs62ar!0wY`#(=snoCWNu!3tg~+ zT{b%?miI2Ua8(k4mp-isR%nc<6t+n?kH0Jlg6?p8zxcf5NT+zldeKfCus$gv0Z&bJ zsqnNBkwr?WS9(rTM>~36Ds!u?UF(+~qxM9`rF1ZXqssg>@Yu&K>gHc5rhG(xlp2Fd zi;-Tt3&gFER!EvMjyg{9wNwVwd7_;&n==5SVGtwGxQWwX|}-G`Q9rwN%e87|-y}#LDn_ z@Xj!x;L{}F@>4E|ZGL_}6Y=w{{VmU{S1QxDwO8~VO1*~nK!yc;iL{L4@Q2ZF_Ot14 zf0qd|8>4FO96KBqD42%+3FE^pAX)XHZ;y1_4YTo$6TnI3r-SkNWHNKd<~qB3m~=tY zDSjqB6*$HR0G2Qk_xZD$yB~cy7Dv|sm-FFWH!p2!ELc!KGt7+Cs!p2fMQ*90D9eqT zyg2Wuv~f514`seEqoTrgsPisO14z;*;3H{aTO6mqjl0>Cp27sN-dzU+606L9ewH<* zKevc&MGqShW;d>PJ|CGW>fKY(&?vPBGX@q%Rax%qwM6X}8gD;MhVo;h4!+_*-so0& zNk-;>n&u857but%-P$oX9GX{JVk0H#YxLR&uIYVfsKxdw<63kS>LB`sv-)%yQo5H_ zHUQm4lf$OhD;^ubrd4{842ptHNS^0bbR+l_g$<7~7x@^b1rr-xiyJY6DDf(uH0pb|I1ZRIvoa&H^7+%e+gRz%V-!I!IY4^BVYj2L$Beh~I50R}m-7K7tyo zO?G*2LENFu)ACWnv1^3f+gQeelw6DONjsg>EhE;|%oJxlvqZ_u=k8F{9AB-ei#=JX zsH5!aOY8D!NFM~W5nwEm>c`U(YI)MJ;U_}dT(uwgE-ZZbR5W`uxElOvCa<*`wL?S> z?b2<6KNMvCyrVxo7WuZPUrI+_dds>2H|K5&Fc-_u`~>eu|NCWdHm@Bmzb$^0;?r1_ z(d=^c9-nGM+$uz?+|T<>b|TpD+jF@`DN@Q;9Y$NMwA6#5I~Eum#c|Q)utAneN1vcU z?cI-2*9lVeV}AIlleO&+dgN)aGo#tSk#RcMRLzwYerXDYvOX1$T7O*bH_PczNwe+T ztkqGgbokozeDJU|ZwDB|kvP*lq}70~ITHKF@8|qba|V#s!^PENRMxfr!^2xWo>yd8 zGL0t52$9$Cn_wL;B8g}lc--C4VhgO=i1Z%KKOCkkTG;$|sp~-w&_ajzLC%H|4YP5b zUW@tHJ{-_Oj_C`lE#jK}G5X-Svg=)&r)5rD_oA{L=yeaOZR>H+Zz*(k^&`w9ty6>* z<4JVl17VSV#Vc|2{P-PJ)Z?HMs!ZG5HLF*430G{n?Y8l9?OLH~Fa;{}_|Xt}1Se#0 z-s@YTkQlv@TfEkV#1(FlWZ7UV*~10-;y2dO!i^FFlRA~~9qphHIB=nEpe2ujz+zO1 zW7>h+)XyWT%Oxa~#y>PK6Eiv`_?L@KoDUv-_uF3ieE$`1GA$e#7o60CSt_*q=&ruxpE}3SZ2dd-vFUnyrZBRWFXx;nG4}N}M;5|hsQ=@Vt z<)ZT~MLporUV07%({!Hg%FewqMAz8r@RQ1#K-cnDG?bo?AVC8dY(rzt+EBdfhW~{s z5lN<%A=42lk|;+gCB}K^Drk#fl1-`NcL^(xy+`EpOeZ3beoXw^C)2tCj%uH)c=40> z^;pV8q4%C@LklC^b+=I9(^RE-rMtGV@*%ykV6E%nThd-SDhA6DzLcicTk2Ykvn*EE5zSTqKbbhVVLn-dKeBTB(sO;{%EvA zc;v_g4Ua8i>4AHUe>~m`O-iAa8c($<vNw}1{CS5R?pW4C_K8uj1UdXk)@NOXck0# z&O%Mcj{<{FdH5(REQ6(!8BG=fazaultXNiaVJ%^(c)6glO2M{RuIDH5R=PyLrrBrB zU!>khA!7C7F;}vPw9TlK7?|`Ol!juiiNR{HBjflyp_jEdmBZ^j-mc zqFN(PzGnhqw|eV>ltmgDHxt0Uvz%k%EXXYH84Ffmb9V>+Dl#4zUC-_mhfX_ck8(v& z&B{=mdREyqcUe8aWVsv!A>CcqzW)hjK?ZON$Z6|&g!ksEw@32vEpbZHCT1ao$GJ3D z%}Rm1OGQxC1QkYorH^r*iL&Saf=&X}+|hh5#~RjLwBNA8>5=u&huDFlKiY^Iki>Pb zId4cU#XIQ(E(}sGswewJQMU|5Gdpq!Y~8wZSD0kLcrb8r;lmgvD0}uL;|oy|?h@=; zZD42<1%<<}3tvuFPz_iRFNBM`?N$MKkLJ~!oU>=b-qw#ymEc+m-puuRTph>2@&&i7 zoh#7xo5n;=-2=NO3aIA z$Rn_mPZ)Uv#YI(G98fh*dH?{ZaLY=Fse3LTFO1l{S0P0In;85~S5N#ys`%F?_xRT= z&m}X@O7|E?#3p=82l`5^YDc2ssu0(F;nr1%Xgd&ZuL0R;0Z+hR zw6OKf31`x*-eVMO-QN(xd_BbRLtC%0!~PwZX@vcF7P#wY=|y;W8l7oORDAgP$Li(1 zNuE4MrJQ|yuu}yPbVXzJ?aRw3mJ@7N9{KMy)I>7IA_CVg+ERZO5xtFIj$ZDk#<>*gYv;=wqm5oW5F97#ohMsh`ToKK+_%as#*^}azO z)fJ1KOTpq|?Q#idS!UB3S&oOQSSvMN8gyFj^Xd4Gp=ENGp(E@Bb{@QgI#ue2kwn~W zAp;Gbx&r)pD?Gf6>;i_m?!2BO7IeAWZ%2dsw6v3%Kp-hy5pM2Zyvqx8n$ArY*F`Fb)sKe;&k_?P67jIwC%1)2Re);ASF`9~G&+nU!*=!R+nF|3nj!X?-U zzYqOc@yJtt@e_eV5p4qkmG9oo;`$SWOEh4Z>OB8@H|9VB1$q>I@A!I^PT|=YzS|xC-sdXZ;M87N`6Jr}3@t>TKf0MZ|bu zc;*N8=d!C_n}5s*V;5BDVuqgv4N^H+Gx~<2(6Uz!Tz7ZLF_C)w?;zT9a3lOE*EV`lxF!sl$)RMjZ~Vv97jQ_XR(0j#p6w3qLgLVXAt5Yf-oI3DoZwiDDFReibDC z)N_pWLo3%=fIWBCNgUc4b@i<$j8ULdYOmoQbjmzvd)hvCEBv&)oUzn1>GxwSnLYoE zi+$-npF8JQm%p>s=9SgS5K;6*iaQGDo!|`jb~s7qvMKwgBlFN+A-!cGM?9Vf`>Ab8 z4E`7s3h(T+am-n_%dtVnN7wsM5Yzo<4RQW1o$E2|%<7fzttbi(ktS~Z2P-d)GxyPfY3^sC2rt zjg6UeWR;$t=Gt9_5=Tpw&%FZx;u2$}jkNV~SM7Ze2#B2IuO^{?*iPSncenpwx&67J z@Zg3uv^rae;ss!nn8Z$kQnh+P($xPk{NDxhKhMf-OFe94v{kJ;2`dE)BagiV0#E2a z@h;l^zAt3?WN|d_n_#{aO{#^gyDk+F8tyhmaVqb!?U8c(eRY>cT>#qpzr(zUa+Xl} z5rnJl{q2)~59H~dS{6HYQ2@M9lusW6mQDU1NAvl^eP2KMc7tF*w4R7eAr2(a+H?=UYi0Z|=rgAN?`f6xj)sX6;|i>=2Zx_Z1=S9qZid=zt<% zLVIE{;dQPpjTxNVy+qS;Hc94D;O3RN44QNxbdD7)wIp%J%*p?!Z}+!p#6G)Paz$d1 z!J1>v)>g=@@!r#tPI_|8!%LOM!FIA_okG>AHMt3QKZT9Z)o=0DlRbpbQWc6~+*pGntf*mnqwfT` ze?=*T#f0go6cBtG*&?E%7dk!g&NkA0{xdUe^tpl0T^YfXf475Xdkm`Rl1wE5Gk)aW z%t5%lXUoUNZQgaz(s|jEpNTCOji#ZKtX#(;OjnD>>-k55Qj0ZOc=epMCU`C63OqJo z|9kke81X1VAy%Pt%ziX+==jsS>1$QVxuP!Un$bjG;Wsm^GyjHpRCHNCzaz)mZEch4 zii*HD;x9ga|F?sH58@PM75w+j&&^FL07r|DgHR4DT9BB>xi$CE_@Hs^T|tNdQS9K_ z5U3RQ#m9QeG#s|B%d%L@=#WU3#mz41`joxb12Gu<2 zmG1Po)=m5?D+2HEutReM*U*0J-D(IQNyGP z7KWjsAO6BuJkEv)Ucn9{Q>u4tDB}Tf2HJ780)P*}Oc%E;OQfWv#+*kaB(3_2y=##p zgckSwe2^i5@^ZlZW33C1h?B-w(m@JwQiADknA6&XVJ&%tmLKN7tOS(iMNY?+&^MU3 z#j$JXp;_}Zy>`f(>V2&c%}~wN^~E<0*j7SJRAJx7#+;!I`Zs%-y<=|w0>`DrBW@(h zSA_2)k41Lh>&T5vADB=|6;ReiM8kuVky2}bZWL-S%f#Tdd~hrRHk62nh%kk8b@^YP zj0PfzbaizzMZS8QYsTv8%cp694dpwRral6`)n1N}q5QKFKPWXeHw$2Zg*`V(8=9K@ z9k+TZu1}>4REjMq$vtc`L#x*AYDZGg|9LhbYj<+1@7V_m2m})Od-n2pz0+*B_+|Yq z10|hemxi)38>b*6Wnp1qks1IX>e!2im(UZ4%K9omUsFXT?a@NiM@nkY&;k5e!rJC& zxNl3DUfpQpudRlLmscwi%LWytu{GL11O4|9@H9VFdP>>-1m$#L_9r#E86s=1-nK)@ zMI-pKcE!mlXPyPRW;Q_=WmCWJFt6KBb7n;y(`WgKeKxT@XrBlup})hr$m{uj9az2A z#`IIMhDCNhzP|bJ^3cP`A|=$@Edu-g6g_7L%VI6adcHBruJy69qBosLK>epIcX2zp zNm0<@r+lSSwQFmjB^9B<*ch!$nu?ADy#-P?m#Prrq5y7g917rKYXneNcwz19n?_RiXYK=7PO`yJ;UzKRSs z&88OBODDvqtjhT2$eYLe`b7I!+f^^fUad24ok4&e#&ko!LHzIB?>cnVYF|?hdG}OY zo9tJl2iI!fD%N#TovaT(dnQxY3fd`O*H=1EFjY%-OJEYJF+H8r^LS8%Tn%pbuYxaov{y4rEbgMMTO_<1G;BSveymvMDG2jvE~-nTe`m5&)vs=?@I^k>51dMKkuGc(BSFdU za8;ce7>LrXq}*Zw?_$6GRkc?4mi_hrqVSsrettnNj%@;rfb%4pp|vTF@t#H{VS}4H z5?K-=viI==11aKplbe$@Gq+QN+s}R)wyy3`VxZ&^M(9fb4T&A^*3?7$ws2St zontoqPAdS5z(lQT$%zh^o!?CLEPPtjg&$Af&IKPbDfAquAQJ)h#gxAR37S?k+jK--YksRGM=2{dIgU)PHs+ zp66JuUZ!5oNBx;?7#Oe zteqPbB`2`D8A8q@$_Y|OK; zeq+Yt%JHU5wTw(OHjU~drKju!IJYhc1duCSDccDSCYThoMGue z%@H2?S;q`et*elW*K4N%YwPsbJ8X1nDYSveNCl~y1zL6*;YQ97!}FBtjhl#~8SzeT zCs$wLjXYXkP96-TtNuSBU#b9QQ>hsk8L=nFCqMnl@z;RaC#b|^a~rP^PD;D-y^eso%eAaGY=u< z3pKG;=>S1;oUr{H5OpFuFApbL-h)=7W`^pZm;{}cpncH49)Gkj-FX+b|HB28o#v;_ zydFUN=0#@gpxLo{8DJPHN9$61dwiSKel05DSDZo`Ek!XpD(pYa_PURh@TZYGY_Tz% zJ_hwh?@vC<5cRi7F*LWZFM3nhrIA3$H_F9+;M~|ZH^oE41dSf|8L`^Wbm%jUL8h)C zI%x#O;t+<;_a8$Bz|hOP+rLzwdplE7-gI62dr*IuD|`nYsGXWa&-D6@fMVi8QZimb6Nm2AK7k9b-YiB~pJ0(yBd zOEb9-lZAadS1f_{o!|zKxcice6rhD5PT0jd7{MT1B*X?#ejTg?eYySwsu89vVfE)(;a>D$EgXV1`8UwwP|s%8HVQ zI05J>Pu^cKe@=w)*5#PCrTjHgy|piiT@(J)M;HfFG(+Tp9ETSGag^+RG?q$Vmu?Pw zz%PSk%q*_8e&zFRo(Gxdzn+O`Gm|&JWK4T*3*fM6P#R(pCa|m&gn*E|NGz%&TugLm z%YeA;)$&FeeAy#9D11xui79EBD_ zX{14%6ioJ>m3pKE(V&gA$RY9~J)dmHF;}Lr04fn|Ac^rNk!T&twodKwN$bXhqPpQa z=AQCo$8g?BDT1y%`w4TOgGD<%Op4ckpRt`%hG@6;FYdRw{t|qHxP>Rs*&PgD2pTWD zO9bC8k31|2z`mxyR+~edaIw^9B1i$dnY)^H@*+x8u9}EQf1QeM@QkIedOOtZ1~s~qL0Z^wW)_13c=yO6GULPD+$8zq>~DNV&s|pK$>75_#hr?TdVTBE z0cn@WJ~K0Om`thq^hB&Mc&u!$vC+-1BfCQ%82S4mt7*X3#Y9bFwtT?>TiAEi>NMi_ z9MsHjXH3uSp}(`U`5EA+FLB}dpFb2={WzC{AC?x3lee6IC8!r3>s z9ilO}-#X{pUfymoeY63I|A>949gxI{jM;WXerYo~NJ1P|OIU@x7`eIe8JUFghf{!B z^h$Hjq?K}BznnCw_acbGz*#9B{vRRce*~NV#g(bRzNaZxuqHte2%@3U(%`o2;_TdD zXYLL&FgG_hHQmV)@Mr`l2mj1(GOIG|y!x^6(!}~O*uJE#sHiJX%obYX=KIKSa=mF~ z?~CYKQv)&!ervZxj{cY(*z>XBO~@)LhO>){Bt%bFm#$Mdafll6_1`wXpY`#v-F)@`$jXX?5zGOFGM*PtTKoBZ_Vx9(gx7aCV7{H~&Gxw_ zXOW_4YNilz>J<_`Ybh5HEc7p)G3G-R5E3FwzdnSMWJObd>d7natD(@TyuH2kgyJVm znZ*S6M!kFgp62}K```iTi{~ku?y%L{&FR6Cl9CJ_5}Tt#Zi6=}&})j_=+F7Xsddhx zdOHJhF)CB6^NlKq2Fyv#z~mE~c@oPoMcywx@gx-YLB*2lg5gx8Lz zr>7^4iRL%c}@fb)rgo6lE~O*LpWO`i1>eS4KUW(&>Sn`n4Mdk_T+ zx{K+`ARx+iQr~GIW=WKNC@M=bx(MFo72N6{wRnw709Tfk4cB*HKG_cp1ltGP<#)Z6 zwY0VUmNAqRvTQkelKf(0PKoA2~@czDK{nG2t5g^hRRR-PI zK7%n?Io-aUj%Sn+wxn<40LEnw3KJdQ=)?<)ip))q4Gj(BbHe)UEZ^aR<{nDWR|NR~ zLXwgwj#q1KbX9N%&yIk=SZp#k_qXX+TTgMu9DFaaKeb4de5!nFVPZ13_&n6SE-${R zz{;^Kz1Awdu;|v|E(rDf4y`%lO&0}uY+omi-!*-v$J>eR%`6)^y=hbSQqhLbcFh9b zFTx}!x$|-P>XY6;3MY)Zyw|Dhf+3@u>R)X@I&PYW*~0ahNnjxshAraRXE?eJc>6UO zoWoBr7}JS7e5bbSQGe*QpQEuEqUPGZ`uecnARCguF^UENQRi=E1x|z`%8#2G$;JYkk?nd8q%%2@DWu-mvu- z5Xl)gw@&#=RjO_+NFF;rer(hRtK)ao>BE;U+MfXMY5nz9jEPNG}7a}x6b0J+Iuqot@g2^ljt=-y9U;3j#vNA&** z-8ShD7XNztXEF?ZewZ<3M29r8-+y8cAIvYLF{e~KgRI-kS~vO%cegQu1^LeWHp=Q6 z<)_d`G;VX}UPWu?cG0kMbKCxEOEhOsyOx$NR2|}z)_4y1$- zO8B!ICsWMeMShDj`}^raj{KPD=X3dQc+K1KloHkx$({vPhQ2^;Mn94xIbRnGDB)`+AC z_+58R*+NF@{rvnK*0b%o(+j6Bf0>?5PbzB{l97&w#^#7CCY+cv9ULA;00O`c9u6Y2 zSJ17ke-h`9CUb|nm(2$4{wBV@l+OVELp!EUTd>Z~2Wcl#4_DXkzF;SYQ6|XYvNucO zP=}`%I}cCJm^nd8^4^|9utsHqkV}K1;_KwlvmuU9{X> zoNA@}E#B>Lqzsarb~Ni_(`H{!p9l<2WO@Jq%r7vvV__lFAIW~GElVvbo&D*ko=UWZr`|a zwFb@>CVRTO8jo4a1*L%38{r)7qWFDvz2ot@T63MGb*UHuw6(mo%JFtdLOox7p%H$y z{+Q8wb<8&8ULGDiKuAh*vZ0*uT10-B46#<J^il;m0#`rdO${jO4*~ZYn{B^LL3l1H(txS=Uf6W}m3s_avehrOH!d z3K|+3f6hkgD7$8Y((PHJs;%*dn=IY*Wf3`%)e#XI5x$>c*|nr zm@o_=R{SOF%;pdiF(uezOVtuCjbBQNKh(D3ySfC=fMdu-lJH2xin|6tH(rdII|+zQ z4AMeSIFt7iuxyDa)jZ5r_V`r!W*FK!4Q~MTT}DTt9ww@7@i@n{`t#umjHs5hN^^J{ zcV=kh*7amLdtIg52TqN}lAk)k+!dU7ak+&&pBE-wx#$jiwBHQz@wu0cdTSSGpE! zRwEQ$N7Qcxf_i>3tUPYw-jX?D@}>A{_P$Hk!O9wGG5QVc*ddbCOp+=$F%!49B@%FU{nbfV4QUuY14q8 zOVQweM*W$Y9;tpsCVfxub>skoPQ|h}?w_!;yIz@l3jBDo3V_)FQok5u@t>C323KR6 z3fPm6s&i8=PP2~=I}t$t4ymOLo3*YHA}3f|0x6CH>oFppwbMt?D7Xx_!ZVCzTFe_y z976l^oLSN%vb|ZPhEcxwLuJi`3^TeAsV69PBK4hjW(>QY7AEM0p4Y~sAtY_i3PzvI z=pag{ns6!CR5f~3OZM7ZDdz?~@4{;~#1(wDIWBwnFn{&HbW8$XC~n(?j-_^Wl_R{4<5uUL(z|Hq)G61ep=Gn;4 zo4c!dpnY+jG9otiE)87;Ks??&GFY^qhgZ$n1cUzCOfgDncg7CwE76-QWxj5e2Ve4a zdRLnBX*tkz?k<0D3Kd+(xWfL%Gm|BP1 z^k{vX@bZZ%5#G4e`p!YD$ay1Y75}hqGFHaylacqa!%@<<#V;v?v~@uzjnH!YO9NEx z59eHAL1l*(70f5K;6<>}E8R36??E{|Z_|rb-IJ?nazwL!6g4^XGl!243NiRkYlags zf^ET5qbFf>3F(1a8j!u)&#vfuHn)aOAJ^Yu0TkF*xLWW*I_uzLr4Nx4tEXDX&_oViN8ARQCgo!xOx;nRacKITWub}Km4V7n;9CdDuYG2Ej(sJR zniWd?Wj=b03x$~yG}G{7%H`KNROjxjzwNN(I5w4vc2B9lvzz@$`}A-Pql$bW0Y&tU zTB5ja^9mMmpW#s*b{~BDm}Y%V)R|!;=l2ZEMo|U#Ma`BhZ|+YK z6*~Far1RnOBuvpH49#sFyFHSO2)lXDp>3nSJi+mBj=gPuD-4ao=c#T|5w_zTJ;M>l zlXt0t&jScR8T_m-V`VGntlb_h0);#l9FFTAnsceFja|501u2cE6CC8?)2&bv6dd&) z&OA1}Sd&qE3IA(5{C~Pr8>ef1JQNM-O~KyoS5r4PWM5qzoY~(Q;3At#TovE+6*tu}`*g0ys2he!^mofp ztA0afnjbkJpF{_flLb`y$d!)4{F82<6R4*W5G_I6_C2?P3M;;~bR zHJO{yMq~ahRGA0p=9cHjCR7OqZXUp3wTfkBAw6WFsDS|=C@I@g{ZoeDzxMzuKxJId zX7yx@DkAbn$5?as4^H~|x!pKe>NxMIw5962ahhHVPX5m;;#&wZV~ENo`Tq+ z+hZ$S@&ofM4fJe01vSOa^te@X8B#sJT=c7qExvqcc=%XcMy!TRvG2b&H!GgespP5v zRPl6$zvl@Ft!zF~mwHY?dPoM|s*p!c-n`Vd5|~n+HQe(>xq(`3qUz?MprARsx}s@< z0}_s8!Va{2L8b?b%ck)F{(9M!AF1bSD?G2vc?IbeZ+yENFYgw%hv(hLPuxugPrG%i z)U`B?N|wfa{k0N)+>d8}#if`Si^Q*Yz4WJw{NoK4=_)!J7#;oGa3=9?qm_^S+s0Z> z`mpIbgo#U1C1FVY0s{e`o{=C7cDkpwo0ilvz$NIUY3ZRg*nAH9R#ymQ>xo92v48U( zXspbgTt@gBS%ntGl1yDzO;RohrX;|7u*@`N|1K&gC-Yh6vAcg~XjFjXG_5}~b9x~r zX62v$mj4Ivlg84n^LFo4EU%@l!+7!Js;Nm2b|1O=&*<( zYTaxrva=D+Tv%8*BcIN~zt7syLXwG_%2u5Hfhi-eEj*w|-|FJ_$8)XYAxnK(VuMR=82Y8 zt+lBmg%%v?8+3^igM&;NdTVRf$kv>@J7mbRYJV+t+aA^tHhAjle%Mhp;wnFDHf1Sq zQAZ)dWIROz?@+uzMU8d)JzrzI(~TQJOc~!^pH>kfXQn10SIBiSXRI^Ia?m#>$OU)z z|N7O@abIFy4j$-jqBg^Ifoa)E9=mrl-q?IVv(~Cz=W;Q3*aV!sx3OsMeu~HL5@`1+ z7Wu8B!kRHR?Zbn^DIp<`vWy6IJ7nx&TXFo-LzxkQWkxRYs!4+KYu>ck$<{^` zge#}I<1*a)ME3S2gA#O+v9PY&S((wszpJ%%$-nSWlo?_&HlV6gZY;epKaXGNd@M+s zJ?#Hm#c0aBv5}5FEzq*!SRw1+U69>avaMcMQW{6xpx|^3oSR?ZQ0l?OO*_{T z;>ReZD8D+3r|)Jq7R)Hkl?)e$+X6l63MDfR7{M0ax>@N->Z&X|Zf0&j1d@dy6vEyW zFwW45a&QC4aD9FOyYQPXH%DXXVbS4ytr>cr?j&<_@&x>vm8F}Quq9W_(00PXqW*9} zeEl2aQ5l+ENn0B~L@q5UEAHIS}Foj$$zHtho6JrVGGqct$lfYT-B=hx|N zYo|~yjh;uT48DuSV^Gt$oa3E4YE;S*(hBdq462W_9^&szpSHA?>yH^%f$3TwPtk;qd$A<>~YD(im*awX8H9 z9Rt6tiK9=`=E*YD!gjX4D@(PLw!mvI7mhUf@v*T7dfYBsJ1dQ^;O^pL3k#3yp?lvO z`2onOkB?Dv^PHlAK~`GUglWUr{(cD_x>&AO-BJY#ARuq+tB}!>{#pE}oE%endOH8w zl9k9(dwULdIUr`c`>mXer=2GzDt8H+jd_5W1pT*XW=?FTU2bdxAl%efc6lEf3e9X| zQix7TUC!3mMykvfVCxS;*_S_*k`vgMqNsKevNM^&cH<{JdzU+2Kl;D?Z61uJqoqxH zx3Q=kQQD~5thJ@N{2QXqS{*q_W7Er_Tu~0i2wl=8rW#h+I@VM%2robZR@-nh7}YPmdb3&5#8j`{Ayg> z+~mT3j6?n)2nyi0u37iS(-*La9t3MLnhk3=?lva_zq;p^vzWC6(L=>DE3L41oww)Q6Ehb1SF;SJ$?0Kn6%TkuYU!S3V)=?fToRTuO{ z*+KMJ4bn5Z;qc>3u%q&H=ZQh#v4}6Vm9_P98nuW>cV#6qJ+{eJuCJ%1rK0k++Ou>0 z@X%V%EOyWZw(3=wcEnU}lQ22}UtML%sa{FjIbI^>K=#l6EN5V5UR~;>2(OUS)qdfR zSQAIqKYldAEjfvwZhJnGNi4GDl92AZ1quY)c2 zc9ClE=rgW)wKLm*L`gC`Gh_2;YpDPF<$v(*Z?}q`Rx_#2TsI>ts{>Un0|4NtIuegZ zR-T(HO4m@=M*Ghn8`YAZw+h$SacI(fEEE+L+x4r?&(B#=7Z6B~C8TkSLij1a8~i~;1bj4(vsDQF3I9~+=I$1~ky>K|1?D)|-b=~KmZm*Dj4P^L z|5ZiiH9wHF2icvMHsE-BXORRXCAT57_|%OJ4RUQ>E^-VWuyvPple6#Bws|MIUYE!ugEizKIEX*Qbl-H%+=+k+~E-gn_ZIzkdg7+dqYKzvngH$3b)j^LVXk~ z9;PrNO4w0fb?8FA!&>R=>;jtp{kvqzkz0e?XEx#8(2DuWZRLQ}o`w0GjeBLR=2BJa ziJ-gI>U#JL`D23X*`Ib{qvrqM-G6rluE)Ummv<}rSRI{P_Wv(19U`k!ewaV0V8p-R zz6(mVPwW2?&zp`cf!EOFW4P1JY}r1Z4^F@@Zu(?rM>0zBNA|B&;M>$V6XOe+uDlr( z9SFiXeq^yG_c)(KT5DA2#lmUgRAzno`!&yN%R2gjjPcEjX5=Wy-nszQumMjtp%{LT z;Bc~I@CtV`j*+C$dcs%aX!IYJbu0J_T4^9K)3&@36r7E&jeomgt3s?c?#TV5<+IQ- zDB%9vDyEfmJ^{HysV>-Z`#kt(BkM3x=V8KE$0OIAM}G>5va%NRvR)azZ86nGs;&ZE zad9^gc>ZJI$Sg@LX-C_jNayVZvq7Z4$Kli^@0iZ{YrVo)TNHq(Tgfnny2f8tXE5jE_O9TWMM7j%3I`HDfkxVDzKz`tU$17j%jm?aFo+oYR*NB~&g_3=^ro~)`Q^^_9mz8oN3G4lPX(e&U zj2g(v&>`)}$bI5Un);_{K4d5;%iK@gdk3b>pYcw_}F$ATdJj<=_74U(K%( zp8fV;Z^wQIi#%+nLFJ_F)&|q;@J;?zF_Q;qguJ9T%DR5D$7u91t@{cZU zmX#5Y6TLcWEUP-2>5g2{JMA1H=PK1442ip4l2O_-a$EOhDDxG5Gxl^VsxIjp_bB@| zoB5MZvwmx;%>95napFfh>im$Hjvf= zF^jAts*Wz_t-mBAo5fYJ#oFjI8YiB%90~cAs)sc+WCKD236DGl4^`8NEH+Ol=RV{|p&N!X6iHY(N&q&IOg}sd_P{WuYO?}nT!PkP`d%-nN2?$oa)$$=i*s!DGBF^~x zkiyI_z32*;psc_QD5DY$ITNDIMnE@2xn%DjRq8Ojdk|Kl5kR+@L$+w84n|YH-oD{h z=lsn7BUdK=oA@h5vHTCXTJf}-^e-6Ren1gR!c|7ZV58OPy>f+0e#jJ7g^~QaRTiAi zQ{naPT0k6+k1amsG_~$OAQsvKqwiKwqcIXyBCS?%>Xn){lzvzBYsmGVTLT?Q5mL=@1}&u2$zkvL z>&{cam4p;Q(`=(v%ZheOv3Kc>SEv~tJufr;73Xn_W~}5&JzSXmNumAw%Wm{n$UAtD?L~jWNmxC7J29DHQWw$Aan&m#Q zLO#Pf&AwsTx%8%bwo^sol>7SZtX^egr0s3LTs!|{%LS5Brr6=TWxX7vLHnsF!(fN& z;M_#y{<^ivik7oG%`q(qJdUe!eRY-Ij(IC}sh5#!d?PdA7iEDj#mZFOkx4{8saNo$HAHnPR9*UF!>dA15l5>U)F9 z{7Zio9USIE+dpQw$qQt00=8a!g{D{Yj5*|UiWTGP`Ys3?_@%X9H5Zb!s>!wu@`EZy zzTET`HNU%vZ&Xt*f&8c6y7K*1ymUPljzoEEnXYc2W?(`nCWME=PtSs}>O$Z60kBLa zh%Bg@7~FQ{Dd0k6Nx2?|QfAcz5a<@R1RJWRo4l zzr0ML`siACW;>|YN+%YtZLVy5=qdRZQl84Hftfn8>hm9Ev(ytgd{)s7)^1!iXJ8W0 z4ud7;s$^DH#0uoXQ?m$~o*qUwDeK}+jn@bVwyimpyF9aTbVcp>Ny>4KM3OoG(c zXS4cRXNDJ954mwaKt@H#PLfJJ?YbdDb(h3Q$~QH{Fpm$JYj#^h=0?t>=I@rHxhA#` zo3(Tr_vd*!Nic0#MUogF(@<2ShhHV7hzMpP28C~9pR0qw+XI!fv@8G8z)Z&|)MyOs z&IRMkVSfwUvE2)M`7^^+Fe|>>Iy!U_DTxe!p2gx$mi4EzV$b7NkNXM^G#u?K)cW2h zLL6M@2}cqB-I?$)5Pbd9vkM4G^X0hSWGK=Sd?{u)xhdE1d!^eMn&-Mlv3De}_yhft zE>ZUMV8Xoq6-+#4IPl!R=V$fT{l&7)!r*s`S3V80T4m)356lWUS&hp4x`)U<)&pdV z?*$X+Pe!I@A40^mc;^*flKIQ#$Q)OlG zivL5~PlGTC=z8&Io30>=HxGXPmhb$(Y!M_rY)7ie=>}G_)|WM8vI@Obmcptc(Tdk3 z5=bR{nAbG%jVs$Y74)qL*T2y?5>Uw%It_p6*MS@|Jw?F84mb57lj?%Bs-y0&Df z*n;^(Lz=3p0MiBYhCYeIK5(aGRp9U5Xf;C;&QM#!{u(PSB2Ai#qGPcNptCnL@BkFD z@jSV|Ytl4c;Ip-S zX4=8#U{?G{F?QftrucdxRwj>}4C!`-Dm zRk7Io(~^;Bxv_KY_L92O91O~Tv9;N%qJ+^f(=smmHUX(^=>&^cI-7NEjttmbWD{jr z_h%){a^5sK9XFkm_r~XAp|R1}j)4^2Z&Qz|*_ftttjL@C18;i{_%x7AvW$7=^1xJe zgfmL~S#MipjpJAnS|7+qpu&3O?eCnR+E!s)w-l@xOXBRcba|Jdb##1F*f^ueTHeI( zf8ZsDw5I{{R%11Xy3n9#;pPJd%WA7}^T(SJiLuqFUMXP!RhEaY~ujU9Nj^uAy?!pn3no zs;&HJ2`Sh!FFeYXU^YkaF|(JImdTx8M8*Y~B1nU1fd_piH~j&+bT z1C$l~_@SNu>M#<@uF5uRgBy40{lFTGRu4Oz0dS(j$Aq)Q*{b#-uM*S0Jy!FfKWznv zh-6IQwJt>IUrb|CYAM=3N?h6#Ydcc5DS~Bec8zfpD?{Y%ASq8d)TgB5 zkW7rI$g=%IyY@Sz#8qwshIAYXbI=6hkfH&6K`Vh+{QqMRB{K4R7@O$6_rBkmnCQN#`ug;3x^UXQsb8JBzd+~NRjo@jF+!R!d7EPO!4IicH zFvJv153d#m26{^)6CdEFX03lX1DMP$u^ks=qIeD-E8P(pSYuJ7!o zBx^kj49IL-gAJ7y2WeFwENL?X$1w8G!>_I?PtGq0W{{-=pvc-Byt7V^o|!1b?#U@6 z#GWYR0kkTi8fG&kwuIbdBW`zOL|*jUTU#rd z<#0!Jd|(R5dw#zApd26nV#{%nnQhF;&@)* zg>7O#_VhSg*?kQRdd(7 zd)1?()WtV6PT=rp+pFd1jApsm zoa5u;vVY~f{+ZbSOCV6fm8|Aj_g$>Se--=>j;)0*GBO8yyTMy`Veon9^&hC5uYB=L z=i>*6T=3IsZ!f%HJCRpOUOp;1nu{1?WMl#r{JBxns9e1_r?$26;c5-Ro8vbbnguIR zWyo~KEAdb$RFZDd!f!+qDZ-vo_@gc&ymy<%EliAUP1WTS@UgAVB~twN9qqR_L}d^X|Go~TwvdC zQ|W4kop&@_y?eJDJ+29ADfFV))ho{jb~PcIODFDHx*uwF_>3V-j2y4~kl!G&P2fdq zXx2sckEq(6V4m&VfjtQqjLhqayiHwQz05R`HAew172i*oS^q378ZJtSoK26xas6Qerz`u zny)YgIWum=^PoH_z(TC2gTWfe8matlb{Owpt}0QoyZUvSI6MJ_I61Qv)!54he-4)j zGkPQs%5R13^AN{z+Y5i}y1LXi*!a+8io&F=-Mtap8Y-TL7#M{*4=s>(561msq_X=_ zws1rhFCz>Vj+Y-Ms2OFIXc$(v98x1Lk4l;hAMJl}&QMTv!-#aM^3G)G=+w$>jwUI? zifV^Lr3X*b(@C)h5xeHc=@Ne&ZZm=z|Bqg-GpeaA3;R^S2KrDziXcccAfSSD>AgLA zF@zd=?^Q$?K#`(|2odrCsi6p=8WLavMgk!OLk$q5N(&MQ5EPh*Z>^a>^Va;Dv+l36 z?%L<>bJp7T?7hG5qZXAi<=sTqZ*HzaNOGXMcj5Ub&*BX3+vi!m?l;^+zwpcJ@0Uov zk=kns;5#zmwsS_0{5%WBV??v&()~e8H-4*iRhTrfZ`M9J$^7bZQ?OYO_UJBB15EGU6*2`^UwTKzFpvD<1$mCY-Kt2Dc`33Q!PP_n66LS zE->1SGp8@>6Cuwy8~Mci?pUW5du`&?4^FJuY(0fMPtQi#-A|HP-Z+~wzD^1Ca4#SC zAV=)AbkM@3zPbd&LklQp3&TDg=8&q^py!JKoX@neCJL#Iy82PV8nyv4Vx16c@jN4Q zwIq3AC?0{9G%;yNIcNsx;s?T-=fa$RWXOj0eGPF$t`UJ07Pi7Stxlm%uad7-`9c>g z+bktFh&%~sSeGYtc-#)#sse6Bz5li~y20pL!5UP%CocdKRqWPIuO(jOZ}Cyuv{cy9 zN_&9SL45Fmmm2|cW>Z$fOEnk)rIQt{JEjErk-AwW{ELG>8(D&Ns%VG_4*NE5ES+6$ z<#_7E-Ms$A#gN7X7Q2p@pk_@i$znoKpkrmQVu47tjU?F~gwoB*Qu!F7I?835;}A-j z`hLo-ffU}(_I#>d^c~j5-CaI1R@o!;PeN8I!-1alF6Zv0)pU16h#p_JnH86$v2ju5 z!-rC--t13VE$+F%8t&NnTKTsq0X3DaGE@k~nGJ+m%Ea2OQ|-j_4|Uxj#u_8-EY1TQ z=X%tR#RF0=TYzV}a@FpLOQ$3!XPu*J>qCS@^ITJlepg)7(|F>kw)gG8$u;qm*ix{< zA&g^S62?xx->c|PL5a}mM8`8D^r)h0x?8sQcY_><6YG?rm!vsxcHzN~1z4Ym@G3;Y zdWHYG3RdrqjoAnv+5zkKhmQ@zA&q?slC$Jn(6I^8Fh z$%~$>1%}&EQVbR{rV`93mo#(I;Xj(NLep?&foz2cAuE)!iZT652}aIP_|RWM(q$l4 zdnSPx#-_UYByrGF%Id!R+aheNfgc%lbd5{F?|Ff$+L7N!h^Lv(xPLATS9q5#|D#_0 zpdxTVJtLxNAMlr$!nWuq=HRQT>MLr%TEZkNk}XS(qvhtKR`89#XfUVxRwlU1v`(;s zx%v3mx^INdV6iLVH9D3fdH2rOjE-)w{1={^ws=Cb)&<(=>6k+-P2TI>>~iy9*za8?qb{zXXPd(Q2le$9=KImVjOkuz(+$QN#HX+%{2Q zBEv1~AA`XR&4nXy$y$N};!qh}D~%q@&;goW*RAtt&P(Z|g-B=NEki(l{@kq8`d?RD zfue}epv~27;+Mb*g_lVdm9O$_gxOOx1Z0=Igap0dq;>leDx%B>yWm-4%j3q_WRX|?=^mhDIiK4Fch!+q@829oO9ddf=`<|!&C2`S z>Dp#?f)wah$lS|YjczmGV(L8AeE%pz$2?Osd&$SgJEN_BYFUrLA1!4d3On!v?VviM z7I-E<4iTX*c5Y7ok*;nLBK5=gP*?;(`}*5%b+L*bvn2axpw6!9q$elKtCIy!6@3Dk z1ca!AHO+|R&le?uRiyI+ajLxYg>#9cG)xHJPxqnoTTx6s46wujfR_!{f6et^W`9wK zsmp1QKgXfh&7A4OvWkK%ZHZ~kR$|1)M&Zgil?N&`K0apO@8uQMrdQihNQNH6LAs|* zm1gnXpB6i*3_!(nYF+%9DCBT3^z(8fw#>s_f}d&A8b9YO0NdhKAOi@NW^J_ zcgtGt=%nNsEs`oE>bunan%JKvHUGL(b_Vcb?11))9j)?f;ky^v`p4~dzgD2$XBm`M zUrgrJz3mAeoy)#_dl)~u`s`0of*%Ryd3+yfZM zFOc2Y(d{*fT+OmlCov&kW*u-<5foih9gG}`qK#oWnckl>(VR(42yZhUx zPYiH&bF_9FF#NpCT?Yw{sijEe+T~ReLTUIdw*_oi;@D+Xn;PpJcDw2P0to!)#NUo@ z1GUH_nW&y1-lHpGxuI&%n#5Ova}8P=b0a7HYF@EU@{-=)gN#1+E-TH^3)lt(l@tpW zOCqET#vKvxw@KF)chX)|v^#ng#^QHlp@mRYg^Pc)-eWY_MbK94bvWMxj^o$fyR)DD zby738iBnSrCLRVpIorWy)3(t3{MXxs_y||omi#`5$9=>Qi6q{;6Vj$pYA;tu^iD@s zFtAtsfg&+ezBnAtkei?X$g70D8h{{pt`$%y3r5Yh_B_hLS}*71#l_`nL57@ZJ{QCJ zN+lg%Y|m;QCvfsk37s=g{NY_wgs+#^=Jx|lP0cc|CosQ#7UupTnUoY=BWLEFDZoY6 zIH&!Lxv|5kn1|&k+xN5=$?a%l`1mwIpf}V6YBDkKkF?yC^f3LJ znws5~h@n0%3%&idEejC&&3{t#u4i8oK>Zm@9+tvTk{ah$J3xYG= zg7T+mEOK68j){&ILhk)>{M~Gc{IwvS>pbu#(z@gogR+~yX3G;2I_IGA^}Bc-M@RAi zT31Bj?*e*cT#pN86l;Y%3mA?TV!wK9q)rjQX$VFp4Fgeib&hqQHgjRXaX!x`2(YP= z&FvHR3JeS^Y2!Ng4`Bvi0Ah%C-6^Kdx=?3JH`di8 z9pB{X>nTQuGDuE_D;pbfXhw!S^zBn>WgKc3yz(V}*i_gXbW1S%06f*Hr)^E&i>Rf^KwXJQp zI0@=l2eDj`8oGw5`_G)J_z&bkJ?Ykp^XRR2XvpW!yP*g35tOAi!kB}dLtpuB7A=%I zJ4B5IJ39y6;^9lrbSw|v5DwaG%O1J?Xw(}z(BD6edK?!R89V8{Ycf7L@j%&|fpP)I zQD~4YQ_jlm7cW>8V2tg4d}O-NpPp-Dy_Ka;ou7x#8rV1HV$o={_V1PC`XtKW;2_S@ zqy!Y+p^6uxCld$+->=5WEG#TXfE)-8IouS{Vi6UCu&(jH8gCAhW5+8g(SFts4sl~+ zXkgy-*jV*2_jeZEQKY@3s`2d>(*bu!T&Xf6IA&u4mc(&W$o}9}+9E?$uJvU;ZcHIg zQCfPCOos9hJoxeV@~10@MMd~L&_rVBl8>B}Y0Q#F*nmN>4IoW_fm5p7pwv7V`F$O; zuOqu&kaRfI)(>6-#hBvz%d$wK<*Td1z$@`b&Z_GLb?ozb3Zm4lch($ literal 0 HcmV?d00001 diff --git a/assets/readme/mobile-dashboard.png b/assets/readme/mobile-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf862c3b2e7ca8f4a4c2b95f105fcd412fbfdb9 GIT binary patch literal 36906 zcmb5V1yo$!)+L$*LLgXh3liMjr3mgWL4vyn2wp(2;O<(uy9O)VEx5b8OJViuyZ67Z z|L)h_W4tjKH7GgztiAS}Yt6mZKB3BrQm7vZKfZbM231B{T=mVHwy)ogZ*b@ z#h(ZJ;;pl)l<1qv@y~~E-bC-nh>NJZFP&t%scU@0?cWEnvBlS_(uArP2nra(;fRPx z#FFEF?IIMoBk`(Qh+B~8TR^hz)Cz#dz1*-5$cJM+l**BfcjwnPOaGQ|-KN)-V#jzq7JV&l+dyp4mfmOf89XabNrb6qe}6|T z>dwL8@!zN_1RxG8X`!gz*tag|9l1hbJ0A&{?CfTFgoN5Ru1mFu>+4kVR@w&o{=Tk&;K4IvEiHV4eU|U|obJ_Nzc04iM_doV=6D6T ztq+8=tZY<0>+1%pcwFl1-J-5|>rv!FY02Nkyo0S~B_7-!M^k5Q<`4~nRGMJ3io02C z_fm@ekeZxabTS>JXNni@=J*_lM58EPZ*9j?75*`MkU1$R+~sKQTDJvT5-A9|p;3>M zy^{Gm@M=e_ESIiMz!m$tj))TIa>#rj}D{p`7qD>{QNo?wYT@{U)hj+F*~0m9hMLxYquGdw(=_Iln}sP&+@R-pGnO z{@f^-YJD(jckT8usN$YNAGMRc+}{$yWTMJ|4a&_q|f$k8wBm zr%PjS;~I011F!Arz9}Cf9CNQ8X#|At&NE+nl<7h$XH}nqF`%gC@7JCxZ4t)*os>)xM?XBs_CZ(6>ATmLC)gCqR$e!6anBoPdOdEJxC9#waR-)Mj;-fP zi7}miL27)L+%PLFZOklw-qR75AC-=%yF@aL`|NNTJ{pgZB^n_Lli} zP}<*23b*M16NIeuZU?2$ES0 zvREC-8?JJXV_*jAxC>)WwQ-G1K_~0JRp~SwZ@lU*NMukQAd0+QNgf;3cz%*)ysN5x zun^=ZecnT+X&W;&!e;L7V0S~KA2T&C{`pGW`Zzy#AdIdD<^ej;bF-oC>pkanNtQ%K z9cE>vdEpFIjp~U9X+oV=Gs?$bYkXm5ebc5XIL`H*w_kJ7$NJFY?r9pCT6Ff3nC{82 zuWxVv=)(T7Gqthxy=2_vv1H*(ivkjBo~;U6|>KX8r^3 zdVjYUy>7USP}?|;Zwg&L{YxLV$CZ+=ZAe$X38>)`=aoS5qr^u7#aB2lwg6(^R~ckb zReSO=>f&qjG_M&H9G2?K+>t_t#b^&`j+ueiU8rYlluzE6PN`MAijtR9On+}^QU*K2FSw~J!A zp00_lFB;7%!W+DiLiNwd$&s$olN052TEM4O%G<}n#3T`P|CNnmx##}PU%>4Y?0jGO zH8mzBB_o5Jo9FRq-S^_FEkbbM%Ggkjf)0J5{Xiw}89xsR)>`Gvu6H4Tn2I2CF~*vWwoDrpQp34$sAd5yM^$s!|u+OX`C1dwaEKs z)TwDjh{dtq7rI+b6ItWVi4KSzcGT^_PhREuNY`6;3~Dz5A=5=lY~g1Yu@uae_6ecc zrdp&Z+$1+3K@ZXCrr5ZTA3s{!(^KzM=+_~r_!h}${=Tr@vRRKI$34BmVPFM06rZ#9 z&2+7_3ErBB*${GNPw(9x&x?G*MkC>$nOqEFcUWtaf$#gd=Da^1x;tA)YqMwEoNrd`_t|u#@HG;^v%s2Psi*46HeS6@!%1TZF<)|%Ma z*&#YSWc7LB(i(@`<}b!)6QQOR77^UChIi^JP#zD6O(467wTv%B$tHpixbeC@i!6kk z3AM$_7^V6Lh;`yc@;i|*f%@c?@%rG+s6wJ9g$fIuNb2qob2~l@b+fXwvlrWB^k=$z zxJZQo3_e8}^%;pXk;0qNi%eIQ$KM0?iJ*6Qi!ik&xMMqG@rrd^#vheKV7fdwCv*cc+df7plqR};(YC{TyjcMwj z#DNrwwxo^IcU`-o>}&j=Wc35cRK+L0;-kuY71~=42Ud%F)$K7E%4c%hU2ai1O>Net zAkmBbn*efh@GK?jP9(k_82QYUnEHW3pUSe_PWMD0oJ1H^{RGxw9%F&LhQ^@W)hm@4_ib8RZ#bq(dE;NHBy8il#CoAgwi5K_2wxsGM zH%@7eWS?P@I1fshS3veoOQH8FrGU9928mwpzif!l)+$!?L2}^I>|zkMO58ZC7tDX! zIuH9?UPcSoMU$F3Z#}E}Mx-(zPE`#>1b00=HQT8dK9vkhy#*Hj%76Gj&Q&^9(v~6~ zHXV>Z4VPcRW_7LaQ~g>yC^Os=euFC8oER`;ZdLy>ti`uzm1qdH!$TssV+J@89|qMg ztIR{;;6`+K^qgE_@J#2hV>mf?*@ z_d03SRnXfAbosO+$XU1Q>dm^!36>4Zb5(Tu zPe|cJDS9anOlRTFcgX^YXN)efxH@830YPDxY+zA#R?hydZPAExCV2gz0t>_52U&8s zPI}e2EwNFNG{pyVNQMm76bI|fAR8+i%XR5K3BIPD#ps9`6Tw4j`9&dXwux{WX5EM-VlBddU*gqV(6<%P)-hOc#NP#uBd!j@wqU z^mZR%%)$IN)6|n}piFkNr?4@-aE$q*bNfemS^9E7F#3CT_Q28-KK0)ZWCBt&*OGH< zMXHP|f|q0Z9W-QE77hGhn5GR34E%sj^4D1DS4apHzd4fXEuujUJ&8eVx`@|(pFx{+ zI_G;WxbkiJG=U*j=q(Q-SVtv-1ybKkai z(r{CdQ{2NNbd!B0EGl}*AT|7R$}7ul`m=WAZd~_r!u#bBU0Im4-5WT=^OD3w z;RUVgcn7MeScT#MJlgvX4pRodrWfPpPVrTsW6I0u-GLadE%1D7HGkogZZr5L)i)Xe z3ueN*guK(aMP<_w8KV+NG%YbGG1zgB@j}@n&K-nE4p3dA5!}myd%9{HLU4YEdZwnP z4n(~~$#|tKp#bSM6#56|0aPKEY=HhEW!THW19s`KKe|#MV#)Y}ES>mFASe5n23HL? z)1uaCz-7Cp5sR=ygCbEMEIAHgck?*S(B>-=XV&PjY%?H`rlEYQ!S<8AKV3%$hMZ|s zf!}v3q_X4W*x-|{^Y7qpn*bw}a*Tly(U)}UuopJ)m5+&7wMu^Vx@?(dl{xV74IVaj zAhx3}@_Anh_>jXqd-Ip(lBKoT?)kZcnOWp62p<#^;#>xkx{3|XKX>AjtGs2W(=Ut` zIH%k&f})w5FGxpr)n@^K&q&N4gRVz#KYUcCx7hWh0Upt%HPl>@B^fR>+u)@&($!Ix zP!IFq_K@9k+008yNx2>Eye>O*2zlMfJN2eLh-bK>EO&cG5>_I^g=;Vx)Z|KkbUR=3 zCI$clFA;#I`}_MQ+D7y;a%P>%2!PE4r7}$qPmf7zYQb!*Hx1|$;_8Kz51*Htg5S=T z=vG00`Te1sWcCaRioE<;)q_Xk^R9;XV(_;D<4%NqX0!7?-5U@krWPLKciN(0)$IVM z>#Mm6U6vw;kDWBlu*Eh3e#F_HCI?GCB3zfw$6QNOc1umvyB|x-%LiUcx%1)<6A5`n zWDJne0aTyvh^uQkx3_KnxvYf}wxakT(c5TDX8fh|)>kKzUndWL;iAV;N82??N0m?? z@6XqZsMII2BGfdf5_%yz4n=BJ}HR;ROwkw2bDR;CbEPEa{Z`q-zLXn8$7`#J5BX(9MboHn|yFh zOCcQebjJxm=g^HTo9|R((}%OqFKV({Mw`~dIE*JeV_WDOuf!o2>Fezs^iao%V4xTgoelFvb8b!{#zS3QsW;nesq72 z=N&>qcs(w+B!)${ucypLSY0#1LO)n>0!(UedS&8aa*A?4#~Piq@G>{tMDFVwLt-J% z(UG#&g`mZT`6`2=w*br9^*S2X(|%M>7dWtH#p$k~Vo+vVqWC{7`6t#UasYZi$4B~VQ~gQ_z9=tz>kRcTX@Bx3k-vR>e!P|cqplp9T)q{hjeL? zADx|kDWkFv#eza6YVKsC7JcjQNDsEXb^^Pb(E5wu{{z^6aqYjrQtvl`5_kJBQj-T$ zf0&sT_?d3IM;eT(!EZee!o-t_*lX~d%?u6iP+U}~X=tXYTWD(pi9db@IPF~RjH}g{U_=xaRd9Ew|Bn9 zW*MlJ49mcvt$_)2En{{;{X@-7L-I$YKOB#R9@jwibcrPkFgpm;S0=*)kEd6mev}^? zW~$crz!>~h)$_=}dg1ZPUO@{a005*nJMDyp^S3Crhme85D_Q#fqPz!`r>aD()EID( zUTl^FHy0DS>w}KZ@kTOtf05k(!0^9#1vB(Nd5^yT-u@w?P^wmz>EA)KfC}hQ1t1@= zQ4^dp#z}v+P?j(B9B)n#4re^P5If1LBj692FjM$U@XkPWPH$Z=G#rhl>+CYjB%RbG z78f_MImUI_`vuZ_Bf#NbMZ)V_H?&pvz-^1k-jO%&htAiqmmw)jsQy)A!gBRS{dWs&o*cV~goV60uYESV1_q|rsD ztKi^_&VvX9Vhqw?Bbr4Z{g*MEOL=9>;&^32}cVO_Wh z4y-DTrHX(FNgAMmYud-WQpX-^IG(PDDLeWXOldAIF8_$q-?$$;?QKthxM@e0KmR z8vFk5I=?*#(7miUQi5gQKF}w^fK9zH%`8WVz?=w@@X>{B zZNm~7>gwu@CNlUKgoR1j07eGQPP|;eV-zHdwxeMfdDSY zGbMv8q=N3!M z`n^&(x9GkRCd%vKzTzUvOeKIpF000thADktdxzVJ%BLe&*0>ZKph7-)h8WMK> z?T{lrmqI-u@;K9SUoQDfp0l7$%uic{KRRb>k&ZqSJ29ot^_z3GzPQf8M0@V@@e!%& z-~L>@Vhb%+EV_y#*~B<*4tDmKIB~X_4uy0cHO2P^Q1gi_F|L5Qv0&@@5;?vA1k|;5 z3-m=TdJU7R;Nbm}X6GT?FO6ek8VmW4`)dUZi+iPM_46mQYX<)Qw83W;W5G(lGt-l4 zfM2P18n^LnV=2^6Boq_w0h#32x6%2v_H-wNE=TD)oc><|)%3=R>=$P`KA@B4iq@ZR zsPC+erfV+?ae|TztForSLN`9J8%5;T&(dAv=nM5ZkoT4?@XwGqArIb zKU#cobI1{BvZfv!7U#C(LLcyZQr#`Msl#EXBJFoA%N;A>lenpA9w z^=zX|hf&Pn<>4Zh0lWDL?}BKdLK+{V*6is;^SB62xi`($JJkheI1Y{LST%kq<{k;Q zJ%@dg%BiQ*t7r?;s;#dPoy5OcW|re!8O4HdPx+p&2?vr(AR)*uM_T>RbafsK?V+ORy;MI!mt>%#(T z;Mod<#mnN6NCT(e2#P^(rxNb3`IRi~M2FwPTHE1OTs9g;aXMRBAB#`F&srncTk|mg<9z0|cFl7P zD;!KyNJ5q6De=F4S5>8liszCU^jLsQPLo_T zYbyDNN0Q@=VzkeZKcCO`AR*LYn84b#jT*~RgMKNmz_f5 zY|y#=@Wle0`r2+}4mJl~4t#ugm?fvqenU9|VdUEPSCDlucD@3$&_ zs$b`Y3MSJUMRmL%>4PTq@l|4*YHOzo(OE+l2QbG%Z9E%dUnLsW+dQWITiwoEB*Ts{ z5)?CK+l;m)h1bswpZ38`r9Lk=hs)7q!X0XYub1Aq%^thMiM%f+zec@ZoM($5+_f}( zKF{v6LRgm*H{JynAcER`C7A)q)UvgYsr)S;~N49 z&ATmwyncnikPk3`8j}4ND?&FOq|C_^tq{;jFil8X)(=t|yb0%J(sirWVs21`(e>d+ zJ37voey*_#H`}#vMSO;E3H95i`TCGlBewajJcM1bFq8o}8qrAsTS0|mJD|U}AVh?J zu#KU)1B&q_FBeRDAd8@t4!*lqmMDb`C_h*ba#C)uh*1AHpAfEU**{zL(wc{->n-2T z(N>H~oPEufnz$D}5Q#w?UO;a1EnLkXiVsF3GG{Bf=7Ka?UK*!U?{@mFr}k$)8}-Y_ z^gnA?>DTi(togHvJl&m|EVQDi^vX6dM%v6%z;%#A#Udm?0;OC)4Q#Dsa02(OQjVOAdtJKfMI zrisU7z6efX&EP&?U;piv>3qhb`~AQ28AH>0leCFHiN!!MCM(uz_gnnnpV86ohzL#t zrFi$7SD_bqxP8(f-yvuF#Ps9Z>>*#D+lKj)WlRc|buJPi&xCiU2?icTVjt{tA3UrB z4PM`3HN*ouer%#^V!h0isEvk?3Dv*^wkm`WN~=f=yXxg|XDGg$4o%#JTzDcJ=6q@I zf4hB9_zP5AOJ+uR1gT+Et+4+3nk=Y^vP*c_F~#?$Lu&+XXZ^{;HSqj3Hq!Qr&k`sN zx%bHuxuktDA$#vVj`rK^!6Tzv>E=o7f2`!chm7?l=Y4HXFt1hC9{9a7Wz-YAEQV=u zzSdrTtn5Q26kzHtJW=TW=XESi$g9ggC~qL5t78z}lQNGUUHf*%;&AOnh=u_f{R_gG zb#-A3I^Fcjkyg8BTsH+pSonxYv~>1zyD-DJK+Pyc#Fji z0(hrszg|W+4_iU(ps$c?yupqH@p)LP2(A8Cc!On!&|gpURnRvhf(6ID69L$3amBur z^d|bNHz;J%UTyXbtvGneT2}DKs?98OTw+R_$9Bt@l<%#tpCBZGsp2nU3GP?tB^ncH zgpi8srB4w=_e^G2%>ttG!ldb>J~y!?hgo+e<&U`6F`}ML!Dt0RU`x;Znv9S44jt27 zlERk@NLVyF+MaT|4p*XT0Ce+bo;=ARVF#I+4!;0|NPM;_KPn%Rq6@5-*H8a*2Eou& z9qaIKIZSn#dIs8&t3YY@MnC6M2C~1HHVFLXlV8Q1N(nXjT9AuM#1lm-yt&fsEHgSD z+*UvEA~^Za&rJ+2hKlT+ulL51+HK7ShWcRMLMVakaM#J?QE}@Ma)eGR0TOr3?W#L! zZ%*ge&GuXKfrK=8I2vQ)`;v95*StRuKFv-*P-q2&4`#ZB5B}CrxD+?qubSWgg;gs$ zu~GIwW7FT`eT!wTgr<0*FeRKo{q zf2Yf^f6*(E@?n5QI~gcDK^j`%LsBX&0{!02LG1Dq{8gljhRroxq%Pz0+~rqKj`x^| zVPS>UiXREvFm0j6SgCkI);E^O(gWv973qC;%MEqUURQgt3X5nk2iuO@(Mj#mP`=wM zEU$;;d^=yeqnRmcjm?&uxBscM)X|rbDc>_R{*zzxNvQ*(=>@@4hyGgIh!$qv(3c4K zJ@M!f)2;izp+`xO@6Qn=4D?6XG`{{T>&qTJ3{;P}C<&%+9*=M-sq`_uRAA++VNJZv+J4T5+mzu&6XQ z*K{e1zc0cfXXVga`C!f&`__D#buYPDKGWPZ9p2G4=VJ>79%8y;FlMtjz8_htIl@Qm z#%^XN8v)Ne8#+6>bn`Pfz@|c!LWnH6>Hd$by<}e!v?3OVwQJ-AZs@h(npmcq9$mra*l|_|dmG!~>RZm=^SlvX`{Pw*(owzr|ZA# zbgtsLzQcFXPox;HP?3a>|NYiC(X_q79*vI}cLtc<9UbC+e}!{DLCNDejO8Zcp>mGv zEAxU3^pCQ@w%X1Nyk6l{eZ}|e_gu+EMo^@ec&;0zlGj69H3E%f41MUH>~*#89TI6~ zsX!d*Ty;%v7~oFzxvc+>72)MWS0d!bMct4ob{2r_M*D(@ z*nDe?po9bu6Rxu=CfZV6-Ql`$ZRzMQuE))1_@iMZ)C+WDy{l*NX3FHwi;2Z`G1CvM z*UeTN2A13{y5je@bM1CfEzV%?)A3saJ<*>!4SRN2!^AuS!e`PO+Tv z-Ro!P?DUxB&dPo&_Dlkm53m6`JjiR*d2sHu)Jo&`1&7FQSn8a z6@$3t_jx>)Ty(sbMx9tzR*}VG=gks$^ z{INX)3Hy&_tHPhWvc1-oD9AJ`In?3KXY?MkCqrV~fcPR3uNbLhqi{!$FH$>SfuZ&; zGSP#_9r)^Pz9U%^IGdG4bt=gPyDaU(u8>0X*mHSLB#moJdU`Fem6Eur>W&xFq(sNs z-JXK3!+hO6^ zX>j^YMaF2k^-F~Rx%GqpPU~M)DgFtSN>i^nDzpkpRtDc1+QI77uDp!U(#+w!VZz(& zd=`E^#!4W0s2YF%2~OVh_+t4UG-#-${l*cpTk;?0IU^!Y1`}K4r;i7(2_(kyiEJ~dh)s03IMH-gpQ5T zv;0j}pOMyy!w%jmb}_-DUXj4ec>|p}Ufc^XIZ+>}fv@}w_`neSXS72>H#1~$O`E!s zwvput{6&5h;E3lKUqmFOn?A7jBV1cn?9oEA@h56nGwW4!HT({#$Sa0s5Ex2vu&vhZ z83TkLlF~wtizb609*bOIP?DZBiLpM;oBwhsKI}F4#&`YWUg>0 zIR|A%dp|;0_P5bz_j!VWX(JTodhm(oe>VZumyYjVzUT{NZ9rs%q+GqejUcW1&@SmF zLXU9Ld_Q@l9)o|pzpMoN#ab+Gj*1E?`9Od^k%!N(o$j3Gd=MWNmHV89MDKz`4usHn%7JaXxtt6HL7HhZxVYRVpzl(QM{JIE0aD$J#gfKteQ z5Lld2#eR=n%Er5?T>Wb^bzu7}sZj$HHZCQ56d-IVMwry;^_! zK}{o?p_F5%{;xOg8}snN6>Qh2=vb1b`W>+tzOUWg-TqY6xaO6siX7_)2gPpqsa#wQ zo?^c?dT?ZMjE6@?cz}CyZ3*z-ANWS|0ZWfqTWY(Oyd0#?t&v zfys0n(ejxcS*w;2jm&DZ+@tz|6(k1F*w$fh3)h6YM@35MDZd92fs2RDZ!47*ijkPq zKb)iTr~j;U#-6?lt@SJx*v9-zmnzz@S1YG(W@d(mvpO;~rl2U}_hB?LGLr29;ll^I zEOd$odU}Qc*VEVJ5$4HlJyt(47i`>*rAT;KIozC_?s5@1W%H`O*YEC-uhsYx&Trm>xXGtl)uh6PWoAk1Lf`=#H0~S9 z2a&QSpE$U_P&2lsGtag$KfqJ@wu>m6jSClJmY{&Z9~-f;KWjguglMh8J!6$Kfslxs znSTjBZnG5`_zcgvOIeI zw-~xlTj5v4Xg-*$@o7jy-zJHfA{5RdJR{z`h~b zC>*ArhAw3jC<%!DvwO04C0MdoUVlj@`@|2ctF}0N;GRc>8X1 zn>BMt{d8)2!jCfqKL!Z(V#7<^exHA?n87e2_ECAtbdqvv@>1Bh`uQd`ZX%A`QT>t^-F zBd!(ujAyl5FLTaWRV#(@AhDERJ6QVBmS2nLA)=yJNL5mt*=mWvFJB;m9TfbfK6Z#( z>Qn}X`6QFVPxnrgD;mnIwZ#WUHonznBW~;7-ztGY+&qo2a~dbk6ED&4Wi$Dl_Zr&- zmvkF#&7Mx`CTfISj<9v{Sq(QpP$>OF9fgZw|cM1X-$-9g8%MM=R|N=m}N>V^Do#V5{0@=KGMs^qP`EC!7}kQ^JVK_ulh2v zDRi3T(Z-3-T&8pM^(<$Y<4@0)s*NaWJiq?(x<3?>IBeVIwGjvUbe+#E$^M@=eO zxnAv8`=(j1)#v$gpsM|jP;TQO72y4QC!DTC>gQJPQ|^_^@mxFamEAfx6W*DTtZ8>^ z(1LD%aXf=B{IpOZeK9xN%+O-7YA2kcL@J?#nV$4FjuZelEWUHWtVRNmF23j=A6?(u za?+nRLouw5ml&;09|ZhvmpW=bmSrboGO*TDFD0rNUnMhbe%^k+7Jx1!o(?2L#%0{7 zZFN0OAJO;Lxav!oq0lL`ZJu9u?{Po9o%aU7{7do}r~Tg?+(t6_OHt!0DDeLT?Eky3 z|4-`%{5*-vq}#&y9-FS>`>qZlogv-d;lwhnrpAXLJW7zXjksM6#jKN@#lCU{=j~C0 z7md7OI{3hQ0G(Zxaon#;ZCPWK*bb?t||sAa_z6U*=^Wi{Q`E_vl=f4 zAC69);=g~!q1UXvK}ZU*oXQ`HW0A~Hnivk~xEf}FbWW*!5;>uzHY^V@82ovg(sZcVK7|Lr507 z@N*3LUk>w8`6P#{1iD1}Z_RlQ2Bv)~o>>NM3v;Q-OyBgc`hvD1Xj8EIf^QfyTUSiE zhHm}DVED$^`2XVDbn^PTH_WT_aAd&ZN*MYg_ViJdxi_obzQB=>On_LadVopD>Pbvf zliMKb!Ach!oVKaX5)OQ~X%OQ-qFj@0L`2S|Oj{Hu?tEh4YM9V)Xn^SS#f&Dcp4+Dzn@da)q<}z@5YFVY<&fhmW&@p;Q%9i_!yMTHSTMn-8kaGlPh@61O-u5A- zfomSk^dHCg1JGwSxif_?9uav0u!i*Cck(UwfBGg=&{YAi=!4<9MT&a&qR@x!XrvZi zd}&V@ElExVe{8(Tob+YKl>!$(T8wANd?A&}hWbKpt1{Tl^okU|jkf-+^bQV>IftFN zYq*0$2Uxx|9M9w!89BSlq(f$18=I(nqTD*K78zO{x2b#Vvy=d;>C<9381wOM%YQDn zo3+baGq*YqrXF8M+8^idW;xl&b&Bginl@d zjy8wf>}E@SUz`Z{^Z&6{bTh7)74MW0FVdkv7C|F8u&oW z2vw$%&uhpjw zOeN*f)`h07U!Trt+V<;S-zy18JoPu8hS8C3nu+sHajzs!o?i4EPII~=+O367Q3JPO zWqAAG^A6}5PR$1Odysp&mOSKy5}la8H`!mUNTF_=2K3!9GE=}!7W{Kp?vC*zDd2$d z8~EL6Rmba+La;p2*%R`856<5(dI+#1N##<%JKPb8nZr{q&-K&F*?w^_4d};$_!(nh)4GrDb;iz9 z`i4TOcNx3I*~(Zt&$Q2Ds>QRQhC?h64;^D4mW4MJ-Z`8Ta#^43%RENxV8to*MsE#U ztS=)uif~?szKH>fUWGoulKT$UrPviL8*K&eU=h%-Uu9&xUtIJ7IAg>C1FIt2Y_Rhe zimHEbjsU@rM+{5KDH<)uTm{d-J#tpa_jIaI*oi}8ep~#@lod+rI0=;Uw47k3D+t6^ zqVviqF|^^wW*y^U3YU%Rh-Ss$oE-X(#X9u4y&nlzA%_jUuPr-1n={5x_Oo z$iUH;Iry5d23JVlv*ho-{>Lns?dU@^l%NpOI3;NShA<^421xFcPqls!x*E%nQJy`4k8`7#B-dTHT(0!iw35*Nz(Be?E@O65Pr=2Qh{h}m7Rqi~ExY znd9k`=HNLwlzE_@-O;q4Dv086-@x;5w{o>5)}qON)l=L!9XLtx#^cfhH@}Z-vV>b~ zYHw%zfiI~mwi1Y-X6VS=njcpHeD5JR6f-*J_WH8DQ@1ksoW1(%EgapmDA?hC!9Ui` zZmB*se;^;DC@W$YL4x-VdXN|WC-jf-%MBJPyXSTI;Ds$+!Xmf@*xW}!PKwGgyAw{* z_4+0w=-EgMy!>DHO9C?DbvM#-OAlRf+4zX*>pN`8k3}2!z9gnbd+b8`!kKh=;&Dq3 zs>(E1Re|g|P7eM+!`TY^krCYvBe+u~;ng9khrN~Wa#G_7{kF2XKgzhT4R%fL3d~1@ zw)<0I(`2QK!}eXc*vvpka#(%*LL*ayI#g7pBb(x(NgoitEg|?B@(k3!q-2GNQ~xjc z{NDos1_=jo8mKZJnUUnwhYi)@HVp%M7EMiJla7E=xPflZKSP_UD+U@99c&yp%?BV=1UA{6Y?( z>(j5(bA>}cSFVvx&f2JHw>zxy1bAXBl3z51d{25bg=if*!%lj*;o&|)T0;H+mvAn{ z|Ib80{0^AndxYEsPKfWw2?&>fCY0Sr3KF8_2Uk$2h}s9?A=09xnGj}XqIOVK)P)Y9 zTRJo+S0s1aiThfw(e}m%?7uZUC`E5)8YWtQxH}?wzRy5g83CMWm5p(DxDa45;`SU$ zUd*Q=MP^6Ti4qmT7X}p#azX-O43+*jPo)80AmY#e+j#R&Q0m;#&B&~yr76m90MRM7ci}XMGmop9}6FhT4&`1H_OPp|a!VBYR zgqeAG>{m=nepJz`Ou3rd*sx~-ow_WZ?vrUfxE9k7&u5yE<@nJ^1Q)W6dSL0?ut_1E zyKc3?<{L~sEsm%2fYS4qsI1&)k4uS#Tq9yUg$)3g_d^zoov|!widna~s8A1~t4Ms6 zPK728yD*B4j9bC0!92>h76h?EM~{+`OyuQd7EjQ{t#kxiEgXB}hW2`QB84PmBBdw+j<-jO=q*jVv z@+vB}xbjIumE+W;j%o^N++{kq{98#yN+6N_mwP4S@Lm zYDQz-uroXV7hlk{*o!xKONB|dfgJb~Df9Q#ugIMgulAXP6S?Yw+D**CK6lqYZ0M2P*J6BKlJd?G{oJDjWi@6o|!uC~r z_dQCU$OC5`^*{}BivVIY`Ki!LxsKCS>K`EG)j!_Rxnf($lZp;it@45U|3#K_Rpl1( zApu!4h=)CS&#sN{Ef;Ab)A4?DX#Ea_PYN&(9S0{i<_7qr6{Sm9^5vMe`RM*!_-n#5 z)u;ygSQWT`0#64<)c!x*j)SHlzAg}_C776^f}{>5%CXWtEueoH2tYvpGR^**nEz*p z{P;i=Y1Mjmjz9v{oSEbA5e)~rBN>P2b;>a+Rjx1JggzL3FurVtJa6^o_kXK=%x;#3 zRF8EZApK~14P?67R2CK1-e1jm=%QR={^rdoAym>okzAJbdCNA{JCRxNXBYzrr{C~2 zh)G8bV6!sWbPm}uA#f0do~^cK0*9K9^ujy|o%hDg%#@ufVZkyu4wz$dg_EpNrWs2F zhHWF7PwZjv?+Z)(Sx5&O(KuI~>8SH**OS3=nuP^}K!%40uk!{mx^LbGrZYu)7`Xpk zI{eQR5Z}1c9@ZtJ?wi=BV^;>do(jZZ#pT!8BDoqL?_Xo5Qj@+vhN*&^jcl&W^Q|nF*9X7 zp8^e5FF^A=c5`i-r~IbZi9o-hfl{<2ZV{uc|bCkvrW->gR#Gsz5!#`Q*t z8k^Q!iFB<$i|>j_*>Du$VrSpedRBjoERO3;7)@7cU0F{`=7FV2SHAVCxlz1)pyrEz z?EgdDTSmqCH2b0?!2$$#2^!oXIDDg(Ah2R_B!lKKNAc35Cq z`~N4_{p;HNi+D;otX=8zfs!@c0)PegAJ=o3^r7nNwqNpajF(Oj8UZ2|nfju)S)Npu zucy`UgXGjtIY-F4jAu5a@Zn)Ja!m7nYV@b@#dh$%Tt4ku;5#G~RGB2aZE$aIC~9tT zHZjEHRxl0_{UBE*7P%w^{YE4aESqHIp0)f_1N@J%oAx=G@brKIGE-n zDbpt;AXAs2T*-hN|HN|t2x6?@E;2$O@G9%NYWgv#j`w+)_?>Q=-Z11-CWXI7{BDNNiB@PzLbygvp~CY4sqTaq9zv>l{F0V143`> zRV?#h8e9Ge zP0vUpI<*+U$T&fh-2F;Nh(X)lLX!8oC2n~=)Lx5c>8+A}pN;C;z6_QDF;6Au(WhaV zu3XckOBmNq8<;5^lFVPP%$-s<%k&e6Z5W#WxCQ=I%8J0;zpfw5S6PswbX&pOq4-y2 z54M|5UYGxLrP3Diec$WERN#7xht+c-j>ft59W%tj$Uq}cpGN;jda|mRv&?}AIm+{)L) z!$6V)4>yo?Ej_!ba;XU>4%_ItTE(W{d?m1)5uj2PkP7x07&CpslVk0BxJz<`+z~|>}1%lk@rlHP( zqaCXT_x?}U1rB2*73a_EgSW;@QOAE`BL)&>>Rp~^^ESwlnfi}@mnCoj4DD-h^0xb_V{vjx!IintpoT z+)vE*D(93|l3ULTtelbfj0Dq&QZBd+;#h##eT8$p4 zvMPmItU}O43p9==PRnNEmP=xyUxAji!s5I1hPJw)0*oE}3$lo1%$ zmyy+C$qIS7pNzTIwEZsE63{o3g;!;!_3^Tl!wOQ;J@-sJwX})(#X3W|{dD+ z^DD>;J0NUm=yo%hy4cwQY20|lD?pHXqtZj9Tvv|0W$LGp{#-iL(c|79Ekwi(QOp2l zvisT;poEw6psyMVJiu-8di>)bIl@8cquh>IBIh_N0_SaV362v{dRNu$RT)ihb=`qa9MiCd1!u~ z)}qt+*G5O$I#eg~lUmWhAytMw!RoBbn7pnlb>y#bipV0LTEAaNZJ4)4V%krAk9A#q ziuGC{A@zbt0JN>=iHyDTqlZcU8Vo2~*-Zpn3EpcC2lME0srl{MbkxcbXWY{fT$r-H z;!AJ6ZoR>$Tg|8$znoa^mt#B@AS=HT!o~T7O&UNPuBo%i4;UBjW@~#3vNUa1&<~bM zdKeZEA3L^&mr+xnHvd2dKA{blJ)z8r$w^;i3vgw))zn3b(G_nOc*G|vYUL3!kr#i{ z>t{koQ&W`#+&d%nkhV?9Kk`n}f3`kY_w(A$QPBSqK<{!>TMfgR1rP&~j(GqZ*TRmADx|p#RMx?`f+qh#|j5J!D|!l^&y?(djKpO z(2Y+*A-#UR!YP&SaDx*C`#9{M-Zk`9?3U*QXamhZzI_QN&EWLt+zuoeq#1-uSN_(b zYIQkD5y6FBNVxtvgKa(lY1Il4?sq8RRy8{DjNV1-md9HfGqtlZU6=b?X9AnUklmT= z+eAD5E$zAw&S(i**LTFhdxQzg*#nZhsJPW-J$C00RDSnws;sJ-rOXj~{n+6%l)q*t zs=#Gn^gX8)eg=y>jtvR*%9NE5Ep@~ElQB9x#I7~%kbLMC64G9B-haLx z&0JsxT<2rwP5X(5A)PZ>T2Lk@=q}D@PV_i%xKfNK(3bM23IZLGqP6Uu#g5<)RbT^D<{Q9H}t{-of6Sh zn@=s$u@CTS(;0aPUs7oelD|CL{m@|XIcnVRQ?w&Q%GM!zVknu<4)9}QXc>ctyzkHy zgp?Jh!+8&RApB8Fy$=1ieh**stsgP&Ci_{+Ph$20WU1K4yW#4pgMOo+QB-uJWm(CF zn~mObYH`n9vwN$yk&t$D zeXlnueBRL)c%1>s_C5SnQCLw*joFE>Az>bJack+lMghUSem~Db{Ar~80tU}U!mQX>L1ydsfg1iL?PskCPwvVQ&TH>MH z5&QzJh_McfGnZgpPc~K|e7|4(H3c)4jw#oJ_9ZXIZ`Gql4CR-W=lgkwFM}rxTBQ7? z=$;g8f!DU{vo6fV98`ib^4ukqfZ1FNG4LGR4fF3$hN&`*7*d`aek=;Hjadkz$rY|u zjWC?FhAKE)R0TmtpWen*8_qdwU^GxPfg)J@ujmn5OKbw{F*dY-!i%r_}1zZcyK#&<<97vWNy8&lq-!JWEa849BN0L(3s@?_=;Eyg;kuGn{$cX+`iV z3|Fx@M9VD7diNtoh#s+T^ItNH_N!4)JKm;xS&)Sbh!AV7**Ee@TA4V2$xo6KSsxmP zDV>AQJIk#aE|w}&DQA$8N)o%{3(x5LnL~1F%hp*+P5{Fd1qD=>&kF=>9(#OLRI{_A zJ{OO-FwcSlCnKZnK-PsBu1YiVg=n0}|npJs3( zXSp~p>^EPYdwLX3PEPz^9xUM*J2Lm)(#`k3#&prrR{Yb+B=bFx=87fzHpXfklEPTocsj|EljKE=xAOnXj2*+uL`uDteG!BD^O9R zY|zOagn+G;g@yUGwRPnJBCn!GMwp9QWkmU3zkV~fFi%TKBNy>+|5JGei%XP&VNQMh zN`o3Fn&$&K{)<8d^Eko9)zH@F{mk3s>A~|85VI6eQ*-gWP~ogq)%oIQIbK#fuF7L* zYHCYBn^H#@{z3H@3n#MUgMpIh8}mPJ)fGNZa4KWhSXt)SgOJuqR)#7y8*-$U)!Zj+ ztx2BTv-Fa0<~_f?r4&Kv$AQ!?SAV6p1-Z(?0wXDBrkDGdCu~P%cpCCecM-FtT7KuJ zWlhSs$2Qw7|JY$?QaNWERr#o*R^ez3{Q)+;1~A!@5EeBy;t!3bvc29db7W_xr;G0R zUS85ad5xvQ&|0?FE-#%mMCHEu2xLKw4XzN7zP`VQi~&fWs5Yy`ZNl>5ci zLHr^0&qlVky4Kc2KH#-@NJ?s&2=ETgMRjVAPPxw2wI$H%`BD9Yat;7!&n*2ri^uVB zu-8^tIL&~B&;KY^4I-B2C7k3vp|7vMzJ-&>3C?X^UO)|HhDAKvf$9wd{`{#o9VJ_` zGbvognifRq^6v`X3d#AaCh=9jsi4;_MiH~(qdm~s>sr%+;!56>Ex;e;uZZNaCC~3Z z)0r!!5#0jQz39UX6trkP-t%5$0{g$(_m#fiSWB?P<8oyxcCAxmOH)kuDQcZ3-gEx?K=ez~@_V zR)?tpfi~~gh_$uq59q1d8nW%c_*eu4HnF!}r+4=JS%iedl#AozdP>|95)#8*m{Gh9 zDrLAt>ljTA&P+AuUT@w6Ue!`l594$I3HCbg&B;ZaP0Vf(eN#^XUtPV-LUaK>dx-w0 z!!wv9xVU}=jriJ0Q3Fss6P=>lw676HextGEZN5I~k6v$@v2+dZAc2<4BTcY~@2&{vq ztqaCz3i&7)#t%OQdP}XXOpEeP7cDTjJX751M7Bc4qB9{$Q1}E`vA*PT$7~xG7zjr5IuZEsK@@zfE!jU(bs1;@u*&^f-GG7sku&6`4_hqGf%tc zD_{wDzt-t;%cvXV<_?Yhxz=>90w1xnk?U__>%X+dlvXTQxfPjV$MdHD{-*kPpFN_V z1WAu-waT?PIKl~cbf=@#PXMfo&0jV}_ZOwthHX?>2uqM{|A?G}T&*kM&3bq)OJ4p{ z1z4hBNZxFRDsBYH^ITD}R zI*i(rp0oY8z$@F$e5rs@)uwT&t?uE~MTfwo`lNc)OWM2-ES2`~Z}X*F*U)RE^3W-=$#)sPypAllPN-I~ik`FbfvD}Ub>kX@uPOlHtB z!eh1v4J>R6YT|)#)b3Y4+#_Cz@Ixqdca9VmtftE|j{x6gaHfZ7WMX+8HqG`2;EeN~ z5=Dc&kOu}$E@$>lTfpzCsvO28HsyB7F}vY8PncoGZVnX_fd1nHz<#e_%*wl-pq?#LD6vRYwJY z@%32!G;7A$fSpXeh!+j(nUua_C6qJn&$t%qAJbC zc0!omIiQ8}QL|SKk(9uz#dfMVZZRH;90XCUPGkMUY3Y{+-cfgeZm5sO3VDaUqTWLi_W_HVg z6WTS_m_0gs4cPdye*O$L zWs3+|m%*$LJ-(go45yHGBkHsqb~d93zaCsnfar59Aktil0GVkr(OaWAx|L;VT0-KGr#D zB&41qJbFcyrYzVwA8zZhO_or|lJop>ODkr7g@kBgPH_F64T!$IQg&)LJyJ?G?C`^90vq>mH zU@*#dW6}1K13#q8Q6|PGYSkGU(`6nPmzKhwPghV)ptECX)uOS>ysmR46ZAW+ac{D? zc69$C1&_`4G^*Qrya>sgG8(V-hFCUCpuZV@mQ@Kr$~U!W+P)u9L4>n!hneciLT%+`|Ib`!8bj%*@8#Z*IJqr#VMdm-CDipdD)L2Evo`^9=Tar zzP`R;(hdLbA15S~IUCx6A~Rgbe|sL!7Q>d94@SRbtAg5{f!f;I zsHv!&+q*|v{``q)N!Fk>-6*}Gg7uiL-!8|t0rO$CSwAAj)nVSQ9Z z;4-E+5jqUs)U&g~HX)2o9cHmTlNuO+$=7^2B3qPq{mm*!sR#g`(erPz{#J%@AR=#r(>1>M0Z28j|v2ZW5qAc zn3Xi_3V3oZX0f{0U+FB zDXtUwcZKc$?%QF>&CDL1scF}XIj4!!g}42sgHY9X$}Hs*No{BH@5cYM9BI8gIt8cOik-U<<$6OiHwZnIe;uCUGQZNX_cufF#!%b4^mP_UEycY-R?JZ7SM7}*2@#0HVIzLOf zdT0;w2?j%j0jxyUr3`7_E1;u%N|sUJg3X^7*A_>YE618V)@lvLp5_}p)-=Tu;@0Yq z@V~8$?tpL;at#a>PhmhFpPZTjljkpj}n!oKzti08Q^kRFi1gvLMEK{O2z9*>v~87 z;XSgLG=6!Zb_fM-?FgiNJX;&BA?4+YUO>X|jSeF=tbtZy&6HU+ z3RIG6&azB)nGRb=t{0Hh@#q9{ZF$MfZjz-jDbbbUEpLJ_MW7CtINR8)DxyxPwpH#; ztnqN!*Z;bpGx&oIZO-y{X<=bz%{*R5!sO4r^&z8kVmiZ`NYD5=Va7>w%8UL14UIOb z%#LU64s7$F!I&~@NcBg>!e^QEPeTKzwl5%n<2uk19~;o0bdOX0?FVq5sd&!%v+giW z`GU>K=LiLh7lU)%eKqIDxG@0mbEI@(Orgw35phSOlg;vR(QM~+pA6Rq6Lv-~;PtR1 zQ?~YNH2BwAWvghs*;om|I6>w7bHa1_)KSHP4J_0H{%x24YYVL`5ov)y7)9s=O6c3R zX5pMc7jjlIv#^h(A5+H;K2%MQ*!hK5ow4S&4a)l ziUe6B(J~Daf^!Dnqxw}znB~Ay)i7@%1%>CA^0XEK9`k%?=wtP%G&`7hEy#YiU{Ujv z#|%wWYn;CJZ~=DBhc|7Nfg$lQQhV+Rj8phgMA_F3f9QgLIG!QMwZy-b?FxQ&z#)&R zb4ZjgR#txH&YH;Sl0G_dy%7`TCjPx`&cJ-kmFh>9pU|mikP~n<55L>d24>EhFbOuV z)C!J&rA*k)?>*K?Pz-^O1r|wN;mb>vz`s>gFVko*tr>yGEU6zJ`})J*-=HyFtK^G>8mF?1b-JT z>iAfxg<56B_sp1hp-{67xtKBSn3ygqrsJldVi#tOlon4c_JoNp(D+HY#r*{;MxW4j zH3!7<*04Oq#Zz}dl&m4TyFjZt?QW)0_ghK(!lU-0)Yf13(gwza6D4}AkeI>QSyVxI zJ2|;!KKdiz^C8Ql82|avnLPn?kP!Z@0B}%G!Rxk_)iInQ&~Z*B4XqSaiZdUZ;x1uc z&)}trAZ>h1_sde$i-Wu7V@%AKBJE%Zw z8_xoyP~>-g5kOk5T0#vELG2Hl=3AyNuYJ(>@9rp#noe?h-567dziK3+;L!RyXIqD^R(hLayFUdVuosrdA1hn= zHdGm#B&s@FH!aFVhyvEQrEW55eHR^%?bol^t5}Q9!Sq&qrXF)eTB? zPW6;b{N9-zg?5K1)bp0 z-W0p0ZtUlMKzvt~VEswhmZ!eDol95(8WlX{%jp+V$iWL?*E8kB{ek2i2@7&ek{Hc( zpo z&+OB}f+9MEaQ!}jCd0JgmC|czn1jnx?l|@!`)uU?=ZAGKePWB;BEk${qKVm^Tx7xu z9fS0LS)m$O4-+fmQe--#H_|`0jLHLe)zkn#HSbg@PnH5h<>l#o$r&T=puloM)s<{W zzK@kxxgzDk%&ADl!g-4PMs=scsN9{q0GXB&+fNg!EMWJXZQ&lzBh3LFH2te>xz;;+ zxn#idnMcO2KqAw!s1bPS-E5x_B>T_1E$b`QSROt7VG=L~tRg9tbJuM|-L>Hc@cz>>?L5FT{b_JDzTiFUWwvR1N_ACbhh^X0 zT`O+m%I3?cfg!`Q`T4p*W^^ClCi!fS(EbV*i76xBM1~RpefmfP>IAAgYeHFgLs`wv8;oTn{|nB|2y{oD_3aifug$*eCxbh` z&7&dIt)EWS8!=H^8ymM6$T#EU#Z!JB$twgnuJ0T0aPeVj8aOC9YTMg;vAoY+TvUX< z_}>4jyGl}f7?ryFfcCTt(5k?T)YQ<3B7wAhDCjC>&XeRbFzEFBs+J%PgZ8uOn}q{p6b`&p6;Lgl$!b9 zTf83_gLvA0Qb~ySnSorZ#4MaI-vv*X#~O&jj9#56!9_V}b(#j@#f9ThdxL;&&x{rK2m=FOc~B5ICntQE9=-y0 zaS+}QKcGSma2NP_ z;p*ZVBVA|&3vX_8Gd3^)u))dPov8=2Zwe3F3QBwV==FxG!6&O91>SmDyt^N_JxeFy zZFic$^DWc(7zFI-=(xSTMT;{+KhTio37S50_GxuPZ&)}Dm@eSFi?~DVsl(OO%zYzf z2m0KDRYG~x{osaxv=axE|;J*hF#Xx0cjs zU&c+IpG8QrG9^Ral;`i*y4~$wRZe<5)~sO#njYDQe@GIq&T4 z?fEC`$Acdj7yie9|N0$;igA!l#cIZfPgY)4pgliU@hfaIp@hB_&s|c!;jWrY1+IhxMiR@^ z@fhhnqp7y$VTnW4Ye9+DqtDR!Z>x0B-hPk7r*a3!rBnBU_zQF2<)1ft6B~0N)awyh zi(DEQM~5-=#1%TZ|K#O==Jhge?q-9o4-tNJ!R(gw4C|o6O;UtC`rAoD3PrvvOH=Cc z(i%s;ENCvx9~$rEiBNtCIg730=A@B35{zB?t;=S8;$A0t)iG})`miG7+4F8aes6!5`cdi!B*?SR?EBj%MM z;`ld9`2l-}hg)10TieqT;I;;hlDXxZo1udgywTCHhZR|y4p352{D0#FreE>w`dajR zZ0W&!v_Z)CrGPr=!?m}Z4ThN6*8Dt@+Bth$Sx-vt_NBNE-GHzLR*_xUS=dL@Byqwo1*Z(h{l}`df9X-;M8(GJbD57XBP? z{FF43l9D137Pk+Ex;TJucTE^mBBf11>y-~hp_uqCTXJncx0{(s7Yx^fxCl7>4zTXG zZzU_*PG6`p?Cfo+snca?R7ggwXx~g53VpPMOVM2Lsww`H!V!ui#7&oM@>zjIcaS_b6w$^s8;QG4Ro%fl%5wjN?Y+C=I0IB`) z2!yb(UVf8?87`WyJqVKgjOuD?M6*5)IoE~FJ-mUaMp~rF^NJ9nU@ewr<*x0w>ai|* z!nkj9+$xu)PYr^_k8Li&%k{eqK0wJHF4p?d^}{}Z@)NwATATU?J>8iB`Ir`U+Tg-i zh>btsbvz~_qICa)@QD)ss`J@PH7VfSt<3aKJYm_g}{WG(b4kdgRx_JZVON|#NcMO*ii6%OIZP$nj&XM`!;hV zJ6kWds94qBo@_&en3#BFfCH8qv2b+&xcQWYFYX6`a1de(+{B$O9S2Fj``~J*u8wm{ zF}s6dedPuJewGKBW9C=dwD;3MOE@i;tKjjq8@i9Fc~PT>{ZnEw2enL$#bA|qxRB}< zsMlm+VJKri9rpM2{;&&ijT;jS6pwRh<#ClfEX}QFDed2S0|{<%^$JE`d0Kw8IIi9h zDOP>0O@!TPWmZ68WDyjk3eC9n4)aMGh5ezsk4sLIG7Nl|TZ zS(OBGa47Mfx1)cvoK6Uyl+^GGPZ`h&Q6j!6h&E`4hiGbQ!on$1b-&@0jM4Cls&7LK zk{v!m1O$)m8q` z{$ExK?Ew6lQYliet|G4@wapx+h&f!6c|{Dx!(YU<+8kkd1MkrGFC6iQD z%Yz-@ub&5{Rm#jx`T0e)g_@rH1isP*fYm2S+?dQ*VN(6WWW z<|Hid#F|uur1?_S)03uQ)gd!2EZ@ZS?T-)d-$#7<9GD50 zcVvG7Ho~ISzj^$7CD#rpLQ=eSQ!zyeDp8n~Tu%1Gk$ELzxp@`w_}#t|_U^*%TY3NL zNBGwL2d_(MNN;sLOl{-I^Z0mJ+dUW4Ws8Y^8k2~s8+eEnS=U`Z%p_$t+?)N`ebsGO zJX|I-S8>HnPSuj%0>a*43NahVO5O;5K@ProtfP^Jt@R{jf6+qbbjh2?v@*gu{d zOJma|wo;}{sn5^9-02I`u5PS(;J`$Egaib@Kylq(p1c#|Ga$%a3rmJDOb>Sm-iMFb z{PZ+jG$DcObds3Oamzb`o?syu{IZr=kpg>cboR!a`>@)eIu@3?UA^|Y=YZ~4}Px^3Ydeh-OpBs>mw*4APPA$VCSt1!(bg|2xyS(4OwAYUSS zseeF6PtY_L9ub!0rp@aTn_zTg#Qb_Vp-_^$sj-n$x#9y=Y#HzA$s}Uy`S463_io;R zvg>sUEYt3IHlAEs^xzNl@GPCF28sV#Q*$A@k{>ZEq%kW+4_$E2G$jSi3fxC1!){UC zavl>r0bKMvyVyA=Qg_cZcT&%TD&_J^Y*7tXrF?oPe#CJcLsI$mck5%bJ~5HA&;elw z-``G;E3*&cl4*Pi0;xPHcC$+Ry`fREIK?@Fp0Oc51{eJ4W`2|I6VZGG2dXct`Ypr$ zv2tI&sBh3oia3+2EatzLR8haiv1j#nbl`DqX^ji|3qwKCf&i5uEuIELFi--R#meeL zdLS&{i99C6i71q2f~IB7vC%I7^@n>=(UYXNTkpPPrXnZ!?_bf-8==S35uA@J#L?5B znAW(`Lk{ayGxyv!CWwfbudm<&tNwBVTUshK1lb2BM1-XMTU_{m=GV&2aW!z_hWDSg zuC7r0?Ri{%7h&g>Suy+6`P(Fd?TM+Mm{eZJrjP;Vr~!w4t%MJF_<(i)zT9~cY?O4m z4^pr_al;GwL_<@+lXe7}&B-W&V`gD7ou-D=;&QM^YG8FE;l|PQs0iKN3?ibVP!>5_ zk8`S(E`#uB6|?*6ow%5lUho3HOZK9nqFSbv<~<%(cW1qR4gHG&mSFNi>A77DYX5vb zJmS5yiGWIwKqd8?VJJStCJ8s|o|peqaZyonh-#vIMmC3QY6o!d3xxrurLo#rLaaf1 zNVcj&77WsmKlF=QIW|s`Y$sJ$++GG{`e`MYdTw60Gt@-&2;1|khQ@dY)`t8Yxvxwt z@z0!u9-0-c{@>sDqgh*OVVpeikL(1M7!xDos@7iBuLS-x)E7j{>G9n%Ez0+S0N30B!NPY%(@YiS=9br=CMYn4#5wML8YxmaP@73bMRo7Y zm5C`#Tt07~g7@^YnkMj=v|ET57vV#C3p*Bo^FG~zfTKh^;(Ic?D*jp&D1jHxXYa`?0=z?9=yq^r**bk}9 z9F|`*d6fPlA2R}jge%-MbIXsY$X0W&126>+pHyzdWwv?YnddeSni-1JGIL$?xxB%j z+gV;0%6)kS64%S_2Crm4bf(AE+Lz*+npjx|07g1htb!P$9fTBVM>;L145x=VXU28C zb&+WeR>%8^2v{1j#8$qg@Q#vl&6E_F6~Ww1Y+i&g^PaGF&{6HW{u`hB(d{Atq7my&P0xdS_@}Nd z>7)?#G3w*&4ImH^t%?Qmp`jMNNjuR^0$W?QfR`8RrnLhX{*HRnySI?wOxit~$1sW-pjh z0b8@;zfFC82?;ZHcc?&FW}$l@Clz|!X3m_i+oKB$r4UPt#zq6I7021*(HX4jNprXF zspl!9o122HO2w)EwXU?nU!;r)UpOLZgJ$@?NB@wMCch926WQ=a@m^ER7+Fl8Y*3C29TqGMo|r877KeEbEv-!bLiIfwQxVH(?hI20`M_wd!O z@^|(y@NeG@d3(QDL+!%U7P&$0yTI?N@BQ=N^cYR2SJaXc2UOaW%DN|f+q{RyNNIhx|Pt!0D#aqcI*j4}6 zZE;~~ijCQ*ySIRZnz}GKDSQ3&R=QBN{%&j$zYy1y+>_`-y!cgSZ6~-h|Dn%#NsW#P{J! z&jEFc>>GtWVynki4C>E6-Ix-;rKaiIc^#a5w(8vA{4D&uMU}~aQ9Ks59yO2e@*GYu zF6f!Vu5I#$pYmA;!I-O>j;{Wimr=5+C5~o>H!8{sEUTwws(vj3mex2KyS$VLV`Qp8 zK36crHj(d6evd>$5T(Lg>iT}iLhp)JNr@4)lnh-Ufj}WrLB3^dBUTA_a3_J-x`f~g zm6q1Pc{?LR$4=ombEh+yqpIJmHlM-ky`26xA3^@lnwt8i(qp4cvg;m@wWK65Mj)j5=}7<&Fn)~3UIDEx6dgV_uqxOI$Ke~k5#kS0Q_!Upt1-@cI(hdA2V<4Q{0yx&Gb7z~;1| zW9|w#I?l8zxHWEaiXmN4l02PYuDEZvnI{U4+(lb01<$2trsTE^sTb}dc zdUY7Nbv#C_(5yuBB>31mM4=J4ItH#1ErSt*&u|qX5$7Q>iN-3ebbNf=Vgn;EsQ_LH zJVuOApsIB7w-#^;h>(Lc$1UbZm+At+>bU+n0Cj0h!PdAsdIZ<75sKnj6|klYSi(<3 z(S1XEtuyzTO^xIuAkAX<#)|&ubL!m;cYkTyNi0^#z&bsgvyOl>@$X%+#;29>_ZCyd zv=i%|F!ls;;fU^ORW@yq&{9wn-3xcyBSh1%0oXp|jZ+4mZ} z-HKB{UZT9 z2p6Hqwdn1eS->;{q-tgRrz?mQZ1{gW!~X)XHVCjbmWQpPW#^L}L2^H5&k~wVE z=UjaoX@*1r*DFUGh}qftFKy4~k8{dr-Ux1n^(zMY=Apke8AVD;cnI*l+FP2J57B~7Gq;YNC^FdTat8Ss((TNBjxGD?_BAM#Axq3J_cpa5rNN3)$?;cI)!8q z2`EO#_Oj0}5fBl6hdYIJY8V-GTx{7?EFc7Qv=5vJbUoD!uZ!D}%JhEbM}0|(dm4~2 zXx+zzvWna>Ndl*k1$;#XiQsTh1`T0$Od3ZuyU97xyUz_^)3sHaA8($d!oncWXZ9mU zf-WN#+5|Vl@fle^KiuB+UPZ$|u#r~6Ku~S#k6tebg96Z8x-fa;Re^YS zi_ru6uq}wS^`fL8OH2~CBM{k_n_KX| zpP#yuy9KpK!CC3QSp&6j!C5h^B8gJ}%VTMqmNU2|6(e5kpp)wNNd1Gf_DCzRGW2d` zCMPTK{|aXZnE26{Vg3CjXJyXX>vRW}3^Vbqy34LtdgxxT|6^t$Up4D1>7x1QQ}*E9 z;Q!q*_h>4StI8r*pC>kapGRCXdwT+n@tMJS<9oexV9uVm6tr&(OBT$g$;*~%C;Gy@ z{-lg$&YJ5C&lfx&(gWj}SF`!ex|Y-ZbGdS@>8baL;%vIIuhEqCk)?XxCS}kH^2<>+ zmrwSE?fGp1?b{~Kc_WR|4Nc{)JWEvTE34J>gyrn5;iUEZb=DjC$2zW&)JV)|jK{x} zZu?i$(BTeMYRlJNF35w7Ja%1t5|@MCiR|=J`S^p-?(RvaFM8BHBhQQyCg10;2kuOZ z7r~q?A{Dqi6wj3Yv`nfh4{kCz|2QgJdN`Ke-#c~eekfHmufJY&{F>jieNJCY=F7~c zn;)FW1d=5=E1T?kvzY(Eo|&^W5JYkOv)GB^fmqn6sGT6Fr50*oCmxlRAc!a8g^EWZ zDvBq9g4kIEH5<>EXkx@fjYKzzs;y_%IY`VZoW_c`c-t5dghMnL3XEqQ<45;8aXrquq}0eUP_c zNOAeTd-fbOK{Jwe{-C*X>YB7$*X#s~M=dmrvk!-2;jp4NMJe+%=F67NCfR2=V=0Cs zG2XEa6l;(5nLs_RP$bHF)9a*d%}yUIZ((1-7(ts?Y*ejZyl*L9?ZeQJ4#9yo*tzrQ z(J&4RhGO}oVP7~JjmKBd#VES$E1!rB+OldF#pHadrvLKc&{Hsrc;m8F1Ut4L+0pB< zFFB=z=C-$B?}1bJq)Ng#d4uM@Ll;amJ*i$bL|19pr#D3@^EBqmmdz&FXGCLTi!>9A zgMjb_M|)8#WME1w8@~j6wez>yV8fCXq;hVBqCpLH?KqGv6hTJLFA%{8MSR32hgmG( z8+TyY^qMeAPv}dfIxdDro7e9QY7Fdvm z`B@b@l;KLpETjRU_|H>Vn_N%k4n z>X)onZhr#xh}17yjhV1CX&Ds+-MbF%+FyB1D>gnYMI(x2pEPMd?U+|jR6!JgpWJc* zX)e19d-pB|3wG>Lum%-1u~$$OEFp>l3Rcv_Xkv{$>Vs%}ASywy5;cl~ulistKgSOy zxo)`J6%}NGlYAkQVdl)4dw0G)vqJxbA5SalzE{@&fZS`J3TnQnHrEvDZgHO=2>ls; zJi&JgDn6_ycV0md`ZL^c&04Rd_M2G)atjvs34+jXDZc;Jyy14Z1uwe(S=`ra#7(Ji*~-xBVN?_Ci0^`QF{y?6%FO71&TNB-7*?%?=eQ+C$i9lfkU-1m=&`=T6i zUk|!3Zqo9^c{|l--I+4fyZ$Vv+Fx|jk)#JoABLX+U%@3;U^K>|9j+m#Z1Y8_v@X?5cI8 zW$I;*X(ul#^KZFs9Q{D@44_&xs#{3IUwEx;)}A`HW0!C^RGQj|9KF0DbgY$t`o5>i zeQ{Y!**m-D*$$q%IzlGa2&VhU!4(+g+jN<>iE*^WrH}QjJWd;UM{$!^w#O;bM$Y0P zFu=3C+UXx2JkUhHmNCt<>I4l4?0!IQ!9#CUlJ3yNlr^W6rmgRtG#HbVyPI7Q)?AeL z2Ch5Kv_}c=|es&Lhz05cl=C`>fydZ3kxcNz++QZ-!mX=v{Y4 z8^I#PKVzdyA7iel z4WE&mSD{47sg51D0JR|+U*XkuU*F?VaG3dvckB{2SJ5k;FaU)`sQY*bWN4R(HG(M} zW@$sG;1TPtjYDsp{$V)Lv!a^(%}h{s*gIXq>x=9@GfC@vx0auHA9Gnh4+(RM5q*RN zAaQ;P|7Buye*XI+;=b-d)g~B3LBY~R_tCGEL5b%tzD+ya3;wM)Xizn18Ph52+Rw?h z*ztLK<#XDB(noYgh|hG+9&uC|h^kW7o{oIywYrb#Xu`Yk0;k{0 z8^rZE+MPa$YSFAf@t|J$g80aewJ2s){J4qB=%mPHOt-iXiBr0guy6Af#?ZD)fHuV- zRkkV&`68hwI!{60Lfj_|P*S(w;nqww9i8b3;yytT1aY4r2!gmz5ClQoCkTQd?h^z- k5cdg!Ac*?}K^W-#1jRJn+4i8=>;M1&07*qoM6N<$g0N7SQ_b!?s0TSHZgA?4{3GVLh?hFvz-Ge&>3liMjo#5^cgUz6K-tTAkoVwqs zx^@4#Q@fr$wfCM`ve#PO{d9Mvl7bY{XZ+6}K72s>E-kL|;lsy@4`S2m|>ASdyn%AmHLRuoxeuS|TukAvlIK|-KFDHNs$7fJNch%5D6kPELFUA6-k1zjRmrqn6Qcpt2lPw z@5aU;C&$eW1j>M3UM|*pmqvH;brJlZI^F)SL9UgRwxq^9&U3{-f9jC-FHMt36Z5)h z8Hn6GUTq1l*e=zGWVd_Wptl6(2?h{*TRYE}PNrm7*_(c$G*4Zy%Eea9=2li$_q&<< z+7eM8b-L3p#lXNow!YaVX!LXKx3j6Ub7WTg@Pu{)Y1HHS$`=*Vvh5kC4~28Tj7~

c-PSAVtMdJ8-lhvo6-*Gz3(-PsEb{uh+XxIPRiBU`S=ldW%QX!fc~$2>~nPKWp?z&z2p}e7!q5wrW%Sfy9iBC(XK{aJd<|_>+W%Wb6D=)B~o? z#qaBf{%o;c_}vWcU&zqO|2gU}Ldp@yhdXkFVs5WZ5$1STjQ?@na8Nc%;2@9og~6CVT1beTbUWzoqZpaLYBV*^8iZ zhqA05ix&JqXpgX3r7*ej=`<)&3DjGxwZ0HlPn@D>`mr6H3ljHFLmm}^fEl*tYbFTE>_g|lzA(3!6Uibinqyd3bYiBnzoHPt?VaRfX_kn)x`~9_2{wR)za9i#e-=7 z(y6+9?V?l#Aea~aJ$#R2F&<)YuxhcNxho_T0|xOVw%AU^Le=`P2O5{=yb zKW^=+0P9r0pXW^5Vkn~yzhg?k{>jh9grRSG`w^9n-6=gQdNCUff?Lk=x=vxlnj@d= zW&cQPXS_s+iGYhHN^bvTin3-8?+bt8(2zCLSTe&zcQIZbwIjct*qSt4@mZ;diJsGGR$ecr$|%a?&^MEAYN{WU0xgoO-vl`(l_{#rOO%@ zURMxw9gW=1hDGr64GG%&oLs}p^^WN7e&?p$vD<*a$J=?^gCyG!;U$@>4sWjZ?X@`z z#d68mjMFn+BisLg`uV>1tuEl41q>E{IGLJ&6yYDgk)si zuU|6)Hz>>b{mpE*wHlS$X|NPI3VV8*bg3?_F%ExpkXjo$j?#5tfYS#!R3k9$NncbA zMPInDK8m!VEo#LINQK z2~CpkPXa31A^DyzD?;@NZz{(OyL@Kmi7;w&M8)6UriNO_^R$gu()!N+A|;sQ=nA4? zG#bsNzjCg!Zbyel1e2x5zsASo&Q!Sarp30r9-x%I>32c-p8H}yBu~3S{ax%eKCWAs zChfYe&yaC{ByK!|o5=|kg6r2GoPWCkTZ1YcEor4#_z7|m zl7`-Ozv7AnT0y!nHqPN{Pp0s;h3<*Vl`TV7B zh8!E4@aQ<=Nh1xXIQd5cx6tKE!;(ixPCY`e!tXIdNIUQGjgG}M7I7_>Wwo2NaNS~Z zWs~AaT1H89#a>=58KpiWzY3<^{cN+LJvX~3DSCUc(tFIQKcgUq&*tT){?twRD4aIL z%fI4CEqF{|OYaFdTiR>?TJ;G$JP`3Ywdpn4@jNXfHmABao(IeQrjJ5k6#*mU^0(8Mzon`)mDr@b~5^?bwbL+B9c0?1DOr9pMp81?MCG zBM}R2{petEmx#mUr%9)ST)s1*(8omICcBNW{ETuEHjnIAX}A!hp0)SZ8V8rSJU@s% zhW`;d4MVioO$i<;X5!Q$o02@oC*z|*|J+uUyT(8l&sLw`v;YYp9Zrt$;I1H;oC>&% zziHs#vkPIk#wE;m_|u<#WIrS64R3N-`_$f?$>S33pEgn^pBG5D6M;&FpH8}u86mA8 z{K9AvG#3p~l&ajr9Z^Im;9Y3*>#_^-ABmIiq6vjz%0bmPp!KaWpQvPv-3a+S77U4( zYo(Ce_}EyA=b3^J>sZPy5;uE11IWQFM!7gHNnPKC%*+T-vSRe6N6zIpE8|73-rnOQ z4!6VBS0}%;UW8fgqk4oA297G-miAA%yNeYPF4TXb+B@6dVCi$LPOjVvX0JE0L9mgk>z$|zlb;BY! zMD4xoJi%iEsIEI}%zqT{!AEMqd)sty;CT#bJ2|mkIL6C7rV$)~{Z&zI|Ao)kc1c(t z$xzj`LFr6ld)_Jsz&iDHXDfr^wXh)YgY^@s4Q3_jA2gFS+M?i2uXRsagY5P zD?J4jP0aNR67RQ2@G3~|f^hZ9ug6o6HOappE`ns=FRpiRByd9Dlo)Y3Lm{F1qQauU>8ZAWPl08!Z9To&)6iZA1G4VGQ zY7k%%O#PSR%TyrV9|oz>K>#;TW)K86R@oG&pzMmX5mWGJsUQZKln*VQklwg8q2 zEZZp>X9lenB?1yW__eQe^K)~FaBu-z3k#HVm6dr$y@d6K9bN^oC_Ln7vQhC6U**Z% zm>=y&M29t96DS#H&0I(iBczkAAMYO;gbJ9LRxM`dB#d3VFU=dC)ggmO2Mia6xQus; ziTFJFIIrf@X()!<%)wyrn&){2%RDT{r!axty}b%`cpRktqv<>xlK9S7>Deehy4vlh z&Zp%n9!nlm3KQJSm<)%DHcd9vl!7`p&`7IBmDYEVkuD0ZwA|>%%ttQ2zULV@_C!@U zJ(i_vLq#FkPfCGIq@**(3s!f%j5}2;4I&q1d40~eG5Bn^f(yfs+alz$q7!91#5+Bf zEbd7B$^?0V6{&R7ep;|7VCv+^*!gYqt%}3(@qfjSy8ug@D@Y%s~FZLAg`lf}Aw%m-N2` z?Jwk^=7rzk*?Z@NfQ+V~Y-~2iXvs5z5uT~3-f-IO5yc!{d8RWK+2<_v$`q!{3Ib6q zld!663bpxk&830=4s}&nLC?pb-4+ z=UP`ypkv*oJoc3|0NLls1-YK>X2-d&Jl$Dc9qX^ZFbf$A8cH`B8rq%hdMp7K z51(SPs~z2~AQ9$&r>-Iv=$g2(JM~X?zY<&*7f_(Qs`rVuQIfE9aemxqSt}UjEav%He2 z&5HlTK&`H9{AU7A2h1Tkob-+MN23xqhQ>JEGyk--40#lSYUoeMg;`QnU%JLNh#oi< z6{Uu6K$M)0o1)QiOnNO=bNjT*!PyzSJ=jm0B^OF~hR=?367w?u&aK!^xGA z(l|jgIWu2E<7a+&8(dw?d+vU|kO_9!Fdq&N&{>W9T@>JM;tU_9N3BB(mD`h^o(>1? zSXVx@;LlmOc8|}FocQs(ST{mJ>1NSkk=4avX!bRd*0!wtVVrEj8D1tC4(lIR5kaRq z5*HXec6{zb`ObY%QZiF&YU)lkCL*R{E~1FQ+nOvp+ZGj?5$O-+&ustd;{!lwEFasj}fy|72C>4c_JGl6EHvNrSEaKT+G&WF@xlw{Nb9F9!VO z4412=(hL^mv59%Wg0U2OkSk~AQyKxisq{lR;QC^H*gD&K7p}Xz3HXQ>cJjsrs(SIK z{bgqaxLQgzJoBW|fHY<#0p9AR4?Am#^hf1_vCIJf)9r9h?SIqc{F`>_*EF(|jTEE~ z)s`%bf?i%;ke!~m0n=Ml%2l7t{fC5}c$Ms%AU zcEWWRUPX=vn2m@%^r%(51ZXbGkdmHH?9jdQ_gk(=!9p1?{al`^q**_v7uuiy-m2&yLMLBqQcr2%L2Nh&x zWT2p-l~q+$g9Up;cpiRGIkkFT5!*Pi2OVy-dnTIFyNQXaN=Ss3OZmKj>Ok#6FW3DL z3XR1jCeP*r=76#3w3~EKxR!x%%rpWAgjp=}2?z*4%oSv2hg2)1ENW`%`QC_b(VWyx zd@QWgpFio|U{vJf5Dj@(Q-x8HzeUAdLC>Acm-Q-~1fTYY*IN4fzkTFMVgSv}&DA$F zoa}xGR=5of4aH$HP%|JuE{<`<(|1LdPAKjPzh(b+;O5*@qM^r?=oEsL4_;+yx6wTaVLy||Y7V{)o4lL8zRnp!q7lr&^OjKa zoL8}7wUl?&aVy_-rkvfmB2AIjjy4?rOZdcSA{=h^f51L$=TAq|gDeS;+@Vzc*c_tvJfq6VwhuW9 zH$oq7-XGSyZ;zOC8gU0(TDbR>XJ^R;H6SC`dvQ2A6{VVo?Dvju0)W{U{7-+8bn%>* z`pQOxs?jBM6z%lS{bpTd0j=ea@)hE5cZ1_oH{Cc$NKWv>HyAp0e%HUzt1H(v>FHUr z5K=R4ZeNYK-ml>_t?U3+0{+x%-E+} z-ouUi#@A_M&mWQ`&QI)kg1~t`l5falWM#|(FqS?pwLLxkBWPr;?(bPF^9Mz*R|AWu zVTVhy(;H;(VrY_vd4&|(@gvjgMVFCN+ManwLSBJyznQR?I<0&J4%>Gjx^7w=?(Syf zBqw`b{G@>*DYgq`izIKlSejPBbgk|BZWeu3>#`P8#h_d~?#U$p4MZ29Fg-m#X(jqJ znhGfY+G>%iOp*?=4OoQBGJSX1?74Y09N$6FsJ++vGv>F>Ep@?kH}E}q$;XQndoQET zltX8ysKH`dyV?IWsYF)ZHw!Q8A(z>@9vBiO|F=drd~#SZgN9E2Vi-~91&h*V{cy5h z;cwOb55@)CncvC=B5W8niqX;}Z7yP-!NjXlHI{ZZ4L0MA*O*5U?7HzepANBIo{*kN zD(TDH503JNz}Ml+?^>s*&m_^W@g8prS{q~xEoOHNY1;|79FklctG*ovXhjp*mOv&seu}yYYYpLz zFE+b2n-0bGI~)}M-RYBt6t#VR&Xre`^S3eO&!4}{iqle2{q^Y)#*K}Q9e%%Os!Qz( zBwHzIc8r#3_P&J_U1JhKLYM+7Qc~2xq`5hg>D;eHgZ;vC{;c9(OCVw!y|Nb<6_vop zv;WLD^l2SjD)hRRyLxzdxVo|-MumNV-l#E{40Z;Z!P8k+W^p+R`oG{x`ulf*4kz5* z=eL7g$EQgcAW7@DR--ugHBotr~xr)BP8l9n4cubv2n-SkzP>YLMdRJdClC z5j79jpS)OugVFD^ZSS+k;sSZ^tLUX0s5eT@58Qx-OM(b z7nAAA)@P1o1c%fFQ)=5K9~$UA6MCQ6pp9Gvq)q^s)sC#A>$^gic+=9`>oVXjqwY6F zQK8EG9O@WW{37!)_WUNXr)`l$@%K70UbUDMI~qntBA!uy&9L8$mUiL}Mw~Maw&bT5 z{7l;Imwz&L-&&8%A>AzeEUnf{;<}T*1oPqjJFM@oC?wF?(B#t?JR3k1B8wt`QAm~w zhJlhQsSSw4uW^f*7e)+3GWafG=`%2-O1Cl`Pcn@0sW7QewF?B4jSUX$mXsRqskG8X zTheyBzI{}blj~a>s9tNL4JWmO*}zPdd7s}TBW{y$+e_Oec;F!D{P8kv^&=RfZFjPc%eI4ILUrG6$eVej=rS^eSTAGJ(+%HdyVYzkagPoUd05Ry zoZas^q%tPWE;zwBniJg(AL|frnO@2%@Pv50V?$m$Pw>Dvj(4vRVLSByi2NTEUlF3p zq)+_}{ZqAy?ErI>GATB3IgLDG{xROd6kn?x{?v`P${-=4bR#@SDvg~1&5u7_F&vd^ zVk_Wx^J65sv<>A+M1kPoTvU=(^D4ku^%0SVW)Mi|N-Q#cx6# z;?a+H6X#P3nS%7#p&I}L(XDrJKth1%8-iitzYs4!AcfP>zEhwq7wP8x)%5dFeaWCn zh2q=7C&=8BgCG&w_xGC8`4+6cG_m))`1tQ_rnteW2i@AbMeW(x!0V}YRaL^+)KpD9 zb74S$$!?VUV#7RY@WQc#yrEQRLExt|u{RIucC(w0nS@Br&r3)u;-_r0c>U6dmBMsC4Q=?hmiF;cgIXy* z<;q2qbwTd!Lz_QiZqChX=I^rP&zLEDkjRnYJ~gb?*t7Bl)uV}_!Z4I3{&wWds){kM z*KXXSs||B{d*0cJF8TWQRK4*H6MIsqEp+6P3m@q(=YF2%@$)|crTMS0f?z~ZK;%BN zwUTY1$1OFE^8?ClpGcmsbS|r{71>d8@Yz7Y24JSI^@Z(-i<9g792;`-qnwN(K7l16 za>jtUvkA4WO%(0jvJ6r%;{%_O6~bem*_|lZm^#iM~E^5%^gT z=&QS-A<4g)4K@eO5gubCu@?flDv^W6)AcaOzD9l16ZJ6TxJ-P!E{>aK6=LuNL`40! z>+`oZ-rlXPC9oby0gX)4U0#u4heKnIgfC`VvjYLrR{f4@_?RlwN z+#qda;}vL(?z~hXGL}f)`2lIrUX>2GU;61i4`QIU`q+P+Y=nTG+~A1m#~98wyA#Rt zdyR|9E5ko1;C?Ne($bR8lr#a&4hK7C7Gd!S_@gkNj~c8EiFIh>)P&1$*fC<0R$4(= zt67R`U9;T>OpO_|>^FY3nou6Y1EG%B=0BBaU{v=OrdvJqCC6Y|c2IyBH-fpl35Wze zE-4-)BTAdAPeC;Ya`fo)(3Hp3x45`b2?& z+oB#G@NZ-EbOSApAX7$hlNgYkr(qi)w56*n5J~^QU;u`ud%LLuAHnA=2_0p${gLkR zaK6d0_)17^EN|?W%G>|_v`QPWe4(b1*7a7=r}f|dkO4neFD^O?1^XpzWeK%AX&Tzu znajlhA3a};W%7Y4CUT#InwnVI8j4rb+gk4LuP~C6^QftoldO%LoUE<2B~{S4_*uzs z5XzES`DM-{aBy&Fw=La+M z?hB)&n0=MYVO5sVli7!&8mT>G#qce$-MQMfyonY9s;_x&b=gHUn>hsDq@1riQb#$0 zQ*Aagwv1!ot8vt@UqKE;dmVHN6qiS!P2+L(q>^ zY~FVcM#4}sq9sA9HIf|8`LJJDxv8>SC+BJLKn;Ub@SpvM;dR-?(enpfzmD=OMy{AU zm(`B1n1bb%oks^;&~}*U-Xe-xa(eVad~eEo!h6!TF3z=Kuc@6IUvam`GC&jj;!fB5 z2X{Xe#|%;3-8>u|9iMxfU!yx+PptPo_dEGZqkSerSLRe-G+(VBD{a5j;i&v2RH(wG zol?#T4=u5-a|w%pFdz|;C2RzP8{%X~9*lHo@w=Dv)0#R0SxDDzvO*XFsd^+6sH2uR zRxrLBFOTPTIaj&L$s_Fk*HKzt1#gZ*c>hQD`rGk1v-SXSv15{g0CCM@lGgFJgLgg8 z_vUnQMW+Y0b3>1*FW(97;%B-E53+0&ncohtN{+8EdV6k8Or!buhQGhNq#1sKcAVZ? zIVTIa;~>uMZue3GvOZkl*%*Rg#-pRZoMZ#3KO44mz0R*~!#{o-?rQAx0ree3XPX^4 z&vx_mBn$Bu3=mwESnh7=?C*y^-0zO|zXq=x|CjpzVZ{Gi9BmAMMfn^ZMU0lDupe;RrKzG~Vw{0^ zkQ-`T^X1jlMd*FH+lxO}j;hn3Hq=;cZqE?N>fdb`1qOSo_FrE+LHJTrQ&R~E2`Q=Y zMH?b~d>dxj_z(DKECL10s;bEU0oW=WjgF=BxK~Yw6ssb zy{V~KRce0_KaFo}=rfd+mDLTX634?0LWnII%wIJ%lKQ&3(F*+Hosds!YousUm=iZY zD{GLCvGLCFv6Y(}CP!FZS65u$iJJ*tr1A3j#*ep_CUtbl5Z{Ik%xTK0^BO?*! z!y-D|$P_NtcOFjyc`R8&;-$~3Pr!j&JAK;MD^<%?CnKAM``pa%*I z|5O))FiYy{sgo9G7Z-zM`QG;SlWrgvH*UWnBy*}d;xhDmAdpouLN$!2u%M=9ikwC6 ze1D%Zj%d1cMyMkUQzc-TqM^I7F+_)Ic6nxTvE(Hgs)4F(5;xNvJdg@SJ)~uNv@8Z` zD?rU{Z8j{$U0uWiG$|Ex`9r2aXa)K_vzj`$R=r>L0D#`&(6&6eDibvpdIo1oV^fn% z;JkGf3kf%`|&^dtgchN#Wi_b(|>D@?F$WSbx7*jOs-J#K8J`6^_cH;DGu9 zYk{UdBct4(fJLL9S_l0-Jw0WQiWm4{f0~{kcaefnvK=DjqiN17`paOU_-H+|;MpM5S44wtK0C{SV1s*JK*P=))xdg|%9ZzZB7M~{S?z=1< z*xrqmJ&2JHWEmB^p78TQ(!^Lo>Vlb|-RHbw>e~G4(%drh0P`~5lq(^RiPw?}=9s;A z>C3})nH+`oa(zVENqSd>aUSTauup}bzKY{&zba*>wt3Ue6OK--kt3C0I%;Fv%+H;$ zHZUVebQG(O5c>_CSzCgF3)z&qI{1&C{WgP-4jLXeR?CoI93Y8c*lopHi|MH@0Cy zUhxP0TC|D#igWhz0)B@nR^gTH#>HFt;j2xK&1qS%Kr$09tYOk6!UXkSe>Vtujhqy>!wH9-F}OLw zB!AA!T0h1MzYJspQ&xlE0;F<4rikXv(^}1OzzYcCZnH44iQAs{4lyXn>|>!MN#fWa|6N zp>_6FVaR#ouLk9NYL&7>CDKB$vdwk|%<; z1W7tKAQFxDh+v`ob0LCgg6cr&Psu;N+P_S=I>RPY@C(Dn8C!p~oyQ}!C4n+12p?S| z097TrkoyK(d-NV%TU%QzJF_tCHFaKeoo77>mMB)?8+0RW7i9&k{K-qF+T;EK$!hJN znN*T$;wu|&roWaHCvV=yM#tVyF&^@ z4H8h_o=H}?&i(NbBa_+mVS%f(<=-lpQ7`dn2-xpi95JwBKFRumy!$G?sk@h5{_rM4wPZ)~hz>6WKQy;Q}8J)hvs zTfdaAS%@eZ+b`!o=6UKPD`9fbXT6T{0#G@7R`2?ob90ksYRomW^_^s(cTYF zgI$m!dYIiw{v0t^kKqd^q~p?6+3kMmK3v(16mR*ZGgf1%U0>Dp;>&nV#mdhiPD^Yt z05@U;%6lxB@??uDUKGInAp&Ip&hP*45QV*Uk(ekTV6r~jYsGF96v&V~>PJu%IjJCa z#plcFAY1Vi@)lC70v)u9d^GurMIF?}`~@2k5u}z6pPR^4ilo(wsfNBj^$u`+bB<&I z98VJqczgHzqVD&zPmGW5Ph|RDt#dtc9ZhsT-Z{7Xzw#0-*A?|>RmSUXnGVNw`#w(X zgnpOSyW8#!hES!|qa#YD*48I$&5qXg5m!oe*MTAdo81A1`uZ2woKX%9utiJ;_2TED ze4Z9&RR%gR+X<>Ew`KUy4%1OzpZsI6}{1Ip+-Qm=dsjJDoOOY;nU1&rDlg3&3M{ZTeup%Rv_>|mX^nFNDdQ} z*4;nd*&S{R+K21Xv3Lt=fe`fhw~A=4Dkdr*Vj=&DU{|8V8-ZgAd(OL*1Nwax-8G?X z53NmH592u1=C0Sfxe{4MWcTOR$7V&*?4O-8q3JQPFzI1birOY;N`h{bHtP{k{AZR9IA26u{yB6V~w2 zTtqKBq*`I;nT%@*6oG{X<_V6EIQQ}DlME%mKS2jf=Ls#*k?dp%LQ9wk>=r_6t*$bF zjrOxN_t5Vx{J+ zb5w@}jqgK>)qV8jZKZ(z(@K}6VF>h%nra+&rJFC zgpNYL2GM0BZC;(L05tgom}?T5B-*SVq2St&h(ySFj`FW5Ue?pod{xOP_u|4rLO10f z^@_NfD=t=~9P~jXDe*+q)gNS`VF{QRqJr5F;-m)H$1+w(k0Zk8tWK{9 zk$T+DSX&o0yt|MPnki^Zz(b0Lz%XRp+*&^ulSh{wmUJd$70m~dsH+*{;tv+)mJR7l zMU_$qDvU8Kd?fXS1Z+%cLrdo7v&bIY<<-@T`=6B6IPF&v>0OV8tRBUSY3X>mmkgt$ z#4`mfk)tHHg5wBN(IbrZocdw2or5n zXdyx9aAbXnX2zgw@Yl9df=8d1-Y*nB+te~1{ z5D+5!UtS2e=RaWy;y{U=g8Ldyl_N%u$tp@dXG0R1p8n%6zMk|#<0|`HE#%tes*RLoGvO=#W>|oB5h9AbI~Em41BZeC&QIMXu{Bq zUqw>Xa@p`^#}IbtV<3h9=y-oM`dCFYMY`{e)W|SPpA_BL@vNy?csi0mXQ;h)-Bk)@ z-bhOV)-$G{SKkF1fDtsoz4q1Z=j}(oF2u}^J5}x}c1sjr4#0%Mh%4t2^eI|hNy^Lk zCb)wgZ`k>TWb`Cu;e42w{xkvKEWA+#flivw-ARKkQ|3g5eWBn$q#;-&NPlA#`E=A2 z-D@EjS~2Jqr}N|s%A6cbs^25PU~dp)=gZl|#6;Y5bNE^<%b3^cKRn%RsU!Fs0r(LZ zD3E}XfN~xP_yI6N*@xbT_9Z1DC0V9G0j6RN)(l3y>LJrmg4J9=&sPTtrfO|P$HHC{ zm0l+<-mQ7#A7QDs(Bjvbtkjh!rVoBVB{QMH^r?GQW@;x|l+0n_sQ=Z(QyC0PWz}3) z0fJj$lzM;yfIBTK11(U^NW06RihN&%n_LdB3o>}SabLCn&q%6bQ$-Fk_(!+S2k?h~ zX%_UqX$B4c0PZ2CX4)#NsL*?Pxklvxt9d-cd!L5$JDKX;v;2BfCIs4uDCrw0^RTd- z903D8Ed?V>>OGLi(O^!w1eF92YWp@m`1%-B{)Md6Qw+8>B33M0T&rr+yFVc}q~yT@ zi(6`X?rr#vvDsq7jg{%|EwW$jyG~9xZ0YKUtB@Opj81!Yt+VB=6)gNoIc;mM-TD(t zVHn~JJoq?z)RCgq3M^5%T1_79^52hUeOZJWfqM`Z6c+pq_wU`^Zwt;%9T>NAjQcj6 z#8d@F4o^PNgly2;MHj+BCjVwAu;woQGdbl7m|E%TYA7W&{LMgyp|)yb0)QN3v(epn zCzdk-VU3cLNenyA5p_=&_xbsrCHlR1b0#e|qS-l`Ayc>DR;}%H0EF*(W?Wxc(bm>i zKlQVZK|;UW}UAZ%pce`_i8Dz<2A^)%IJ5N3VJe%y+gpg-HM2 z>OIIipUiE0)LF}Y0@sm^0Y~|zV%TZ+WxLqkM|-z^brVfRJ%U?#l@fqogN&S*OQcf| zi$*k6b-7r9B3L_ZV1KLf%644{JF|VftfgGlQc$3b*Zb=hCrWmgx2kBbmDun00mavH zYEBO|bSDq!2Z&a7YId%Wm~9bPN{-NJDGCk34jPJZjbFFmp5U6C!gp;}cJr_hqs*+$O^Bxn~y0TH+}7?c^TslAF3+=-J*70S8US z7x}ck-Zytz9k(% zD?p7lGa*h{>;n0Q+|w(8N+&Ej3?iI&c4H0-8AV>xdxt0D0egDs>MMolb9KM+FG7l~Xtk|XD6*YT$?vsQkwuxIk-#^bD zHMow3NC43wyIJFsMio;~PTkKh%5D*W>S`Ka06CEqI$lwyTRPL%rgmVd@TqJ8J5(38zDZ+kp!`D&q=3fl^CLaZ0wTnN_+pArPh{ zgSQ(sHp%G}>dW-hl&6PhS(znwr7^>RW`JzvXk;mHrB$v^1>Erh1V+_|19aEcMPj2p%zAskxxBQhTqr7;5POKO-YWcj%yc908m{h#V;qkNpeNCxH; z0VQ7q{gJ{iCqwPmkpZlki1uV23~aB4xBJse9)wW*V7QSr&w6be$zZT`bZKh*XDNL9 z3aB!Y^cG=eA=lY#;4JEsk9TIEn);u~_%ZWBjST^=@|TA?D4`5P^I1~x@6QP9sI|Ym zw2uCqA?_wpM$+F zeELeqJb?h?Z&fYF#69ePvvhsDzO`5VNPKYud;9W1>?r8IMHrmn~BPnpo`i2zrUuBAE?)wUCL$cJVEo5XhI}+@dl<5i2%gyHu z>5P>I@~!qi9FYBQ`!AlGJst-(?d{8(CkA|WM%Yxzy^jqsM z{6g0k<_&=H$*J_|ccY_cF0ca58lT^TUC4>tp~w4=vgOUrTW30~weX0;cY943>``jU zyx%DQ2IJrVN_O8xDLC=ojuezPD6=aF+OhqK6@Sh6nrD>&aC1$z@n9%cZv6>pZ};W$ zeI^$2mNEIYx-xfys9oWTzcsfy^eV(lp2L3^8Xhkh1h%(Deo03unex{65F*4QDq2XJ zsF?M%Jo)gmI!KG}$@5<9kt6S}e1_EX$2m3J;hTVg&J{P_^fbG%Y~P!& zswWn7)|-${6N)y3HrnL6dLwU^oHcTd z_T?SGaCmqgU+!(hQUBx0_VtU8@?Nj?3Ok{8^bc`Qd^5_X_?n+OKbf{sjDQs?g8Q*8 zzXcbzA{I5r&$DO_Z% zy45|X`ixN%zV9slh<+-&zDHf|*Fy45*)T(mVcK0s3NfN(V-C-_O zKmK+7xONw{xv;X+F>@>-dH?BK`}?=8>7pVt)<~$ymHL$`!nW3~I1eW)BW)VYrsk_% zBKCrb0~duORGS2avYWl*W$q`Fv~hDgc*-l&=Mb5a`sT;aOW#XW@*W?}`7!{4@fHr{ zfxc%H4%ahhM5;TteJwxkj7c zuJ{iJ&7IqR6#2R8ZqA4dZ&TU~TWx138Sp9S3U?tEy4Um@=mver1YTOpD}9297MckO_^7ZGdwlr8Y4=cs8p(@+a9HixxV42oeFTtdd4Y| zK|)~2ne(Tbbk5CVPyhJDP0XINV8<0r zKMx%KhCSTWnt?`08MxP^<>YF=3s@2!y3sG`x}M-tGc(2iu39A zd@AAxYUyN zvtEg2SV8E*WrU$pI<~;M?zhJVU~8qeQ{?(A?4gpeub7R>N~dp$^+D~O9=T%qF`w1p zN3b+?9uwbKR9BtFx9BXfOhg*&Zl+(Z_ zO|1?P{V^r6E9uNg6#|>v%|6P*R}3!8mqgIDz1#ebl)ximp6{dwg1lld;L;>h!6 z`pwJ>_%b&s^!{HJkiCpqF0dcw4fWztnI`&a%=WKYB!v3oU!N)dG;z%o|6mY(IcvPY z)sFH`OPlGhVUbxoCCiv?b~CdhNlhUQ`*bE{^eVu7Q79sT4pP|Y-y7O$yYnhPIvS~b z4b@(I`E#av@HaM!0#k38hqG;SVh2`_sD9^(AyU(Y6fD?;bVPa1`PR}#6fH!e)Y;9Q z2@iO9ZEWkP>0NDN*U6V(iNxRWTtT$z$zSuoEPUXD(60C4T=us2(AJ_W03WWKyOO;v zD4zuc?>)R|?zz_)pRTdGq^zi+O&ZkQq^f&(pe|WD@_Zl=EVSCBx`^%QlG>#5{1x3L zb@%`4tN-kZe?IrWRq;NvgI-6D0i_$1+W5I1)OVxqy=qO3H3+tgp|eG%4{6S&Y-Tvy z2CIEynPed(z-VgZP_+)dW<%R)VRtm$_d1}X-2Lzahb~B3mCZz{`&t zdeZ!W{E^Ymi#hrD5aIavI88W*DGVxrBkpZ?X+tLs6?ejDYir|5M-Lj< zc^@dsiiX%TJ|4xTZqEkYfMYdp+u#QP09J3GX1lj9c{mUuNYG(2Kc95xij3&L410-k zW%7lP1{$W?dFh7s@`AvM=>N{CP`PBz@?_@tHs?bqyb}kJO@k)EabF+8{Q240BDxZk z{1LQpERi)cqcT?%53_da;z^&_O&i|lhlYfNRINn81S{?59FQ`$KV=>O>C&Q(A2O|z zxryvER9$>|5k#Xth<9^yqx*>R=tZWzg#~lUPl7?`3!~uHum%}GkEn$N`;G`I(c6oh{j)72>Rmj;Yj#C(9uIv+GMHQXi%19o zfre-14Vo2(K zFp#o-KoBk+8k7L%Cr?gXUeUjGr4FP?c;&dSKRD1q@3;IN{3w`z~NBq9Y~S+<>oj1N68b4H&$)0$^L$aBiDBu@1`Csid>KVW zly;-j!@EG4z^cHbb#I8ryzL_~t>&eaTlny}DxN-hLeO#AnoA}TH*4S^8P!LG5l$=* zqztN!laGy_G=SX^EIngYAY?JXtT056o}|s|#qh?X%_io1gqcEBD&C*s>X805CJXfa zu$#zU;Fr)^nV(tsCR`X>VUaeCL~%NbmdMun>L!rlR>3#?$x#F^Cs~hn*7A&QmtoLv zQs81hP@O_g1TSDe@b%F16d}+H;YSYOR&J&MpKjNa_zCQj2AB8dDfq))j3RrhU3~FA zb7-cM`@v1u^`+YJ%!hN&isVh~Z)tj3C7pb03+`dioxBMF2Pr#2zyfd8CEu7XzRlJBT;X(n{8bI&Em_EWU+B$_80g#H4zez>BFZoA`%Utp;J6fFb zFfRl|jU$6EaY7X<-AZQ&2o;}}z=lXu$1#U3N>Ro-DfI$zLJPtv?_#B?bw1Yoe?AhF zcyqEENy$QN17`d7(Vfr-`=1)OxdsSep-ZWg8RtkSp33!(fS<){x<8X$MSgr3xRcGC zUBf+d3rM%|`qa;L_wo>^v83UN#KWR@K$v_KpLXC`(8bR-U<4}ncLJBhUrP&uPM_hW zL0@GbZ#zR5i}Q(0gytD^@qX4A=$go8r-7dHDtuxrosNik0;>}6)GTV{F9KUb%c!J? z2rLiGL@DpEA&AQB?4VZN4xHJ$hs&Ie=)jTP&1+ZTyjS}A_NjhPJT0jS^MuZm+4ivg z*Gjl#3pPl@^#~<4Bm@LU1JLWtay^F^JU$^Y#yTHS4pNe~=ur0}a%q;{W<_#eN{L}T zFUZeLPJ6JJ&a)h`0Cw~3;CWTySiTbeJP5fBQkxh3YR+rYKU)FupZKi0AC~}wfonnA z9s=qhGBIrBVLZ|Q`c=)PvAwyuy#3ipNl8+2|Cc|~fiyK@kff!T+_y2XRx9JDPn-vT zmE2`yEPnim)y|4dO-xDIJqP5xJ?ecP*0{qkGHkR_h#>=*W_gl$0UpdGc_%n=qTx z^2c6v0w_kcvpMN5gM?vhVuG8^>sZs?-dq5GpPdm`>$LPG19yx&eKRbLXhs?i*JqYT ziZ)br*w~hh$RDw2-*I9M@7*9vMb~1#$k@PDV)919UrzaWp>`ZTQHX`8yt)rh@#+#vfCgFKS%Z#D%Wt>l}^h7|G{S&QJciM zdn74m=v+!NbcGt|ajxFMN z1i>v^7mPx$KyAbV`C#$tqY^$S0`|s#OY}dm!4x|(n_)Uf4Ir@)pSMPseO2)WDjJC zznSGk#6D42dcCy#p2!N|?ka{8$+i{v^r+5C7dMh7opDrkvLDc`{y}b;gyjw$d460E z;(Odw#mVy4I~0G+Z4Kqc;UnTRYbPW)!5}rdxIp7XUG6Y=5Gft3Els0#I^MsB`opN) zCYf~_Ni3*5QNq-^;5RwBn9mrjhYz z24)T>5MJsX@yYrG^v3m?yQC>_=B>@GjoB44fsrDcp^^F~;^{~Ietmz7C$cV2#1iJ$ zTl*wqDkW_~qr#T+7^#Cu6|$;R7wlHC`Z`9IBd_Vxf53*kl(3S~#k#r6;jhWqTfESo zFqA8UR}MXm^}4}J6hs{WD`Upb&6Ji|8qMPKk$NEt4^%NKegT8Xe2sJL@4Kl zxfeaDB4@ur^+;*zekxEPAh*#wI@ule-WsLDNkI@&Ai;fM3h?hFvQX;UPZDC0t%+dGc-F)(S4D)y5SP;U$_LG-OXszua9o6a>bfVPc_%o z9U$YYn^>={A)8eP6H=E-E$e1hW)ql(m{FbGi@mD$#t=27A-la&WN*Kr#Y;EtK-q1K zx|2;?F)nivmE8zVw*>dmxIGG00P};&7$J7t9Kgy5j z<^68O0tU}<$fw3wq3aHWUrzid(_nxctGrxI^~e`E@cIUdV(_(wBxxv?Mp#Bc2pYbiltSy z@LrQm!B0tLxjmekU%JP{Kd0s0upu6f^)HYQ?>_D&>$JX&jIWh(-~>95fD@RKZCsZ6 zMJt~F&V3b`>|YiwLr&A6U)K8EzfxUa(|8Lz-N&qUiI9#(3SX0Q;nS@WK+!X5Fc|Ha zVj!8nH6q)zQg&-P(*t_(e%k@(FDxu8ue5c{6zz<&FwVnQgsBK+@tL1C*VSp=dmTgC zTF==UaGV3^_bEH z|Cbe=T5kOLv!%~U!0i>(vvoNnLp%=MYTDV}uASkvJdo%!F)>|s1v#>lb-sURy9P%8 z7D$+y`nkm779AaOfBpuAwTja-FnH=`(pTj?951ls$l9*A^YTzRe~gqorXzwi$&M)h z2e`6XNLF1yoI!yg6&NTX>>6bnXAmTr?_7}kiv?07myN;l03{!VF1SHCtRa=F?a!$A z%w6cb9yfI2zx@4oULf~xMlkvxYN2C#iH_DR87ixK8$e8A(!)@9*&j`P z`C9E0p(_ge&pFIF$Rs~BG`b6?r)Ol)NV=!^c&<?u5MXK`#wK--9wsx{i0~M(NfV09#F|O*y{RueS6GZXtLL( zOQ0zJL&Ev~Oc1N1)8`EeoE08f7rxl;3vYgK3u&$i{oLN`bALu!N&abWCo%E6{jplP zc1d0-;{9^F`}Gs;H(z_w%#5wQS12@AZfx|IAC?{m7gq&TyOf9hmotay0{Q3tg)bdk z5J9!3pVsXBy1eC*l;mbtzH|K=civ=IGN@}FE6`*ke;}Ho51!=RO7sOyR&X(GwTxc` zm-T8$MuUBB%p|MZYfYUiIZeEP=N+0ZU(S!C0{~3y)8jR5s0chG)<@r8a8tUvO21M& zevNS(-x_Rf*?(4GvFldDx@!KFi>+NyR~Md=hPEi$n{R!&{RzuNuM>8$r8Su*pLJa* zuUC^-%3D7jM2MHZmVjXK33Gw#zrLQ503STB|Z}uqE1BP;-s@# zgnPrtAB~m0r$1A{$)~rV0WSOTgNru4^SbMy>ucW51CqC?b?h$4bjkNWpri1fRaaw& zi`4@sZ^n~E{P=iW`|O2drl(zdK2%GD`VfsE@4T4?KQ%DK3;DyKeU~yAok0+Oui4g6Qks;zfJ-z zsDPy@kkV|o1wy#F7*%>yv~+lWgt~?XmTt!Mv7~;GLKZOp%svFKm3mH?&G^iS1NQ#? zgO?CW94x-3hBgT?ph=^=`6igk7?@qGfEAt4g85M2`t_3ZA%O%<^~%MY%y0uKSZr22 zU&$cUTxl8B`_r+|T_UbhCm$!zY7l9GIGM@J6&fI@1jI7L)tj6l&j`|PX6`Qn)`L}8EsAz_b+y`O<`qVj&k zKBW$TT*wJ8N^cm7$FJYiqd5aTw6Zo>$h|v0gFXd2y|+En)Qp6}vr@{UjT`nn(^E?& z)v&d^0~(C`mu{AWphP(m3-sY)eB4RC#YodjXOPZ3W9m1``T|6UgmL39wMqK*jdvIZ zl1n({{?_y^o)Ejf)#BEQ+ry@aplu*DJW2n|h?M|s|9^BD(bETVGYDcp$j2NATc5~R z7MQBwm2YauckuX~69&OUJ#k3Q2i{**cdfd9*JjRL33uzo34O+Zhjp`{g8bcUUN^62 z*7i8;c&4|1oifuoCso?u6j*_Vj}};zc&k#D$6$Ou^$6jxsj1bUiOr}-(!Ic1hH+`y zn#D`a1B;o(;Hsj;pdd-j@9TIEXy-*mD1op(R>#hkpQ0!okG96gZQ}|A*x5(?bbXCR zJ&z39Pa>GB0dP)@+MY6>ZYJWiOI#VtxYg?(HYWt)%K#*isQSQ~F zQ&uK)?&}ca-{E==91a+jm7t9ot2k692p7BmxPMP3tNH-lxXKJ6sgZERn8i3XU`*nO z_fuu_nqK5xZ_!D6G)cVHc%EVD zHI*mA$H~@8L_)eQ8wI2i+^)b7LGG}MQycLbYYfKv$U#`a9V&YPHr~U$gd@<)i@qRJ zj=s)28;MtLF)_7-g5rwPl6eP#FV!qVx~fsF%MmoLP7W$YI9hCoqH*H zg8G<%fwgh3+4Fw9u+SuRKi8*2{HVZ_TDB*rwx+802ooTvrlTG{Tvb_F)F!^5u=QJ( zXn%5ecV_Fz!wE9J_At1tf^zo*MzjW};%QW3NxQv*cL6Q*C%S9c6`0`^V0nH|%b-?W zWw;j(y1qtGy42&OcHrmZOZo*ua{+qZX)_Ly6e@|MNaqHG?0p=)vzo;U5IhNg=)(-+ zK+oc8YNWhF$mNrBGrLJhxktwUBl_G0;V7(#wJ7%j=VxZL5-8dFB0xSqdU#xdf*%mn z@|UU^gg?VXCn6d{ zgq9WqW)(hkzEwNkx6AatBN^BTQ6xj{RkN^2SNqinEb8wq{GMma?4qcm_}Q{y-q$zq z&(P4?n7P|KTWUV`MTFhY?WjIub3uwThCB8PkhqD9PN4w@-Z(v|{I>I4=;pIT>1zcs z4dfI1mG<7iI-o7s& z19LYF_UNbt=hG_8AkGPrp7U5Dp1QQrBk8u=*DM_00%*7GO$F95fqLD0@-2e<&!1ox znIWnjuJ+1_E|xep97IQiYO$aSHAqWczNC;5Qp5D|n%-EE=Jo6kyM#4{D~|OEprft> zjPZu<=Lk;3}X@q?jH2A>^d58)cqA^5bVRnrp9+4w-=0 z&BEXaK1wpPUTEJz!^*`{F`VTyAQt2ss6hpZh?rT+#%ugqsh*zR#$I@J%J15<6?*g3 z%4K51#Orl;Xv&nn#=ia23Yh4T@vU@by-e5IIPmH^Wkv^Nj9RrwQjhr?~~- zw8U;BGb3eoO4}B52}vU)vDR*5#Y?xz&4*IKyUtaJMQ*@>WZ*5fej3zJ&q3!{H}Z|C zo?ND_h@fE8+bv~)raWIq)0oC(P#?W1rTNr&MS5+~#K^xU7P993hTpLbCq`-L_T>Et z-gI#D0b28Mxm^i@fRnZxec6ynaIxdHp{|CuS4(?@NCy7PuqE#GOX$#EB5t?|DL>D* z>{BXbI)r-6AAjBz{vCPC}pjnUE$#%tiY?_vUE69?BC zQI-wcgAe)n=biM4_B@Q^8d)9})l=pK5k=yZHJ#_spQ80PbCfDQbhH7_PeVT>I&Ae1 z4SimJ*m)qR`hzjmDwJ#$5(|>u|beavCI-CSGz57~ir0L{I`wg6<%|DUr z)?P6jiJ2+7_s4P#>M{KXS0NQlqo9`s*8I95W1JrKV)o>bP^*Zn(eZ5r%?kOfPDWdx3{oe>q6f+IlN@Egeb5-*MeBD z|2mLk{Z=hqvbJ@-pSk@dVoj+qsMw+nT?00wA>YFps8j*&AD{aSFAtWMf=_KqsygHE zw$x-gH=?^G^OdNfynN`$a06G{Kc#1~|FB?0?)e*Tj*}bQt5asfEoe?Tp2ti=85PP? z0iREUq7<{%HP>B2a95dmO;eD zouG)Ivk%x)AWF(s1#$8F(2&1CZ0t2!$~=g8!5%bwI;?jZWOEwoRK%Wo6taIQPW{w7 z3R<#0+q$fY?JL|fC;Kj*_ic1lU68lWwdx^jyxj_06z0%_Q<`MHe_xFwTT@(f4P-2% zv$4t%#?t$E2*iG@KclLuuM!GBnNA&#`I$C?PF{O6um+JH?yh))JP}fCB$$6kae?lw zpgn?msPgBI-p?SDw0Cq+$tSX3;8T^$cyR>F6!C=+t*$Q_055$W0}&8;ZA$6^;K@Ui zFB|6umm_6mk6u|%QtJ0FFK|ERDZZrjg|Zm$89I>WJt~VEeic}~-#g}p|HZNnlgrTp zbx!~_El31-7^`qmJ_-tXvf=CQa5tZqt5Kc0+f{w+0{OhY8u1H!x5GR;ez{F2LJSVF zYPfE-ydM9OCtIU1?UhK6oNlbRV=cn*-24H_4a`39Iu-dW}q-4t$w!V@;wd5AyBl~ z%=CcH{Ex3(YqX(>v(0^Aho_kZL&Ofxt2~O_&!9%xD*bjv^)`#PlyRFsA4x_holcZ# zbyQjL@hL(=mD)F*Lx{vCv2cbEj9^3IMb`s`c-*l5{5d7LPMo;Npryq^_Tn{ldyS9Z zS4j1!oGlXUBPN0quEeuk9)si(H3Ib`@ zJuShgY2BKwdT_hd2F4vG22@b|a`)oc2FIfFE?E|J8Hp#EX*v08;b_CSr8>m0CdCv- zB44)Q*eW$ObFO5-!`XN@{A~7RfR@%suhU}S{4I0hfH<=MslR`7Qn73fC75bCd1l6m z4`f=2FP8hWrMxbl6NL&V)KaHJRW$daY{BlvlJ()fRBh&WB=i~o3I^SKdwLc-JJz)| zL6xtwKu>Q+7?KU}*_vMQOsbv+tE`z)(Ek2@16|_Rg9Cghh+ zXZfJ6I^(JXpzN1aMqS;gXvou3yGQvMG27yRBPQzq(D7?Gwpx(*}iqQ9it8e=ihX)b}E}_1~YK^Rxm5zL9$}b3eZQj ztibjB-J!v^k`jcDrzM+d!a;nTAPMaAh%fujI3R5`3f1)5dBuLf!PcWY%1}^Id)7MOSOZQ7t`E^i7OnCT- z!F!h4Xq91&mm1zrD4rgkysO1f-MOMsw(Q5Bs|p%uLEqOCE1CP7*JgUO8fzjhw`Ey2 zn%y1uH}E}=ZnC&DLF62Oh^Q}2>4sb*rr9^zSDv~u4#syfVN`Vr$|R|vxb4A{_>WSi z*#$gbAk=-aOSGS(MuGpTl3zXw78fyakojE5GUX*8GnH(z(?(s)4$e`T)VhrarHF)WWOSK0cKa#{`(5T7T(W%NW{k zG~s!VoiVjK^TvR&{9xcy3Qx}VCt7YyH@mlYl^Hxs%_dgzMN3klvaR^X+kNvLkh@fn zvX`;cT_AMX7I3S-za-^5t-1Vj3-)szWK7Vm^O{-xcdMnXZ7x)yL5EaupYV>~Doh^W zzSlGSm1_r0SnKHVdMsBy@$PVbo{OdcFXp}M*AZ!ZqW2{j1>a(bp<$%I6)SkJK7+~- z0*9a&Sz?9EuXr8RoV3YdYIO=>o_B#Ar}0oufms6RneiK5A%C8T^r_GVa1ZLNr41>? zvR_qEaSW)>+mBW|kkWlOR>JRkM#(qC0$bKn7?@fTy?#Bve(|}%7Y*iTWCZF`@_O~P zmH@89VXMLyNS47}&6%gU!WdCSeR%|%Y;{o)I|W3#{z3=k^Ssl3`k4B(<5h($y>pbi z+lcA&N}XA#QosJ%fMHCOd4Kuqpq%J+?g?DDl*>&3jn+fyk>!8faIA{81vIwr$w8ir zD~@K$%T2~Kn;QCML%;aACEEWv5?w*&EQdCHnkr4yf1L2r)L z?S{_I&K4FWZII$~W<76KzSi2>RuF`NP)^hx5RZn2rlv-Q;(VdtuRIM!W`bW|Cpvi~ z5B{#M>a}-MN&Gp#KZ}frc;<#EuQYP*?sM+$v9Ur57zcr^u`;Q5cTr;i2UCQ(0Vs@S zSN^RFrr0b%RZ~x|7~A{j&$#C2%Xy}-{X^!p#hYz;Xv~mOmH_mms};}SFn8sM9wsOs ze-Ps+8Opzhc$%1)oBwTCaJ3!>->86;zxS;?Jyp>qK*^u|*^Z8X+nqk&tT~RZJUsjZ zWUc~%W`T~e@Y$)UsaaXz={bqxqpPc{1}h&MU>lIY-U6m$`C9!2D^Np+<`e^@N9$&9CN<|Pdq5u5O=!g!gSD{RyOu|kw zVZxg?4ONK4WY&g7ZItBw8OC47K91I$eb42%-fsk^n7FpQMWJn3Vtih*720vx6m5I4S#j z`wcYhr3jEQ-`Pz6h>u>?n+brD#&dZLm?m*2@@+eQCQgY)Zidu7n*rI+xTC1N-wK^P zO0vouWppLQk2dxe2K=fojCm#@$?+ilJ<14shL^>i?)YkOF}O3hjwOt8BXI@98ua$Q zMj!NTdA0kdIadiaL zg+y{*d?6&H-y62%GGm}@wR1t{-y+r0O%J?$aHaEIrTqe-i;C_vaomTlBu#$>VI04c zfp5|hy29|ShwXLMyN^v9xXjnJauHX!!(#A-m9sA|g z72^@Z$;{$2+o)@DjViet+Z>IhqFvptoEH@nB6T!m^Yp?p6VZQ^GNo%f|N9^ySJ7-lE?!n! zT-PM^m0Ti);PW#DR)p-P6jFxa)pvF=@L0@s>yH&f8j_%>miJz2_cGo?!4MZM8>cS4 ziJU9|*?!&D+ObNvl+18=dcY<6#DqVBAqKfuXY+4vqWHDo2fEFwG21FBWUJqV0l8;R zfVb@dtZ+cC`lt4%-Q5I*K-o5%*PQK9l(oHjK1mLnVb6%kxbbPn@R- zsLUr`)vc<`8*W+2a$7vUU7yW#s&y7Z~8a(y)fv%e1lp4XIG;yx@BSYaA z?qWr445K`HYXT25!@QPtf?lD$?>pkAZPm@to4dEGo6coL3R~;-^jX$F4`HY9%)F3XMK#miK7&>pWKw~3? zQF=w5+Q>)xeM+8`X-bhVN}vl)tTvNkCuJJx8J(=){4%~`DU-AQaM4AGzltq;!&RvB z{E{7#*5pr)?6)d~1ut?&h3rRuj}0#p1A$%)f_&l?`1zm^GM&neC`7@(HxpFbPv7a74E25!Ko)Vk&J~`d|0swXE`%SZN>b@#8@KApM?TrXJB0coHJX%?P~X;8 zHX{K!Fo=F14G2&vC@6Y7xLc>dMyNDT2o+~+sKbeV@WyvaMgT|vA!)V z=?h2Q&lvxLe^=oujS&qP-{V8*RwdJuIirI81A-z1O@ny7eLx^FJG*ls*X*+={3-6v zyNUDLk~U0xzg}yKsIR@#+kaJEiKWOdCyH-`e4+Ps_93h{Gc%eoNy9s4bE~JZbVAuwry%bQP2WNE&s`i!Z*t+4A@}!N=bs0RBeiM=V>VD>l(k zTRS)RJ%1>owY;4~uo~BrnF=z#p93G~o*BLazs^AgiY-H49I&!th>+*LC{Oi}d zL`{D|Gz|3H8?W%ozveIVcP>gwY?eVa<>lpW860$~!P@zqa}0`vk6+NGu&m1=Q;O^a z{Z=hChD|KK4%8tS3 zJ?+@l?F>mcL@}JO*enc|VN|uhc0R?!rF}S0rMmr>4BGw4Rt_w2sv4RI-3cugql)bH zRb;U>-ix!WVIuBcziJF-S3t{g82!BY)$4J+8guSUhC1{CCcjBJxCDB1Y8M5X(S*N) z)v!w=d2+ycT86`F#;R=N7OrPvLgORYUlW&{e>;!}F!*w5*GkRcHEiLHhO!bk@5ow9 z{x;AEHq?~M`e5@kOcrsyhrNzDAS7hI3l??)NPqq6{k%JLUBmW(ioUJ?9?{CuDp?Pl zkqKfG5P-5vqOj*dQAv1>&>fvu$o%}@m4)73V;>)(eyDaH%I{s>xkg28EI*-IZE9}T zQ2G(-S6EZ?e&xa?yJ{6_3+%BcIyHBjII{P%Dm=4^CzI^D%lvE=3AB0bMG*2{=r3sj zQ*4Hf(HfFm4qnhep4xJ$Qufw+#UAnBVmPvLkwQB-`*8$c1n~N#d-ssonjqigCYDMx z7)l&Zt^N0>b+oUjhpTVW={61ZS`Y3;A0#bv`N#`c^!$=P(#A1e>{r+4?_-C(#X%SP8WuKdf0Yw2J zF@7}Px7>h*7&@gkY-9<3e*V6`Pi%-(;z0#L5qLga?^I}t3o5IsJezUq*V}>{Vg#|z zyjoZUM8m&@eGH8>ko~0owINoC?3%om;&pJF`O;*|>_M~#Nr=EQIHnrQK(%G8d{+Jl z=wx{%r?|-LZ$l0ZUJKULS_NlA z){_Zr9!O!i{0Fv2K>s;xfKUAAdYyV=PJUL3Vu5RG8}$uMkOL~pkT4_SN= z1TO+lxeN3ohsFL05W)I|+!9S5A{+pn^2EtSp>rN376o+D!w&gp)jl1-f=P^-! z-BoY=StWE@S&V1txaTbBgmxgl3*~6eEzCb`%q<7CuBXo)KWTlM<_6o}&zzil%_$v; zy0%{qZ>X4HTi8bbg&tE8kmV_f>Twp$bm-2gWkEXV6~TN2og3ZlY%!q!ioLrJUqxQ$ z| zG!msO7GG#4bhY(OLYxfElxW4Z#B6x|_sCsYCACtuUAx>_F-QSC78>H+xx_7)@!VMq z>M-hCIX6Sx8S(JuNx0qL_r03}I`$ousZD`znVt8(=Q)$-^Dpwo@ed-nW<%N^eMBHo z57Dv|1nMPPM*63Am$qcn1U|C+btMdsDp+{%?_JA4@{!IaxlkAKpK2B?OUui2Z(H{N zsaMHY&gEEFB9acKrFeEP#nF|lk|AAc}dW?k1 z8RO8qu|P}k77 zW=l+&cW`pGvS7|}qTrsDEMGikH+$&R%{prQWlAPj(VlkBl9SJY#aV!CjcV*R;Tugw z1C;(3e0}R7NEC_oq96g7&lbA0Bt}CN!J@`_f1L-pUpcrN1k$IE%^%jQ2u1UdFdMU{dd{+? zN4aFTe=0LYiZgxe)P;EM2`of{u_n>E0fmhZ*+Cm~-H^#|5n2}7&GY(lPHC(O4@>b2 zO9KASdnFP@C2aMuR&3e~7s^N2^XPkFhr<=;6#`f`&}+D&+Cs(FY)SF>Y30|Mi1M%y zXtdM##DwQA6dDCR59o`lt7~MR)R%u~$`Gic&FazQyL8BHV`F1NFEorFk<3`+=i2JN@x{Y%$p)#>FJbKkrXzUs6`FaPxkWh3L z6?D^?&ad7p`k_cP6J>-072Ct-=|-lzjz$!AGJ{pmPw>_e>Yy+*>>kCiDXgxpzPPx^ zc_o1Dsdd+Iv$d56L!Zkmx245>en?|FPXD09rtLPsiFk5#b@eFjCl?5JIC_O@>w2IW+K%g}o6^e+2F+y#xozK}A&WDJ`@Cd!ZVU2*JK%vwL)1aQmtf~AONp&@h+lfHL{6Sud{hj3Ewh4dxCBN%wB zDmUgEYip#=Nib?^YTcoz&(SZkAI!|ml3bh93Mt>MuCGt$#NcE$!94RTLo~`e6S+`` zKc=&`83YBca9S--GOoogBgGGTn2ymsXeMb+EW`v!0{S8_!+< zQ+^Qgr0}X?zwfa}X(H9p#PGzuRl?P);l&C}O(i7TsNekNf>uW+Z%Qc!fmgZ9tF0?3 z?krWFhG)3A+>ycoTxQOh1RvNK5^_o}T!l-O{^9Cc*sE`_^YB=X>GXP}tnlhh;gdYu zL#7M?M{zPaeb2npP%f)}T`{ewN5*M$$S(REwJ$)&wEyz@;yE^_`n%_ zd<(wyDezYY(#EDHBY(y};6y^L4kaxM(bp`z6%sNsk9oH(k$em$Bjfl_Hm!534H`3s zj*co#oW_+5%iJF3mpQOF;!;R|EymP?H5Z+T^RH}vot69{4vG|LkIHzgUqQMnaRwKG z8t|-^@kRF5*R$dj->(6VrscwO$sl13n3qYHb#SNL<1{j9{oIW;fzLGbTk0GPF32L3 z-);I+d^Ycsm|bA`^*n__77SK!Kz81-0vpNHL&w6ty!`wOPyQ?bi2)oeUNs+JMf@_f zz^dDdrvpyhi$z7S;O>*m72idWmZ6~DLK4A+N#|S9zp~)e-FrygP@^`Ky+dcgyH!*e zj+_(vKYvdDh!I%GZ1?>qFTWDxJe(Lo?M_w#k_jGGf|#xUHP2rEIX|j2$K$$*a+T%S+-Z$|880epjguHmAsJ`ES14fH)ivR!s literal 0 HcmV?d00001 diff --git a/assets/social-preview/social-preview.png b/assets/social-preview/social-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..0d5f38dc3e72f70770d89a4093650aecec25893b GIT binary patch literal 9688 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 literal 0 HcmV?d00001 diff --git a/cmd/codexgo/codex.go b/cmd/codexgo/codex.go new file mode 100644 index 0000000..c1a3586 --- /dev/null +++ b/cmd/codexgo/codex.go @@ -0,0 +1,29 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/bastean/codexgo/pkg/cmd/server" +) + +const cli = "codexgo" + +var port string + +func usage() { + fmt.Printf("Usage: %s [OPTIONS]\n", cli) + fmt.Printf("\nE.g.: %s -p 8080\n\n", cli) + flag.PrintDefaults() +} + +func main() { + flag.StringVar(&port, "p", os.Getenv("PORT"), "Port") + + flag.Usage = usage + + flag.Parse() + + server.Run(port) +} diff --git a/deployments/.env.demo b/deployments/.env.demo new file mode 100644 index 0000000..81142fa --- /dev/null +++ b/deployments/.env.demo @@ -0,0 +1,33 @@ +RABBITMQ_CONTAINER_NAME=codexgo-rabbitmq-demo +RABBITMQ_DEFAULT_USER=codexgo-demo +RABBITMQ_DEFAULT_PASS=codexgo-demo +RABBITMQ_AMQP_PORT=5672 +RABBITMQ_ADMIN_PORT=15672 +RABBITMQ_RESTART=on-failure + +MONGO_CONTAINER_NAME=codexgo-database-demo +MONGO_INITDB_ROOT_USERNAME=codexgo-demo +MONGO_INITDB_ROOT_PASSWORD=codexgo-demo +MONGO_INITDB_PORT=27017 +MONGO_RESTART=on-failure + +SERVER_CONTAINER_NAME=codexgo-server-demo +SERVER_IMAGE_TAG=codexgo-server-demo +SERVER_BUILD_TARGET=prod +SERVER_SMTP_HOST= +SERVER_SMTP_PORT= +SERVER_SMTP_USERNAME= +SERVER_SMTP_PASSWORD= +SERVER_JWT_SECRET_KEY=codexgo-demo +SERVER_COOKIE_SECRET_KEY=codexgo-demo +SERVER_COOKIE_SESSION_NAME=codexgo +SERVER_ALLOWED_HOSTS=localhost:8080 +SERVER_URL=http://localhost:8080 +SERVER_MODE=release +SERVER_PORT=8080 +SERVER_PROXY_PORT=8080 +SERVER_COMMAND=./run +SERVER_VOLUMES_APP=codexgo-server:/app/logs +SERVER_RESTART=on-failure + +DATABASE_VOLUME=codexgo-database-demo diff --git a/deployments/.env.demo.test.acceptance b/deployments/.env.demo.test.acceptance new file mode 100644 index 0000000..86dc92b --- /dev/null +++ b/deployments/.env.demo.test.acceptance @@ -0,0 +1 @@ +SERVER_COMMAND="make test-acceptance" diff --git a/deployments/.env.demo.test.integration b/deployments/.env.demo.test.integration new file mode 100644 index 0000000..ea2c348 --- /dev/null +++ b/deployments/.env.demo.test.integration @@ -0,0 +1 @@ +SERVER_COMMAND="make test-integration" diff --git a/deployments/.env.example b/deployments/.env.example new file mode 100644 index 0000000..98a5573 --- /dev/null +++ b/deployments/.env.example @@ -0,0 +1,33 @@ +RABBITMQ_CONTAINER_NAME= +RABBITMQ_DEFAULT_USER= +RABBITMQ_DEFAULT_PASS= +RABBITMQ_AMQP_PORT= +RABBITMQ_ADMIN_PORT= +RABBITMQ_RESTART= + +MONGO_CONTAINER_NAME= +MONGO_INITDB_ROOT_USERNAME= +MONGO_INITDB_ROOT_PASSWORD= +MONGO_INITDB_PORT= +MONGO_RESTART= + +SERVER_CONTAINER_NAME= +SERVER_IMAGE_TAG= +SERVER_BUILD_TARGET= +SERVER_SMTP_HOST= +SERVER_SMTP_PORT= +SERVER_SMTP_USERNAME= +SERVER_SMTP_PASSWORD= +SERVER_JWT_SECRET_KEY= +SERVER_COOKIE_SECRET_KEY= +SERVER_COOKIE_SESSION_NAME= +SERVER_ALLOWED_HOSTS= +SERVER_URL= +SERVER_MODE= +SERVER_PORT= +SERVER_PROXY_PORT= +SERVER_COMMAND= +SERVER_VOLUMES_APP= +SERVER_RESTART= + +DATABASE_VOLUME= diff --git a/deployments/.env.example.demo b/deployments/.env.example.demo new file mode 100644 index 0000000..21d75cf --- /dev/null +++ b/deployments/.env.example.demo @@ -0,0 +1,33 @@ +RABBITMQ_CONTAINER_NAME=codexgo-rabbitmq|-dev|-test|-prod +RABBITMQ_DEFAULT_USER=example|-dev|-test|-prod +RABBITMQ_DEFAULT_PASS=example|-dev|-test|-prod +RABBITMQ_AMQP_PORT=5672 +RABBITMQ_ADMIN_PORT=15672 +RABBITMQ_RESTART=on-failure|always + +MONGO_CONTAINER_NAME=codexgo-database|-dev|-test|-prod +MONGO_INITDB_ROOT_USERNAME=example|-dev|-test|-prod +MONGO_INITDB_ROOT_PASSWORD=example|-dev|-test|-prod +MONGO_INITDB_PORT=27017 +MONGO_RESTART=on-failure|always + +SERVER_CONTAINER_NAME=codexgo-server|-dev|-test|-prod +SERVER_IMAGE_TAG=codexgo-server|-dev|-test|-prod +SERVER_BUILD_TARGET=dev|test|prod +SERVER_SMTP_HOST=smtp.example.com +SERVER_SMTP_PORT=25|465|587|2525 +SERVER_SMTP_USERNAME=example|-dev|-test|-prod +SERVER_SMTP_PASSWORD=example|-dev|-test|-prod +SERVER_JWT_SECRET_KEY=example|-dev|-test|-prod +SERVER_COOKIE_SECRET_KEY=example|-dev|-test|-prod +SERVER_COOKIE_SESSION_NAME=example|-dev|-test|-prod +SERVER_ALLOWED_HOSTS=localhost:8080 +SERVER_URL=http://localhost:8080 +SERVER_MODE=debug|test|release +SERVER_PORT=8080 +SERVER_PROXY_PORT=8090|8080 +SERVER_COMMAND=air|'make tests'|./codexgo|./run +SERVER_VOLUMES_APP=..:/app|codexgo-server:/app/logs +SERVER_RESTART=on-failure|always + +DATABASE_VOLUME=codexgo-database|-dev|-test|-prod diff --git a/deployments/Dockerfile b/deployments/Dockerfile new file mode 100644 index 0000000..9a24fa3 --- /dev/null +++ b/deployments/Dockerfile @@ -0,0 +1,39 @@ +FROM golang:bookworm AS dev + +WORKDIR /app + +RUN apt update && apt upgrade -y + +RUN go install github.com/air-verse/air@latest + +RUN go install github.com/a-h/templ/cmd/templ@latest + +RUN apt install -y nodejs npm + +FROM dev AS test + +WORKDIR /app + +RUN go run github.com/playwright-community/playwright-go/cmd/playwright@latest install chromium --with-deps + +RUN npm i -g concurrently wait-on + +FROM dev AS build + +WORKDIR /app + +COPY . . + +RUN npm i -g prettier + +RUN make build + +FROM golang:bookworm AS prod + +WORKDIR /app + +COPY --from=build app/deployments/run.sh run + +RUN chmod +x run + +COPY --from=build app/build/codexgo . diff --git a/deployments/docker-compose.yml b/deployments/docker-compose.yml new file mode 100644 index 0000000..8e2226d --- /dev/null +++ b/deployments/docker-compose.yml @@ -0,0 +1,77 @@ +services: + broker: + container_name: ${RABBITMQ_CONTAINER_NAME} + image: rabbitmq:3-management + environment: + RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS} + ports: + - ${RABBITMQ_AMQP_PORT}:${RABBITMQ_AMQP_PORT} + - ${RABBITMQ_ADMIN_PORT}:${RABBITMQ_ADMIN_PORT} + restart: ${RABBITMQ_RESTART} + healthcheck: + test: rabbitmq-diagnostics -q status + interval: 12s + timeout: 12s + retries: 12 + + database: + container_name: ${MONGO_CONTAINER_NAME} + image: mongo:4.4 + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} + command: mongod --quiet --logpath /dev/null + ports: + - ${MONGO_INITDB_PORT}:${MONGO_INITDB_PORT} + volumes: + - codexgo-database:/data/db + restart: ${MONGO_RESTART} + healthcheck: + test: echo 'db.runCommand({serverStatus:1}).ok' | mongo admin -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD} --quiet | grep 1 + interval: 12s + timeout: 12s + retries: 12 + + server: + container_name: ${SERVER_CONTAINER_NAME} + image: ${SERVER_IMAGE_TAG} + build: + context: .. + dockerfile: deployments/Dockerfile + target: ${SERVER_BUILD_TARGET} + environment: + BROKER_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@broker:${RABBITMQ_AMQP_PORT}" + DATABASE_URI: "mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@database:${MONGO_INITDB_PORT}" + SMTP_HOST: ${SERVER_SMTP_HOST} + SMTP_PORT: ${SERVER_SMTP_PORT} + SMTP_USERNAME: ${SERVER_SMTP_USERNAME} + SMTP_PASSWORD: ${SERVER_SMTP_PASSWORD} + JWT_SECRET_KEY: ${SERVER_JWT_SECRET_KEY} + COOKIE_SECRET_KEY: ${SERVER_COOKIE_SECRET_KEY} + COOKIE_SESSION_NAME: ${SERVER_COOKIE_SESSION_NAME} + ALLOWED_HOSTS: ${SERVER_ALLOWED_HOSTS} + URL: ${SERVER_URL} + GIN_MODE: ${SERVER_MODE} + PORT: ${SERVER_PORT} + command: ${SERVER_COMMAND} + ports: + - ${SERVER_PORT}:${SERVER_PORT} + - ${SERVER_PROXY_PORT}:${SERVER_PROXY_PORT} + volumes: + - ${SERVER_VOLUMES_APP} + - codexgo-modules:/go/pkg/mod + restart: ${SERVER_RESTART} + depends_on: + broker: + condition: service_healthy + database: + condition: service_healthy + +volumes: + codexgo-database: + name: ${DATABASE_VOLUME} + codexgo-server: + name: codexgo-server + codexgo-modules: + name: codexgo-modules diff --git a/deployments/run.sh b/deployments/run.sh new file mode 100644 index 0000000..a12cfe0 --- /dev/null +++ b/deployments/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +logs=logs + +today=$(date -u +%d-%m-%Y) + +now=$(date -u +%H_%M_%S) + +log=$logs/$today/$now.log + +./codexgo |& tee $log diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8af0451 --- /dev/null +++ b/go.mod @@ -0,0 +1,78 @@ +module github.com/bastean/codexgo + +go 1.22 + +require ( + github.com/JGLTechnologies/gin-rate-limit v1.5.4 + github.com/a-h/templ v0.2.707 + github.com/brianvoe/gofakeit/v7 v7.0.4 + github.com/cucumber/godog v0.14.1 + github.com/gin-contrib/secure v1.1.0 + github.com/gin-contrib/sessions v1.0.1 + github.com/gin-gonic/gin v1.10.0 + github.com/go-playground/validator/v10 v10.22.0 + github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/google/uuid v1.6.0 + github.com/playwright-community/playwright-go v0.4401.1 + github.com/rabbitmq/amqp091-go v1.10.0 + github.com/stretchr/testify v1.9.0 + go.mongodb.org/mongo-driver v1.15.1 + golang.org/x/crypto v0.24.0 + golang.org/x/text v0.16.0 +) + +require ( + github.com/bytedance/sonic v1.11.9 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect + github.com/cucumber/messages/go/v21 v21.0.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gorilla/context v1.1.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.3.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-memdb v1.3.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/redis/go-redis/v9 v9.5.3 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2eb7db7 --- /dev/null +++ b/go.sum @@ -0,0 +1,236 @@ +github.com/JGLTechnologies/gin-rate-limit v1.5.4 h1:1hIaXIdGM9MZFZlXgjWJLpxaK0WHEa5MeloK49nmQsc= +github.com/JGLTechnologies/gin-rate-limit v1.5.4/go.mod h1:mGEhNzlHEg/Tk+KH/mKylZLTfDjACnx7MVYaAlj07eU= +github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U= +github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8= +github.com/brianvoe/gofakeit/v7 v7.0.4 h1:Mkxwz9jYg8Ad8NvT9HA27pCMZGFQo08MK6jD0QTKEww= +github.com/brianvoe/gofakeit/v7 v7.0.4/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg= +github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= +github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= +github.com/cucumber/godog v0.14.1 h1:HGZhcOyyfaKclHjJ+r/q93iaTJZLKYW6Tv3HkmUE6+M= +github.com/cucumber/godog v0.14.1/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= +github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= +github.com/gin-contrib/secure v1.1.0 h1:wy/psCWbgUBDCLH13KgB/m06NHXb1jczSTRp+H2hK7E= +github.com/gin-contrib/secure v1.1.0/go.mod h1:LtEfyy326NRwgkUq8ac6npf845L0L9B8yfEaLcxMHIc= +github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI= +github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= +github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= +github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/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.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +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.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +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-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/playwright-community/playwright-go v0.4401.1 h1:3EMTn9HUGETP3vjZLrVVNW+2xh+AtastOe7NHdT3fMs= +github.com/playwright-community/playwright-go v0.4401.1/go.mod h1:bpArn5TqNzmP0jroCgw4poSOG9gSeQg490iLqWAaa7w= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= +github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= +github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 h1:tBiBTKHnIjovYoLX/TPkcf+OjqqKGQrPtGT3Foz+Pgo= +github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76/go.mod h1:SQliXeA7Dhkt//vS29v3zpbEwoa+zb2Cn5xj5uO4K5U= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.15.1 h1:l+RvoUOoMXFmADTLfYDm7On9dRm7p4T80/lEQM+r7HU= +go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..693e5c2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,16813 @@ +{ + "name": "codexgo", + "version": "4.3.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codexgo", + "version": "4.3.0", + "license": "MIT", + "workspaces": [ + "pkg/cmd/*" + ], + "devDependencies": { + "@commitlint/cli": "19.3.0", + "@commitlint/config-conventional": "19.2.2", + "@release-it/bumper": "6.0.1", + "@release-it/conventional-changelog": "8.0.1", + "commitizen": "4.3.0", + "cz-conventional-changelog": "3.3.0", + "husky": "9.0.11", + "lint-staged": "15.2.7", + "npm-check-updates": "16.14.20", + "prettier": "3.3.2", + "release-it": "17.4.0" + }, + "engines": { + "node": ">=20", + "npm": ">=10" + } + }, + "node_modules/@actions/core": { + "version": "1.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/cli": { + "version": "19.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^19.3.0", + "@commitlint/lint": "^19.2.2", + "@commitlint/load": "^19.2.0", + "@commitlint/read": "^19.2.1", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/cli/node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@commitlint/cli/node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@commitlint/cli/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/npm-run-path": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@commitlint/cli/node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "19.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.0.3", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "19.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.0.3", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "19.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "19.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.0.3", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "19.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^19.2.2", + "@commitlint/parse": "^19.0.3", + "@commitlint/rules": "^19.0.3", + "@commitlint/types": "^19.0.3" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/execute-rule": "^19.0.0", + "@commitlint/resolve-extends": "^19.1.0", + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/message": { + "version": "19.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "19.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "19.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read/node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@commitlint/read/node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@commitlint/read/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@commitlint/read/node_modules/npm-run-path": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@commitlint/read/node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/types": "^19.0.3", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "19.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^19.0.3", + "@commitlint/message": "^19.0.0", + "@commitlint/to-lines": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules/node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@commitlint/rules/node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@commitlint/rules/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/npm-run-path": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@commitlint/rules/node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "19.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "19.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/top-level/node_modules/yocto-queue": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "19.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@hutson/parse-repository-url": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "dev": true, + "license": "ISC" + }, + "node_modules/@inquirer/figures": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.3.tgz", + "integrity": "sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/move-file/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.2.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@release-it/bumper": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "detect-indent": "7.0.1", + "fast-glob": "^3.3.2", + "ini": "^4.1.1", + "js-yaml": "^4.1.0", + "lodash-es": "^4.17.21", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "release-it": "^17.0.0" + } + }, + "node_modules/@release-it/bumper/node_modules/detect-indent": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@release-it/bumper/node_modules/ini": { + "version": "4.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@release-it/conventional-changelog": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "concat-stream": "^2.0.0", + "conventional-changelog": "^5.1.0", + "conventional-recommended-bump": "^9.0.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "release-it": "^17.0.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "1.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.2.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "1.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "1.0.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0", + "tuf-js": "^1.1.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/expect": { + "version": "1.20.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.11.24", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver-utils": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vinyl": { + "version": "2.0.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/add-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/any-shell-escape": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/anymatch/node_modules/braces": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/fill-range": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/is-number": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/micromatch": { + "version": "3.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/to-regex-range": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/archy": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-filter": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-map": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-differ": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-each": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/array-initial": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.map": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "1.5.2", + "dev": true, + "license": "MIT" + }, + "node_modules/async-done": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/async-each": { + "version": "1.0.6", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/async-retry": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/async-settle": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.19", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bach": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base": { + "version": "0.11.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/beeper": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/better-console": { + "version": "1.0.1", + "dev": true, + "license": "BSD", + "dependencies": { + "chalk": "^1.1.3", + "cli-table": "~0.3.1" + } + }, + "node_modules/better-console/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/better-console/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/better-console/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/better-console/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/better-console/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/binary-extensions": { + "version": "1.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/binaryextensions": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boxen": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "2.19.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacache": { + "version": "17.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.3.10", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cachedir": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "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.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001617", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "2.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/chokidar/node_modules/braces": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/fill-range": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/is-number": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/to-regex-range": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/clean-css": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table": { + "version": "0.3.11", + "dev": true, + "dependencies": { + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" + } + }, + "node_modules/cli-table3": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cloneable-readable/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/cloneable-readable/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-map": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commitizen": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cachedir": "2.3.0", + "cz-conventional-changelog": "3.3.0", + "dedent": "0.7.0", + "detect-indent": "6.1.0", + "find-node-modules": "^2.1.2", + "find-root": "1.1.0", + "fs-extra": "9.1.0", + "glob": "7.2.3", + "inquirer": "8.2.5", + "is-utf8": "^0.2.1", + "lodash": "4.17.21", + "minimist": "1.2.7", + "strip-bom": "4.0.0", + "strip-json-comments": "3.1.1" + }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-with-sourcemaps": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/configstore": { + "version": "6.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" + } + }, + "node_modules/configstore/node_modules/dot-prop": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/conventional-changelog": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^7.0.0", + "conventional-changelog-atom": "^4.0.0", + "conventional-changelog-codemirror": "^4.0.0", + "conventional-changelog-conventionalcommits": "^7.0.2", + "conventional-changelog-core": "^7.0.0", + "conventional-changelog-ember": "^4.0.0", + "conventional-changelog-eslint": "^5.0.0", + "conventional-changelog-express": "^4.0.0", + "conventional-changelog-jquery": "^5.0.0", + "conventional-changelog-jshint": "^4.0.0", + "conventional-changelog-preset-loader": "^4.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-atom": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-codemirror": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-core": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@hutson/parse-repository-url": "^5.0.0", + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^7.0.0", + "conventional-commits-parser": "^5.0.0", + "git-raw-commits": "^4.0.0", + "git-semver-tags": "^7.0.0", + "hosted-git-info": "^7.0.0", + "normalize-package-data": "^6.0.0", + "read-pkg": "^8.0.0", + "read-pkg-up": "^10.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-core/node_modules/find-up": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { + "version": "7.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/conventional-changelog-core/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/conventional-changelog-core/node_modules/lines-and-columns": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/conventional-changelog-core/node_modules/locate-path": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/lru-cache": { + "version": "10.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { + "version": "6.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-limit": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-locate": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/parse-json": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/parse-json/node_modules/type-fest": { + "version": "3.13.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/path-exists": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^6.0.0", + "parse-json": "^7.0.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^8.1.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/type-fest": { + "version": "4.9.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-core/node_modules/yocto-queue": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-ember": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-eslint": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-express": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-jquery": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^4.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^12.0.1", + "semver": "^7.5.2", + "split2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commit-types": { + "version": "3.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/conventional-commits-filter": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-recommended-bump": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-preset-loader": "^4.1.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "git-raw-commits": "^4.0.0", + "git-semver-tags": "^7.0.0", + "meow": "^12.0.1" + }, + "bin": { + "conventional-recommended-bump": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/convert-source-map": { + "version": "0.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-props": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "each-props": "^1.3.2", + "is-plain-object": "^5.0.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "^1.19.1" + }, + "engines": { + "node": ">=v16" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/css": { + "version": "2.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "node_modules/cz-conventional-changelog": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" + } + }, + "node_modules/d": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/dateformat": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-assign": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-assign/node_modules/is-obj": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-compare": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-resolution": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/del": { + "version": "6.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/del/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/deprecation": { + "version": "2.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/detect-file": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "1.0.8", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.0.2", + "dev": true, + "license": "BSD", + "dependencies": { + "readable-stream": "~1.1.9" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "0.10.31", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexify": { + "version": "3.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/each-props": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "node_modules/each-props/node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.763", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/errno": { + "version": "0.1.8", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/ext": { + "version": "1.7.0", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-memoize": { + "version": "2.5.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.16.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-node-modules": { + "version": "2.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fined/node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/first-chunk-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/first-chunk-stream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/first-chunk-stream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/first-chunk-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/first-chunk-stream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/flush-write-stream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/flush-write-stream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/flush-write-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/flush-write-stream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fomantic-ui": { + "version": "2.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@actions/core": "^1.6.0", + "@octokit/core": "^3.6.0", + "@octokit/rest": "^18.12.0", + "better-console": "^1.0.1", + "browserslist": "^4.21.4", + "del": "^6.1.1", + "extend": "^3.0.2", + "gulp": "^4.0.0", + "gulp-autoprefixer": "^8.0.0", + "gulp-chmod": "^2.0.0", + "gulp-clean-css": "^4.3.0", + "gulp-clone": "^2.0.1", + "gulp-concat": "^2.6.1", + "gulp-concat-css": "^3.1.0", + "gulp-concat-filenames": "^1.2.0", + "gulp-copy": "^4.0.0", + "gulp-dedupe": "^0.0.2", + "gulp-flatten": "^0.4.0", + "gulp-git": "^2.9.0", + "gulp-header": "^2.0.5", + "gulp-if": "^2.0.2", + "gulp-json-editor": "^2.4.3", + "gulp-less": "^5.0.0", + "gulp-plumber": "^1.1.0", + "gulp-print": "^5.0.0", + "gulp-rename": "^1.4.0", + "gulp-replace": "^1.0.0", + "gulp-rtlcss": "^2.0.0", + "gulp-tap": "^1.0.1", + "gulp-uglify": "^3.0.1", + "inquirer": "^8.2.0", + "jquery": "^3.4.0", + "less": "^3.12.0 || ^4.0.0", + "map-stream": "^0.1.0", + "merge-stream": "^2.0.0", + "mkdirp": "^1.0.4", + "normalize-path": "^3.0.0", + "replace-ext": "^1.0.0", + "require-dot-file": "^0.4.0", + "wrench-sui": "^0.0.3", + "yamljs": "^0.3.0" + }, + "engines": { + "node": ">=12", + "npm": ">=6.14.8" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/auth-token": { + "version": "2.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/core": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/endpoint": { + "version": "6.0.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/graphql": { + "version": "4.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fomantic-ui/node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/request": { + "version": "5.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/request-error": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/rest": { + "version": "18.12.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/fomantic-ui/node_modules/@octokit/types": { + "version": "6.41.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/fomantic-ui/node_modules/node-fetch": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-stream": { + "version": "0.0.4", + "dev": true, + "license": "BSD" + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fp-and-or": { + "version": "0.1.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-mkdirp-stream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-mkdirp-stream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-imports": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1", + "import-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-uri": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/fs-extra": { + "version": "11.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/git-semver-tags": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^12.0.1", + "semver": "^7.5.2" + }, + "bin": { + "git-semver-tags": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/git-up": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-ssh": "^1.4.0", + "parse-url": "^8.1.0" + } + }, + "node_modules/git-url-parse": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "git-up": "^7.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-stream": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/glob-stream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/glob-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/glob-stream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/glob-watcher": { + "version": "5.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "14.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glogg": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "13.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/gulp": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-autoprefixer": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "autoprefixer": "^10.2.6", + "fancy-log": "^1.3.3", + "plugin-error": "^1.0.1", + "postcss": "^8.3.0", + "through2": "^4.0.2", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "gulp": ">=4" + }, + "peerDependenciesMeta": { + "gulp": { + "optional": true + } + } + }, + "node_modules/gulp-chmod": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-assign": "^1.0.0", + "stat-mode": "^0.2.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-chmod/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-chmod/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-chmod/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-chmod/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-chmod/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-clean-css": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-css": "4.2.3", + "plugin-error": "1.0.1", + "through2": "3.0.1", + "vinyl-sourcemaps-apply": "0.2.1" + } + }, + "node_modules/gulp-clean-css/node_modules/through2": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-cli": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-cli/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/cliui": { + "version": "3.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/gulp-cli/node_modules/concat-stream": { + "version": "1.6.2", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/gulp-cli/node_modules/get-caller-file": { + "version": "1.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/gulp-cli/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-cli/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-cli/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-cli/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-cli/node_modules/string-width": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/wrap-ansi": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/y18n": { + "version": "3.2.2", + "dev": true, + "license": "ISC" + }, + "node_modules/gulp-cli/node_modules/yargs": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "node_modules/gulp-cli/node_modules/yargs-parser": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "node_modules/gulp-clone": { + "version": "2.0.1", + "dev": true, + "dependencies": { + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + } + }, + "node_modules/gulp-clone/node_modules/arr-diff": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clone/node_modules/arr-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clone/node_modules/array-slice": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clone/node_modules/extend-shallow": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clone/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-clone/node_modules/kind-of": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clone/node_modules/plugin-error": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clone/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-clone/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-clone/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-clone/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-concat": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-concat-css": { + "version": "3.1.0", + "dev": true, + "dependencies": { + "lodash.defaults": "^3.0.0", + "parse-import": "^2.0.0", + "plugin-error": "^0.1.2", + "rework": "~1.0.0", + "rework-import": "^2.0.0", + "rework-plugin-url": "^1.0.1", + "through2": "~1.1.1", + "vinyl": "^2.1.0" + } + }, + "node_modules/gulp-concat-css/node_modules/arr-diff": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-concat-css/node_modules/arr-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-concat-css/node_modules/array-slice": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-concat-css/node_modules/extend-shallow": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-concat-css/node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-concat-css/node_modules/kind-of": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-concat-css/node_modules/plugin-error": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-concat-css/node_modules/readable-stream": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gulp-concat-css/node_modules/string_decoder": { + "version": "0.10.31", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-concat-css/node_modules/through2": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": ">=1.1.13-1 <1.2.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/gulp-concat-filenames": { + "version": "1.2.0", + "dev": true, + "dependencies": { + "gulp-util": "3.x.x", + "through": "2.x.x" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-concat/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-concat/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-concat/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-concat/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-concat/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-copy": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "gulp": "^4.0.0", + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + } + }, + "node_modules/gulp-copy/node_modules/arr-diff": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-copy/node_modules/arr-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-copy/node_modules/array-slice": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-copy/node_modules/extend-shallow": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-copy/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-copy/node_modules/kind-of": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-copy/node_modules/plugin-error": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-copy/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-copy/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-copy/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-copy/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-dedupe": { + "version": "0.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "~1.0.2", + "diff": "~1.0.8", + "gulp-util": "~3.0.1", + "lodash.defaults": "~2.4.1", + "through": "~2.3.6" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/gulp-dedupe/node_modules/lodash.defaults": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "node_modules/gulp-dedupe/node_modules/lodash.keys": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "node_modules/gulp-flatten": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "plugin-error": "^0.1.2", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/gulp-flatten/node_modules/arr-diff": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-flatten/node_modules/arr-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-flatten/node_modules/array-slice": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-flatten/node_modules/extend-shallow": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-flatten/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-flatten/node_modules/kind-of": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-flatten/node_modules/plugin-error": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-flatten/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-flatten/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-flatten/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-flatten/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-git": { + "version": "2.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "any-shell-escape": "^0.1.1", + "fancy-log": "^1.3.2", + "lodash.template": "^4.4.0", + "plugin-error": "^1.0.1", + "require-dir": "^1.0.0", + "strip-bom-stream": "^3.0.0", + "through2": "^2.0.3", + "vinyl": "^2.0.1" + }, + "engines": { + "node": ">= 0.9.0" + } + }, + "node_modules/gulp-git/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-git/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-git/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-git/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-git/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-header": { + "version": "2.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "concat-with-sourcemaps": "^1.1.0", + "lodash.template": "^4.5.0", + "map-stream": "0.0.7", + "through2": "^2.0.0" + } + }, + "node_modules/gulp-header/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-header/node_modules/map-stream": { + "version": "0.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-header/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-header/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-header/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-header/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-if": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "gulp-match": "^1.0.3", + "ternary-stream": "^2.0.1", + "through2": "^2.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/gulp-if/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-if/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-if/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-if/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-if/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-json-editor": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "detect-indent": "^6.1.0", + "js-beautify": "^1.14.11", + "plugin-error": "^2.0.1", + "through2": "^4.0.2" + } + }, + "node_modules/gulp-json-editor/node_modules/plugin-error": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp-less": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "less": "^3.7.1 || ^4.0.0", + "object-assign": "^4.0.1", + "plugin-error": "^1.0.0", + "replace-ext": "^2.0.0", + "through2": "^4.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-less/node_modules/replace-ext": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/gulp-match": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.3" + } + }, + "node_modules/gulp-plumber": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^1.1.3", + "fancy-log": "^1.3.2", + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + }, + "engines": { + "node": ">=0.10", + "npm": ">=1.2.10" + } + }, + "node_modules/gulp-plumber/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/arr-diff": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/arr-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/array-slice": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/extend-shallow": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-plumber/node_modules/kind-of": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/plugin-error": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-plumber/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-plumber/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-plumber/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/gulp-plumber/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-print": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^3.2.4", + "fancy-log": "^1.3.3", + "map-stream": "0.0.7", + "vinyl": "^2.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-print/node_modules/ansi-colors": { + "version": "3.2.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-print/node_modules/map-stream": { + "version": "0.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-rename": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-replace": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/vinyl": "^2.0.4", + "istextorbinary": "^3.0.0", + "replacestream": "^4.0.3", + "yargs-parser": ">=5.0.0-security.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-rtlcss": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "plugin-error": "^1.0.1", + "rtlcss": "^3.5.0", + "through2": "^2.0.5", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "node_modules/gulp-rtlcss/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-rtlcss/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-rtlcss/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-rtlcss/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-rtlcss/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-tap": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.3" + } + }, + "node_modules/gulp-tap/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-tap/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-tap/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-tap/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-tap/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-uglify": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-each": "^1.0.1", + "extend-shallow": "^3.0.2", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "isobject": "^3.0.1", + "make-error-cause": "^1.1.1", + "safe-buffer": "^5.1.2", + "through2": "^2.0.0", + "uglify-js": "^3.0.5", + "vinyl-sourcemaps-apply": "^0.2.0" + } + }, + "node_modules/gulp-uglify/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-uglify/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-uglify/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-uglify/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-uglify/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-util": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/gulp-util/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/clone-stats": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-util/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-util/node_modules/lodash.template": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "node_modules/gulp-util/node_modules/lodash.templatesettings": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "node_modules/gulp-util/node_modules/object-assign": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-util/node_modules/replace-ext": { + "version": "0.0.1", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gulp-util/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-util/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-util/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/gulp-util/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-util/node_modules/vinyl": { + "version": "0.5.3", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/gulplog": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-gulplog": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/has-value": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-yarn": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "dev": true, + "license": "ISC" + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/http2-wrapper/node_modules/quick-lru": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/husky": { + "version": "9.0.11", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/import-regex": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ip-regex": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "dev": true, + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable/node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ci": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally/node_modules/global-dirs": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally/node_modules/ini": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-map": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ssh": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-windows": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-yarn-global": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/istextorbinary": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binaryextensions": "^2.2.0", + "textextensions": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/iterate-value": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jquery": { + "version": "3.7.1", + "dev": true, + "license": "MIT" + }, + "node_modules/js-beautify": { + "version": "1.15.1", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.3.15", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-helpfulerror": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "jju": "^1.1.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonlines": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/just-debounce": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/last-run": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/latest-version": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/less": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/liftoff": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/liftoff/node_modules/braces": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/fill-range": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/findup-sync": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/liftoff/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/is-number": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/micromatch": { + "version": "3.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/to-regex-range": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "15.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.4", + "execa": "~8.0.1", + "lilconfig": "~3.1.1", + "listr2": "~8.2.1", + "micromatch": "~4.0.7", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.4.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2": { + "version": "8.2.1", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._baseassign": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "node_modules/lodash._basecopy": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._basetostring": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._basevalues": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._bindcallback": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._createassigner": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._bindcallback": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash.restparam": "^3.0.0" + } + }, + "node_modules/lodash._getnative": { + "version": "3.9.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._isiterateecall": { + "version": "3.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._isnative": { + "version": "2.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._objecttypes": { + "version": "2.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._reescape": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._reevaluate": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._root": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._shimkeys": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._objecttypes": "~2.4.1" + } + }, + "node_modules/lodash.assign": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._baseassign": "^3.0.0", + "lodash._createassigner": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.assign": "^3.0.0", + "lodash.restparam": "^3.0.0" + } + }, + "node_modules/lodash.escape": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._root": "^3.0.0" + } + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isarray": { + "version": "3.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isobject": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._objecttypes": "~2.4.1" + } + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.keys": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.restparam": { + "version": "3.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/cli-cursor": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/longest": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/macos-release": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/make-error-cause": { + "version": "1.2.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "make-error": "^1.2.0" + } + }, + "node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/make-iterator/node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "dev": true + }, + "node_modules/map-visit": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/findup-sync": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/matchdep/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/is-glob": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/is-number": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/micromatch": { + "version": "3.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/to-regex-range": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { + "version": "2.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/multipipe": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "0.0.2" + } + }, + "node_modules/mute-stdout": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "license": "ISC" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/needle": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/netmask": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/new-github-release-url": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^2.5.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/new-github-release-url/node_modules/type-fest": { + "version": "2.19.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "2.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/cacache": { + "version": "16.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-gyp/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-gyp/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-gyp/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "10.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/node-gyp/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-gyp/node_modules/ssri": { + "version": "9.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/unique-filename": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/unique-slug": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-check-updates": { + "version": "16.14.20", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/semver-utils": "^1.1.1", + "chalk": "^5.3.0", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "fast-memoize": "^2.5.2", + "find-up": "5.0.0", + "fp-and-or": "^0.1.4", + "get-stdin": "^8.0.0", + "globby": "^11.0.4", + "hosted-git-info": "^5.1.0", + "ini": "^4.1.1", + "js-yaml": "^4.1.0", + "json-parse-helpfulerror": "^1.0.3", + "jsonlines": "^0.1.1", + "lodash": "^4.17.21", + "make-fetch-happen": "^11.1.1", + "minimatch": "^9.0.3", + "p-map": "^4.0.0", + "pacote": "15.2.0", + "parse-github-url": "^1.0.2", + "progress": "^2.0.3", + "prompts-ncu": "^3.0.0", + "rc-config-loader": "^4.1.3", + "remote-git-tags": "^3.0.0", + "rimraf": "^5.0.5", + "semver": "^7.5.4", + "semver-utils": "^1.1.4", + "source-map-support": "^0.5.21", + "spawn-please": "^2.0.2", + "strip-ansi": "^7.1.0", + "strip-json-comments": "^5.0.1", + "untildify": "^4.0.0", + "update-notifier": "^6.0.2" + }, + "bin": { + "ncu": "build/src/bin/cli.js", + "npm-check-updates": "build/src/bin/cli.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/npm-check-updates/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm-check-updates/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-check-updates/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm-check-updates/node_modules/commander": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/npm-check-updates/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-check-updates/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-check-updates/node_modules/ini": { + "version": "4.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-check-updates/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-check-updates/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-check-updates/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-check-updates/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm-check-updates/node_modules/strip-json-comments": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-check-updates/node_modules/update-notifier": { + "version": "6.0.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", + "is-installed-globally": "^0.4.0", + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "10.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "6.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-packlist": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "14.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.defaults": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.reduce": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ordered-read-streams/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/ordered-read-streams/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-name": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "macos-release": "^3.1.0", + "windows-release": "^5.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "12.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/pacote": { + "version": "15.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^5.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.3.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-github-url": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "bin": { + "parse-github-url": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-import": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-imports": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-path": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^2.0.0" + } + }, + "node_modules/parse-url": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-path": "^7.0.0" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-root": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/proc-log": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/promise.allsettled": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.map": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "iterate-value": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/prompts-ncu": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.1", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/protocols": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/prr": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/pump": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-goat": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "10.3.10", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "6.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "5.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/readdirp/node_modules/braces": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/fill-range": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/is-number": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp/node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/micromatch": { + "version": "3.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp/node_modules/to-regex-range": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/registry-auth-token": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/release-it/-/release-it-17.4.0.tgz", + "integrity": "sha512-tYnk1tS530TLQtV8UQ+6OCiV3opVtkgwmLOpjXeV63ZtlZpSAGLZCXrA/I6ywiYKcEQWxW8WV7YJQvdxxGNZSg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/webpro" + } + ], + "license": "MIT", + "dependencies": { + "@iarna/toml": "2.2.5", + "@octokit/rest": "20.1.1", + "async-retry": "1.3.3", + "chalk": "5.3.0", + "cosmiconfig": "9.0.0", + "execa": "8.0.1", + "git-url-parse": "14.0.0", + "globby": "14.0.1", + "got": "13.0.0", + "inquirer": "9.2.23", + "is-ci": "3.0.1", + "issue-parser": "7.0.1", + "lodash": "4.17.21", + "mime-types": "2.1.35", + "new-github-release-url": "2.0.0", + "node-fetch": "3.3.2", + "open": "10.1.0", + "ora": "8.0.1", + "os-name": "5.1.0", + "promise.allsettled": "1.0.7", + "proxy-agent": "6.4.0", + "semver": "7.6.2", + "shelljs": "0.8.5", + "update-notifier": "7.0.0", + "url-join": "5.0.0", + "wildcard-match": "5.1.3", + "yargs-parser": "21.1.1" + }, + "bin": { + "release-it": "bin/release-it.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || ^22.0.0" + } + }, + "node_modules/release-it/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/release-it/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/release-it/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/release-it/node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/release-it/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/release-it/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/release-it/node_modules/emoji-regex": { + "version": "10.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/release-it/node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/release-it/node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/release-it/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/release-it/node_modules/inquirer": { + "version": "9.2.23", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.23.tgz", + "integrity": "sha512-kod5s+FBPIDM2xiy9fu+6wdU/SkK5le5GS9lh4FEBjBHqiMgD9lLFbCbuqFNAjNL2ZOy9Wd9F694IOzN9pZHBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.3", + "@ljharb/through": "^2.3.13", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/release-it/node_modules/inquirer/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/inquirer/node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/release-it/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/release-it/node_modules/npm-run-path": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/cli-cursor": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/is-interactive": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/string-width": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/release-it/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/restore-cursor": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/release-it/node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/release-it/node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/release-it/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/release-it/node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/release-it/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/release-it/node_modules/url-join": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/release-it/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/remote-git-tags": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-bom-stream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/remove-bom-stream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/remove-bom-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/remove-bom-stream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/remove-bom-stream/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-homedir": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replacestream": { + "version": "4.0.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/replacestream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/replacestream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/replacestream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/replacestream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/require-dir": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-dot-file": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rework": { + "version": "1.0.1", + "dev": true, + "dependencies": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + } + }, + "node_modules/rework-import": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "css": "^2.0.0", + "globby": "^2.0.0", + "parse-import": "^2.0.0", + "url-regex": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rework-import/node_modules/array-union": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rework-import/node_modules/glob": { + "version": "5.0.15", + "dev": true, + "license": "ISC", + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rework-import/node_modules/globby": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "async": "^1.2.1", + "glob": "^5.0.3", + "object-assign": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rework-import/node_modules/object-assign": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rework-plugin-function": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "rework-visit": "^1.0.0" + } + }, + "node_modules/rework-plugin-url": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "rework-plugin-function": "^1.0.0" + } + }, + "node_modules/rework-visit": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "5.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.3.10", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rtlcss": { + "version": "3.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.3.0", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/semver": { + "version": "7.6.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-greatest-satisfied-range": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/semver-utils": { + "version": "1.1.4", + "dev": true, + "license": "APACHEv2" + }, + "node_modules/server": { + "resolved": "pkg/cmd/server", + "link": true + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/sigstore": { + "version": "1.9.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks/node_modules/ip": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/sparkles": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/spawn-please": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split-string": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "10.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/stat-mode": { + "version": "0.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/static-extend": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-buf": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "first-chunk-stream": "^2.0.0", + "strip-bom-buf": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sver-compat": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/tar": { + "version": "6.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ternary-stream": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.5.0", + "fork-stream": "^0.0.4", + "merge-stream": "^1.0.0", + "through2": "^2.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ternary-stream/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ternary-stream/node_modules/merge-stream": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/ternary-stream/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/ternary-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/ternary-stream/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/ternary-stream/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/textextensions": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/through2-filter/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/through2-filter/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2-filter/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/through2-filter/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/through2-filter/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-through": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/to-through/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/to-through/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/to-through/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/to-through/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/to-through/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.6.2", + "dev": true, + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "1.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "1.0.4", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type": { + "version": "2.7.2", + "dev": true, + "license": "ISC" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/undertaker": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker-registry": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-stream": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/untildify": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.15", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-notifier": { + "version": "7.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^7.1.1", + "chalk": "^5.3.0", + "configstore": "^6.0.0", + "import-lazy": "^4.0.0", + "is-in-ci": "^0.1.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^6.0.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.5.4", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/url-regex": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/use": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8flags": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/value-or-function": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vinyl-fs/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/vinyl-fs/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/vinyl-fs/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/vinyl-fs/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/convert-source-map": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl/node_modules/clone": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wildcard-match": { + "version": "5.1.3", + "dev": true, + "license": "ISC" + }, + "node_modules/windows-release": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/wrench-sui": { + "version": "0.0.3", + "dev": true, + "engines": { + "node": ">=0.1.97" + } + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.4.2", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yamljs": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "bin": { + "json2yaml": "bin/json2yaml", + "yaml2json": "bin/yaml2json" + } + }, + "node_modules/yamljs/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "pkg/cmd/server": { + "devDependencies": { + "fomantic-ui": "2.9.3", + "jquery": "3.7.1", + "lodash": "4.17.21" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b48bfdc --- /dev/null +++ b/package.json @@ -0,0 +1,138 @@ +{ + "private": true, + "name": "codexgo", + "version": "4.3.0", + "description": "codexGO", + "author": "Bastean ", + "license": "MIT", + "homepage": "https://github.com/bastean/codexgo#readme", + "repository": { + "type": "git", + "url": "https://github.com/bastean/codexgo.git" + }, + "bugs": { + "url": "https://github.com/bastean/codexgo/issues" + }, + "engines": { + "node": ">=20", + "npm": ">=10" + }, + "workspaces": [ + "pkg/cmd/*" + ], + "devDependencies": { + "@commitlint/cli": "19.3.0", + "@commitlint/config-conventional": "19.2.2", + "@release-it/bumper": "6.0.1", + "@release-it/conventional-changelog": "8.0.1", + "commitizen": "4.3.0", + "cz-conventional-changelog": "3.3.0", + "husky": "9.0.11", + "lint-staged": "15.2.7", + "npm-check-updates": "16.14.20", + "prettier": "3.3.2", + "release-it": "17.4.0" + }, + "lint-staged": { + "**/*": [ + "trufflehog filesystem --no-update", + "prettier --ignore-unknown --write" + ], + "**/*.go": [ + "gofmt -l -s -w" + ] + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "release-it": { + "git": { + "requireBranch": "main", + "commitMessage": "chore(release): v${version}", + "tagAnnotation": "codexgo ${version}", + "tagName": "v${version}" + }, + "github": { + "release": true, + "releaseName": "v${version}" + }, + "plugins": { + "@release-it/conventional-changelog": { + "preset": { + "name": "conventionalcommits", + "types": [ + { + "type": "build", + "section": "Builds" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "ci", + "section": "Continuous Integration" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "feat", + "section": "New Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "test", + "section": "Tests" + } + ] + }, + "infile": "CHANGELOG.md", + "header": "# Changelog" + }, + "@release-it/bumper": { + "out": [ + "pkg/**/manifest.json" + ] + } + }, + "hooks": { + "before:init": [ + "make lint-check", + "make test-unit" + ], + "before:release": [ + "make lint", + "git add . --update" + ], + "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}" + } + } +} diff --git a/pkg/cmd/server/component/layout/index.templ b/pkg/cmd/server/component/layout/index.templ new file mode 100644 index 0000000..efc4405 --- /dev/null +++ b/pkg/cmd/server/component/layout/index.templ @@ -0,0 +1,71 @@ +package layout + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/component/script/fomantic" + "github.com/bastean/codexgo/pkg/cmd/server/component/script/storage" + "github.com/bastean/codexgo/pkg/cmd/server/component/script/jquery" + "github.com/bastean/codexgo/pkg/cmd/server/component/script" +) + +templ Index(headScripts script.Head, bodyScripts script.Body) { + + + + + + + + + + + + + + + codexGO + + + + + + + + + + + + + + @storage.Init() + + @jquery.Init() + + @fomantic.Init() + + for _, headScript := range headScripts { + @headScript + } + + +

+ for _, bodyScript := range bodyScripts { + @bodyScript + } + + +} diff --git a/pkg/cmd/server/component/page/dashboard/form.delete.templ b/pkg/cmd/server/component/page/dashboard/form.delete.templ new file mode 100644 index 0000000..a14ce25 --- /dev/null +++ b/pkg/cmd/server/component/page/dashboard/form.delete.templ @@ -0,0 +1,88 @@ +package dashboard + +var DeleteFormTagId = "delete" + +script DeleteFormInit(formTagId string) { + $(`#${formTagId}`) + .form({ + on: "blur", + inline : true, + preventLeaving: true, + keyboardShortcuts: false, + fields: { + Password: { + rules: [ + { + type: "size[8..64]" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + }, + ConfirmPassword: { + rules: [ + { + type: "match[Password]" + } + ] + } + } + }) + .api({ + action: "delete user", + method: "DELETE", + onSuccess: function(response, element, xhr) { + $.toast({ + class: "success", + message: response.message, + showProgress: "top", + }); + + _.delay(function() { + Storage.Clear(); + window.location.replace("/"); + }, 1000); + }, + onFailure: function(response, element, xhr) { + $.toast({ + class: "error", + message: response.message, + showProgress: "top" + }); + } + }) + ; +} + +script DeleteFormShow() { + $(".ui.mini.modal").modal("show"); +} + +templ DeleteForm() { + + @DeleteFormInit(DeleteFormTagId) +} diff --git a/pkg/cmd/server/component/page/dashboard/form.update.templ b/pkg/cmd/server/component/page/dashboard/form.update.templ new file mode 100644 index 0000000..a50679d --- /dev/null +++ b/pkg/cmd/server/component/page/dashboard/form.update.templ @@ -0,0 +1,154 @@ +package dashboard + +var UpdateFormTagId = "update" + +script UpdateFormInit(formTagId string) { + $(`#${formTagId}`) + .form({ + on: "blur", + inline : true, + preventLeaving: true, + keyboardShortcuts: false, + fields: { + Email: { + optional : true, + rules: [ + { + type: "email" + } + ] + }, + Username: { + optional : true, + rules: [ + { + type: "size[2..20]" + }, + { + type: "regExp[/^[A-Za-z0-9]+$/]", + prompt: "{name} must be alphanumeric only" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + }, + UpdatedPassword: { + optional : true, + rules: [ + { + type: "size[8..64]" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + }, + ConfirmPassword: { + depends : "UpdatedPassword", + rules: [ + { + type: "match[UpdatedPassword]" + } + ] + }, + Password: { + rules: [ + { + type: "size[8..64]" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + } + } + }) + .api({ + action: "update user", + method: "PATCH", + onSuccess: function(response, element, xhr) { + $.toast({ + class: "success", + message: response.message, + showProgress: "top", + }); + + _.delay(function() { + window.location.replace("/dashboard"); + }, 1000); + }, + onFailure: function(response, element, xhr) { + $.toast({ + class: "error", + message: response.message, + showProgress: "top" + }); + } + }) + ; +} + +templ UpdateForm(email, username string) { +
+

+ Account settings +

+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ @UpdateFormInit(UpdateFormTagId) +} diff --git a/pkg/cmd/server/component/page/dashboard/page.dashboard.templ b/pkg/cmd/server/component/page/dashboard/page.dashboard.templ new file mode 100644 index 0000000..f2b6e16 --- /dev/null +++ b/pkg/cmd/server/component/page/dashboard/page.dashboard.templ @@ -0,0 +1,112 @@ +package dashboard + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/component/layout" + "github.com/bastean/codexgo/pkg/cmd/server/component/script" + "github.com/bastean/codexgo/pkg/cmd/server/service/user" +) + +script PageInit() { + $(".ui.container") + .transition("fade in", "3s") + ; + + $(".ui.menu .right .dropdown") + .dropdown() + ; + + $(".ui.menu .right .header .icon") + .popup() + ; + + $(".ui.blue.nag") + .nag({ + key: "account-confirmation", + value: true + }) + ; + + $(".ui.green.nag") + .nag({ + key: "account-confirmed", + value: true + }) + ; +} + +script Logout() { + Storage.ClearSession(); + window.location.replace("/"); +} + +templ Page(user *user.ReadResponse) { + @layout.Index(script.Head{}, script.Body{}) { +
+
+ +
+
+ @UpdateForm(user.Email, user.Username) + @DeleteForm() +
+ if !user.Verified { +
+
+ + Account Confirmation +
+
Link sent. Please check your inbox
+ +
+ } else { +
+
+ + Account Confirmed +
+ +
+ } +
+ @PageInit() + } +} diff --git a/pkg/cmd/server/component/page/home/form.login.templ b/pkg/cmd/server/component/page/home/form.login.templ new file mode 100644 index 0000000..bf00407 --- /dev/null +++ b/pkg/cmd/server/component/page/home/form.login.templ @@ -0,0 +1,82 @@ +package home + +var LoginFormTagId = "login" + +script LoginFormInit(formTagId string) { + $(`#${formTagId}`) + .form({ + on: "blur", + inline : true, + preventLeaving: true, + keyboardShortcuts: false, + fields: { + Email: { + rules: [ + { + type: "email" + } + ] + }, + Password: { + rules: [ + { + type: "size[8..64]" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + } + } + }) + .api({ + action: "login user", + method: "POST", + onSuccess: function(response, element, xhr) { + $.toast({ + class: "success", + message: response.message, + showProgress: "top", + }); + + _.delay(function() { + window.location.replace("/dashboard"); + }, 1000); + }, + onFailure: function(response, element, xhr) { + $.toast({ + class: "error", + message: response.message, + showProgress: "top" + }); + } + }) + ; +} + +templ LoginForm() { +
+

+ Sign in to your account +
Don't have an account? Sign up
+

+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ @LoginFormInit(LoginFormTagId) +} diff --git a/pkg/cmd/server/component/page/home/form.register.templ b/pkg/cmd/server/component/page/home/form.register.templ new file mode 100644 index 0000000..e188bcf --- /dev/null +++ b/pkg/cmd/server/component/page/home/form.register.templ @@ -0,0 +1,148 @@ +package home + +var RegisterFormTagId = "register" + +script RegisterFormInit(formTagId, loginTabTagId string) { + $(`#${formTagId}`) + .form({ + on: "blur", + inline : true, + preventLeaving: true, + keyboardShortcuts: false, + fields: { + Email: { + rules: [ + { + type: "email" + } + ] + }, + Username: { + rules: [ + { + type: "size[2..20]" + }, + { + type: "regExp[/^[A-Za-z0-9]+$/]", + prompt: "{name} must be alphanumeric only" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + }, + Password: { + rules: [ + { + type: "size[8..64]" + }, + { + type: "regExp[/^.*[^0-9].*$/]", + prompt: "{name} cannot be only numbers" + } + ] + }, + ConfirmPassword: { + rules: [ + { + type: "match[Password]" + } + ] + }, + Terms: { + rules: [ + { + type: "checked", + prompt: "Terms & Conditions must be checked" + } + ] + } + } + }) + .api({ + action: "create user", + method: "PUT", + beforeSend: function(settings) { + settings.data.Id = crypto.randomUUID(); + + settings.data = JSON.stringify(settings.data); + + return settings; + }, + onSuccess: function(response, element, xhr) { + $.toast({ + class: "success", + message: response.message, + showProgress: "top", + }); + + _.delay(function() { + $.tab("change tab", loginTabTagId); + $(`#${formTagId}`).form("reset"); + }, 1000); + }, + onFailure: function(response, element, xhr) { + $.toast({ + class: "error", + message: response.message, + showProgress: "top" + }); + } + }) + ; +} + +script ShowTerms(modalTagId string) { + $(`#${modalTagId}`).modal("show") +} + +templ RegisterForm() { +
+

+ Create an account +
Already have an account? Sign in
+

+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+ + +
+
+ +
+
+
+ @RegisterFormInit(RegisterFormTagId, LoginTabTagId) +} diff --git a/pkg/cmd/server/component/page/home/modal.terms.templ b/pkg/cmd/server/component/page/home/modal.terms.templ new file mode 100644 index 0000000..c2a9a6a --- /dev/null +++ b/pkg/cmd/server/component/page/home/modal.terms.templ @@ -0,0 +1,28 @@ +package home + +var TermsModalTagId = "terms" + +templ TermsModal() { + +} diff --git a/pkg/cmd/server/component/page/home/page.home.templ b/pkg/cmd/server/component/page/home/page.home.templ new file mode 100644 index 0000000..b2615ca --- /dev/null +++ b/pkg/cmd/server/component/page/home/page.home.templ @@ -0,0 +1,71 @@ +package home + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/component/layout" + "github.com/bastean/codexgo/pkg/cmd/server/component/script" +) + +var RegisterTabTagId = "tab-register" +var LoginTabTagId = "tab-login" + +script PageInit() { + $(".ui.container") + .transition("fade in", "3s") + ; + + $(".ui.container .column .menu .right .item") + .tab({ + context: ".container" + }) + ; + + $(".ui.cookie.nag") + .nag({ + key: "accepts-cookies", + value: true + }) + ; +} + +script ShowTab(tabTagId string) { + $(`.ui.menu .right .item[data-tab=${tabTagId}]`).trigger("click"); +} + +templ Page() { + @layout.Index(script.Head{}, script.Body{}) { +
+
+ +
+
+
+ @RegisterForm() + @TermsModal() +
+
+ @LoginForm() +
+
+ +
+ @PageInit() + } +} diff --git a/pkg/cmd/server/component/script/body.templ b/pkg/cmd/server/component/script/body.templ new file mode 100644 index 0000000..ef771a8 --- /dev/null +++ b/pkg/cmd/server/component/script/body.templ @@ -0,0 +1,3 @@ +package script + +type Body []templ.ComponentScript diff --git a/pkg/cmd/server/component/script/fomantic/init.templ b/pkg/cmd/server/component/script/fomantic/init.templ new file mode 100644 index 0000000..0670486 --- /dev/null +++ b/pkg/cmd/server/component/script/fomantic/init.templ @@ -0,0 +1,27 @@ +package fomantic + +script Init() { + $.api.settings.api = { + "create user" : "/", + "login user" : "/", + "update user" : "/dashboard", + "delete user" : "/dashboard", + }; + + $.api.settings.serializeForm = true; + + $.api.settings.contentType = "application/json; charset=UTF-8"; + + $.api.settings.beforeSend = function(settings) { + settings.data = JSON.stringify(settings.data); + return settings; + }; + + $.api.settings.successTest = function(response) { + if(response && response.success) { + return response.success; + } + + return false; + }; +} diff --git a/pkg/cmd/server/component/script/head.templ b/pkg/cmd/server/component/script/head.templ new file mode 100644 index 0000000..2887a0a --- /dev/null +++ b/pkg/cmd/server/component/script/head.templ @@ -0,0 +1,3 @@ +package script + +type Head []templ.ComponentScript diff --git a/pkg/cmd/server/component/script/jquery/init.templ b/pkg/cmd/server/component/script/jquery/init.templ new file mode 100644 index 0000000..9ba5b0e --- /dev/null +++ b/pkg/cmd/server/component/script/jquery/init.templ @@ -0,0 +1,6 @@ +package jquery + +script Init() { + //? $(document).ready(handler) + $(() => {}); +} diff --git a/pkg/cmd/server/component/script/storage/storage.templ b/pkg/cmd/server/component/script/storage/storage.templ new file mode 100644 index 0000000..2be6fc0 --- /dev/null +++ b/pkg/cmd/server/component/script/storage/storage.templ @@ -0,0 +1,57 @@ +package storage + +script Init() { + const Storage = { + MasterKey: "codexgo", + Key: {}, + Init() { + let storage = localStorage.getItem(this.MasterKey); + + if (storage == null) { + localStorage.setItem(this.MasterKey, JSON.stringify({})); + } + }, + Put(key, value) { + let storage = localStorage.getItem(this.MasterKey); + + storage = JSON.parse(storage) + + storage[key] = value; + + localStorage.setItem(this.MasterKey, JSON.stringify(storage)); + }, + Get(key) { + let storage = localStorage.getItem(this.MasterKey); + + storage = JSON.parse(storage) + + return _.get(storage, key, null); + }, + Delete(key) { + let storage = localStorage.getItem(this.MasterKey); + + storage = JSON.parse(storage) + + delete storage[key]; + + localStorage.setItem(this.MasterKey, JSON.stringify(storage)); + }, + async ClearSession() { + localStorage.removeItem(this.MasterKey); + cookieStore.delete(this.MasterKey); + }, + async Clear() { + localStorage.clear(); + + let cookies = await cookieStore.getAll(); + + _.each(cookies, function(cookie) { + cookieStore.delete(cookie); + }); + } + } + + Storage.Init(); + + window.Storage = Storage +} diff --git a/pkg/cmd/server/features/page/default.feature b/pkg/cmd/server/features/page/default.feature new file mode 100644 index 0000000..64b2799 --- /dev/null +++ b/pkg/cmd/server/features/page/default.feature @@ -0,0 +1,6 @@ +Feature: Default Redirect + + Scenario: Check the correct redirect for not found page + Given I am on /non-existing page + Then redirect me to / page + And the page title should be codexGO diff --git a/pkg/cmd/server/features/user/create.feature b/pkg/cmd/server/features/user/create.feature new file mode 100644 index 0000000..0fa6a26 --- /dev/null +++ b/pkg/cmd/server/features/user/create.feature @@ -0,0 +1,21 @@ +Feature: Create a new User account + + Scenario: Create a valid non existing account + Given I am on / page + Then I fill the Email with create@example.com + * I fill the Username with create + * I fill the Password with create@example + * I fill the Confirm Password with create@example + * I check the I agree to the terms and conditions + * I click the Sign up button + And I see account created notification + + Scenario: Create already existing account + Given I am on / page + Then I fill the Email with create@example.com + * I fill the Username with create + * I fill the Password with create@example + * I fill the Confirm Password with create@example + * I check the I agree to the terms and conditions + * I click the Sign up button + But I see already registered notification diff --git a/pkg/cmd/server/features/user/delete.feature b/pkg/cmd/server/features/user/delete.feature new file mode 100644 index 0000000..2ea4d76 --- /dev/null +++ b/pkg/cmd/server/features/user/delete.feature @@ -0,0 +1,30 @@ +Feature: Delete a User account + + Scenario: Create a valid non existing account + Given I am on / page + Then I fill the Email with delete@example.com + * I fill the Username with delete + * I fill the Password with delete@example + * I fill the Confirm Password with delete@example + * I check the I agree to the terms and conditions + * I click the Sign up button + And I see account created notification + + Scenario: Login a valid existing account + Given I am on / page + Then I click on the Sign in button + * I fill the Email with delete@example.com + * I fill the Password with delete@example + * I click the Sign in button + * I see logged in notification + And redirect me to /dashboard page + + Scenario: Delete a valid existing account + Given I am on /dashboard page + Then I open the account menu + * I click on the Delete button + * I fill the Password with delete@example + * I fill the Confirm Password with delete@example + * I click the Approve button + * I see account deleted notification + And redirect me to / page diff --git a/pkg/cmd/server/features/user/login.feature b/pkg/cmd/server/features/user/login.feature new file mode 100644 index 0000000..ef286dc --- /dev/null +++ b/pkg/cmd/server/features/user/login.feature @@ -0,0 +1,28 @@ +Feature: Login a User account + + Scenario: Create a valid non existing account + Given I am on / page + Then I fill the Email with login@example.com + * I fill the Username with login + * I fill the Password with login@example + * I fill the Confirm Password with login@example + * I check the I agree to the terms and conditions + * I click the Sign up button + And I see account created notification + + Scenario: Login a valid existing account + Given I am on / page + Then I click on the Sign in button + * I fill the Email with login@example.com + * I fill the Password with login@example + * I click the Sign in button + * I see logged in notification + And redirect me to /dashboard page + + Scenario: Login a valid non existing account + Given I am on / page + Then I click on the Sign in button + * I fill the Email with non-existing@example.com + * I fill the Password with non-existing@example + * I click the Sign in button + But I see not found notification diff --git a/pkg/cmd/server/features/user/update.feature b/pkg/cmd/server/features/user/update.feature new file mode 100644 index 0000000..7b21881 --- /dev/null +++ b/pkg/cmd/server/features/user/update.feature @@ -0,0 +1,30 @@ +Feature: Update a User account + + Scenario: Create a valid non existing account + Given I am on / page + Then I fill the Email with update@example.com + * I fill the Username with update + * I fill the Password with update@example + * I fill the Confirm Password with update@example + * I check the I agree to the terms and conditions + * I click the Sign up button + And I see account created notification + + Scenario: Login a valid existing account + Given I am on / page + Then I click on the Sign in button + * I fill the Email with update@example.com + * I fill the Password with update@example + * I click the Sign in button + * I see logged in notification + And redirect me to /dashboard page + + Scenario: Update a valid existing account + Given I am on /dashboard page + Then I fill the Email with updated@example.com + * I fill the Username with updated + * I fill the New Password with updated@example + * I fill the Confirm Password with updated@example + * I fill the Current Password with update@example + * I click the Update button + And I see account updated notification diff --git a/pkg/cmd/server/handler/page/dashboard.go b/pkg/cmd/server/handler/page/dashboard.go new file mode 100644 index 0000000..845811b --- /dev/null +++ b/pkg/cmd/server/handler/page/dashboard.go @@ -0,0 +1,39 @@ +package page + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/component/page/dashboard" + "github.com/bastean/codexgo/pkg/cmd/server/service/user" + "github.com/bastean/codexgo/pkg/cmd/server/util/errs" + "github.com/bastean/codexgo/pkg/cmd/server/util/key" + "github.com/gin-gonic/gin" +) + +func Dashboard() gin.HandlerFunc { + return func(c *gin.Context) { + id, exists := c.Get(key.UserId) + + if !exists { + c.Error(errs.MissingKey(key.UserId, "Dashboard")) + c.Redirect(http.StatusFound, "/") + c.Abort() + return + } + + query := new(user.ReadQuery) + + query.Id = id.(string) + + user, err := user.Read.Handle(query) + + if err != nil { + c.Error(err) + c.Redirect(http.StatusFound, "/") + c.Abort() + return + } + + dashboard.Page(user).Render(c.Request.Context(), c.Writer) + } +} diff --git a/pkg/cmd/server/handler/page/default.go b/pkg/cmd/server/handler/page/default.go new file mode 100644 index 0000000..208b242 --- /dev/null +++ b/pkg/cmd/server/handler/page/default.go @@ -0,0 +1,13 @@ +package page + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func Default() gin.HandlerFunc { + return func(c *gin.Context) { + c.Redirect(http.StatusFound, "/") + } +} diff --git a/pkg/cmd/server/handler/page/home.go b/pkg/cmd/server/handler/page/home.go new file mode 100644 index 0000000..0df1df0 --- /dev/null +++ b/pkg/cmd/server/handler/page/home.go @@ -0,0 +1,12 @@ +package page + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/component/page/home" + "github.com/gin-gonic/gin" +) + +func Home() gin.HandlerFunc { + return func(c *gin.Context) { + home.Page().Render(c.Request.Context(), c.Writer) + } +} diff --git a/pkg/cmd/server/handler/user/create.go b/pkg/cmd/server/handler/user/create.go new file mode 100644 index 0000000..31bc600 --- /dev/null +++ b/pkg/cmd/server/handler/user/create.go @@ -0,0 +1,34 @@ +package user + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/service/user" + "github.com/bastean/codexgo/pkg/cmd/server/util/errs" + "github.com/bastean/codexgo/pkg/cmd/server/util/reply" + "github.com/gin-gonic/gin" +) + +func Create() gin.HandlerFunc { + return func(c *gin.Context) { + command := new(user.CreateCommand) + + err := c.BindJSON(command) + + if err != nil { + c.Error(errs.BindingJSON(err, "Create")) + c.Abort() + return + } + + err = user.Create.Handle(command) + + if err != nil { + c.Error(err) + c.Abort() + return + } + + c.JSON(http.StatusCreated, reply.JSON(true, "account created", reply.Payload{})) + } +} diff --git a/pkg/cmd/server/handler/user/delete.go b/pkg/cmd/server/handler/user/delete.go new file mode 100644 index 0000000..6f7ca3d --- /dev/null +++ b/pkg/cmd/server/handler/user/delete.go @@ -0,0 +1,45 @@ +package user + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/service/user" + "github.com/bastean/codexgo/pkg/cmd/server/util/errs" + "github.com/bastean/codexgo/pkg/cmd/server/util/key" + "github.com/bastean/codexgo/pkg/cmd/server/util/reply" + "github.com/gin-gonic/gin" +) + +func Delete() gin.HandlerFunc { + return func(c *gin.Context) { + id, exists := c.Get(key.UserId) + + if !exists { + c.Error(errs.MissingKey(key.UserId, "Delete")) + c.Abort() + return + } + + command := new(user.DeleteCommand) + + err := c.BindJSON(command) + + if err != nil { + c.Error(errs.BindingJSON(err, "Delete")) + c.Abort() + return + } + + command.Id = id.(string) + + err = user.Delete.Handle(command) + + if err != nil { + c.Error(err) + c.Abort() + return + } + + c.JSON(http.StatusOK, reply.JSON(true, "account deleted", reply.Payload{})) + } +} diff --git a/pkg/cmd/server/handler/user/login.go b/pkg/cmd/server/handler/user/login.go new file mode 100644 index 0000000..8a5f5d2 --- /dev/null +++ b/pkg/cmd/server/handler/user/login.go @@ -0,0 +1,55 @@ +package user + +import ( + "net/http" + "time" + + "github.com/bastean/codexgo/pkg/cmd/server/service/authentication/jwt" + "github.com/bastean/codexgo/pkg/cmd/server/service/user" + "github.com/bastean/codexgo/pkg/cmd/server/util/errs" + "github.com/bastean/codexgo/pkg/cmd/server/util/key" + "github.com/bastean/codexgo/pkg/cmd/server/util/reply" + "github.com/gin-contrib/sessions" + "github.com/gin-gonic/gin" +) + +func Login() gin.HandlerFunc { + return func(c *gin.Context) { + query := new(user.LoginQuery) + + err := c.BindJSON(query) + + if err != nil { + c.Error(errs.BindingJSON(err, "Login")) + c.Abort() + return + } + + user, err := user.Login.Handle(query) + + if err != nil { + c.Error(err) + c.Abort() + return + } + + token, err := jwt.Generate(jwt.Payload{ + key.Exp: time.Now().Add((24 * time.Hour) * 7).Unix(), + key.UserId: user.Id, + }) + + if err != nil { + c.Error(err) + c.Abort() + return + } + + session := sessions.Default(c) + + session.Set(key.Authorization, "Bearer "+token) + + session.Save() + + c.JSON(http.StatusOK, reply.JSON(true, "logged in", reply.Payload{})) + } +} diff --git a/pkg/cmd/server/handler/user/update.go b/pkg/cmd/server/handler/user/update.go new file mode 100644 index 0000000..ef1efe0 --- /dev/null +++ b/pkg/cmd/server/handler/user/update.go @@ -0,0 +1,45 @@ +package user + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/service/user" + "github.com/bastean/codexgo/pkg/cmd/server/util/errs" + "github.com/bastean/codexgo/pkg/cmd/server/util/key" + "github.com/bastean/codexgo/pkg/cmd/server/util/reply" + "github.com/gin-gonic/gin" +) + +func Update() gin.HandlerFunc { + return func(c *gin.Context) { + id, exists := c.Get(key.UserId) + + if !exists { + c.Error(errs.MissingKey(key.UserId, "Update")) + c.Abort() + return + } + + command := new(user.UpdateCommand) + + err := c.BindJSON(command) + + if err != nil { + c.Error(errs.BindingJSON(err, "Update")) + c.Abort() + return + } + + command.Id = id.(string) + + err = user.Update.Handle(command) + + if err != nil { + c.Error(err) + c.Abort() + return + } + + c.JSON(http.StatusOK, reply.JSON(true, "account updated", reply.Payload{})) + } +} diff --git a/pkg/cmd/server/handler/user/verify.go b/pkg/cmd/server/handler/user/verify.go new file mode 100644 index 0000000..e3a4f36 --- /dev/null +++ b/pkg/cmd/server/handler/user/verify.go @@ -0,0 +1,37 @@ +package user + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/service/user" + "github.com/bastean/codexgo/pkg/cmd/server/util/errs" + "github.com/bastean/codexgo/pkg/cmd/server/util/key" + "github.com/gin-gonic/gin" +) + +func Verify() gin.HandlerFunc { + return func(c *gin.Context) { + id := c.Param(key.Id) + + if id == "" { + c.Error(errs.MissingKey(key.Id, "Verify")) + c.Redirect(http.StatusFound, "/") + c.Abort() + return + } + + command := new(user.VerifyCommand) + + command.Id = id + + err := user.Verify.Handle(command) + + if err != nil { + c.Error(err) + c.Abort() + return + } + + c.Redirect(http.StatusFound, "/dashboard") + } +} diff --git a/pkg/cmd/server/middleware/authentication.go b/pkg/cmd/server/middleware/authentication.go new file mode 100644 index 0000000..a7ca2e0 --- /dev/null +++ b/pkg/cmd/server/middleware/authentication.go @@ -0,0 +1,46 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/bastean/codexgo/pkg/cmd/server/service/authentication/jwt" + "github.com/bastean/codexgo/pkg/cmd/server/util/key" + "github.com/gin-contrib/sessions" + "github.com/gin-gonic/gin" +) + +func abort(c *gin.Context) { + c.Redirect(http.StatusFound, "/") + c.Abort() +} + +func VerifyAuthentication() gin.HandlerFunc { + return func(c *gin.Context) { + session := sessions.Default(c) + + token := session.Get(key.Authorization) + + if token == nil { + abort(c) + return + } + + signature := strings.Split(token.(string), " ")[1] + + claims, err := jwt.Validate(signature) + + if err != nil { + c.Error(err) + abort(c) + return + } + + if value, exists := claims[key.UserId]; exists { + c.Set(key.UserId, value) + c.Next() + } else { + abort(c) + } + } +} diff --git a/pkg/cmd/server/middleware/cookie.go b/pkg/cmd/server/middleware/cookie.go new file mode 100644 index 0000000..8196928 --- /dev/null +++ b/pkg/cmd/server/middleware/cookie.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/service/env" + "github.com/gin-contrib/sessions" + "github.com/gin-contrib/sessions/cookie" + "github.com/gin-gonic/gin" +) + +var secretKey = env.Cookie.SecretKey + +var sessionName = env.Cookie.SessionName + +func CookieSession() gin.HandlerFunc { + store := cookie.NewStore([]byte(secretKey)) + return sessions.Sessions(sessionName, store) +} diff --git a/pkg/cmd/server/middleware/error.go b/pkg/cmd/server/middleware/error.go new file mode 100644 index 0000000..402d430 --- /dev/null +++ b/pkg/cmd/server/middleware/error.go @@ -0,0 +1,40 @@ +package middleware + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/service/errors" + "github.com/bastean/codexgo/pkg/cmd/server/service/logger" + "github.com/bastean/codexgo/pkg/cmd/server/util/reply" + "github.com/gin-gonic/gin" +) + +func ErrorHandler() gin.HandlerFunc { + return func(c *gin.Context) { + c.Next() + + var invalidValue *errors.InvalidValue + var alreadyExist *errors.AlreadyExist + var notExist *errors.NotExist + var failure *errors.Failure + var internal *errors.Internal + + for _, err := range c.Errors { + switch { + case errors.As(err, &invalidValue): + c.JSON(http.StatusUnprocessableEntity, reply.JSON(false, invalidValue.What, invalidValue.Why)) + case errors.As(err, &alreadyExist): + c.JSON(http.StatusConflict, reply.JSON(false, alreadyExist.What, alreadyExist.Why)) + case errors.As(err, ¬Exist): + c.JSON(http.StatusNotFound, reply.JSON(false, notExist.What, notExist.Why)) + case errors.As(err, &failure): + c.JSON(http.StatusBadRequest, reply.JSON(false, failure.What, failure.Why)) + case errors.As(err, &internal): + c.JSON(http.StatusInternalServerError, reply.JSON(false, "internal server error", reply.Payload{})) + fallthrough + default: + logger.Error(err.Error()) + } + } + } +} diff --git a/pkg/cmd/server/middleware/limiter.go b/pkg/cmd/server/middleware/limiter.go new file mode 100644 index 0000000..cd7f1c6 --- /dev/null +++ b/pkg/cmd/server/middleware/limiter.go @@ -0,0 +1,31 @@ +package middleware + +import ( + "net/http" + "time" + + ratelimit "github.com/JGLTechnologies/gin-rate-limit" + "github.com/gin-gonic/gin" +) + +func keyFunc(c *gin.Context) string { + return c.ClientIP() +} + +func errorHandler(c *gin.Context, info ratelimit.Info) { + c.Status(http.StatusTooManyRequests) +} + +func RateLimiter() gin.HandlerFunc { + store := ratelimit.InMemoryStore(&ratelimit.InMemoryOptions{ + Rate: time.Second, + Limit: 20, + }) + + limiter := ratelimit.RateLimiter(store, &ratelimit.Options{ + ErrorHandler: errorHandler, + KeyFunc: keyFunc, + }) + + return limiter +} diff --git a/pkg/cmd/server/middleware/panic.go b/pkg/cmd/server/middleware/panic.go new file mode 100644 index 0000000..4ad397f --- /dev/null +++ b/pkg/cmd/server/middleware/panic.go @@ -0,0 +1,14 @@ +package middleware + +import ( + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/service/logger" + "github.com/bastean/codexgo/pkg/cmd/server/util/reply" + "github.com/gin-gonic/gin" +) + +func PanicHandler(c *gin.Context, err any) { + logger.Error(err.(error).Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, reply.JSON(false, "internal server error", reply.Payload{})) +} diff --git a/pkg/cmd/server/middleware/security.go b/pkg/cmd/server/middleware/security.go new file mode 100644 index 0000000..7d246de --- /dev/null +++ b/pkg/cmd/server/middleware/security.go @@ -0,0 +1,28 @@ +package middleware + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/cmd/server/service/env" + "github.com/gin-contrib/secure" + "github.com/gin-gonic/gin" +) + +var allowedHosts = env.Security.AllowedHosts + +func getAllowedHosts() []string { + return strings.Split(allowedHosts, ", ") +} + +func SecurityConfig() gin.HandlerFunc { + return secure.New(secure.Config{ + AllowedHosts: getAllowedHosts(), + STSSeconds: 315360000, + STSIncludeSubdomains: true, + FrameDeny: true, + ContentTypeNosniff: true, + BrowserXssFilter: true, + IENoOpen: true, + ReferrerPolicy: "strict-origin-when-cross-origin", + }) +} diff --git a/pkg/cmd/server/package.json b/pkg/cmd/server/package.json new file mode 100644 index 0000000..6d46429 --- /dev/null +++ b/pkg/cmd/server/package.json @@ -0,0 +1,7 @@ +{ + "devDependencies": { + "fomantic-ui": "2.9.3", + "jquery": "3.7.1", + "lodash": "4.17.21" + } +} diff --git a/pkg/cmd/server/router/router.go b/pkg/cmd/server/router/router.go new file mode 100644 index 0000000..8adb049 --- /dev/null +++ b/pkg/cmd/server/router/router.go @@ -0,0 +1,33 @@ +package router + +import ( + "embed" + "net/http" + + "github.com/bastean/codexgo/pkg/cmd/server/middleware" + "github.com/gin-gonic/gin" +) + +var router = gin.Default() + +func New(files *embed.FS) *gin.Engine { + router.Use(gin.CustomRecovery(middleware.PanicHandler)) + + router.Use(middleware.ErrorHandler()) + + router.Use(middleware.SecurityConfig()) + + router.Use(middleware.RateLimiter()) + + router.Use(middleware.CookieSession()) + + fs := http.FS(files) + + router.StaticFS("/public", fs) + + router.StaticFileFS("/robots.txt", "static/robots.txt", fs) + + InitRoutes() + + return router +} diff --git a/pkg/cmd/server/router/routes.go b/pkg/cmd/server/router/routes.go new file mode 100644 index 0000000..f8a1831 --- /dev/null +++ b/pkg/cmd/server/router/routes.go @@ -0,0 +1,25 @@ +package router + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/handler/page" + "github.com/bastean/codexgo/pkg/cmd/server/handler/user" + "github.com/bastean/codexgo/pkg/cmd/server/middleware" +) + +func InitRoutes() { + router.NoRoute(page.Default()) + + public := router.Group("/") + + public.GET("/", page.Home()) + public.PUT("/", user.Create()) + public.POST("/", user.Login()) + + public.GET("/verify/:id", user.Verify()) + + auth := public.Group("/dashboard", middleware.VerifyAuthentication()) + + auth.GET("/", page.Dashboard()) + auth.PATCH("/", user.Update()) + auth.DELETE("/", user.Delete()) +} diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go new file mode 100644 index 0000000..5f08f67 --- /dev/null +++ b/pkg/cmd/server/server.go @@ -0,0 +1,72 @@ +package server + +import ( + "context" + "embed" + "errors" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/bastean/codexgo/pkg/cmd/server/router" + "github.com/bastean/codexgo/pkg/cmd/server/service" + "github.com/bastean/codexgo/pkg/cmd/server/service/logger" +) + +//go:embed static +var Files embed.FS + +func Run(port string) { + logger.Info("starting services") + + err := service.Start() + + if err != nil { + logger.Fatal(err.Error()) + } + + logger.Info("starting server") + + server := &http.Server{ + Addr: ":" + port, + Handler: router.New(&Files), + } + + go func() { + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Fatal(err.Error()) + } + }() + + logger.Info("listening and serving HTTP on :" + port) + + shutdown := make(chan os.Signal, 1) + + signal.Notify(shutdown, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + <-shutdown + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + defer cancel() + + logger.Info("stopping services") + + errService := service.Stop(ctx) + + logger.Info("stopping server") + + errServer := server.Shutdown(ctx) + + err = errors.Join(errService, errServer) + + if err != nil { + logger.Error(err.Error()) + } + + <-ctx.Done() + + logger.Info("exiting server") +} diff --git a/pkg/cmd/server/server_test.go b/pkg/cmd/server/server_test.go new file mode 100644 index 0000000..8d9e4ac --- /dev/null +++ b/pkg/cmd/server/server_test.go @@ -0,0 +1,156 @@ +package server_test + +import ( + "context" + "log" + "os" + "strings" + "testing" + "time" + + "github.com/cucumber/godog" + "github.com/playwright-community/playwright-go" + testify "github.com/stretchr/testify/assert" +) + +var testURL = os.Getenv("TEST_URL") + +var pw *playwright.Playwright +var browser playwright.Browser +var browserCtx playwright.BrowserContext +var page playwright.Page + +var headless = true +var exact = true +var sleep = 4 * time.Second +var err error + +var assert *testify.Assertions + +func InitializeAssert(t *testing.T) { + assert = testify.New(t) +} + +func InitializePlaywright() { + pw, err = playwright.Run() + + if err != nil { + log.Fatalf("could not start playwright: %s", err) + } + + browser, err = pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{Headless: &headless}) + + if err != nil { + log.Fatalf("could not launch browser: %s", err) + } + + browserCtx, err = browser.NewContext(playwright.BrowserNewContextOptions{BaseURL: &testURL}) + + if err != nil { + log.Fatalf("could not create context: %s", err) + } + + page, err = browserCtx.NewPage() + + if err != nil { + log.Fatalf("could not create page: %s", err) + } +} + +func InitializeScenario(sc *godog.ScenarioContext) { + sc.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) { + //? browserCtx.ClearCookies() + return ctx, nil + }) + + sc.Given(`^I am on (.+) page$`, func(route string) { + _, err = page.Goto(route) + assert.NoError(err) + }) + + sc.Then(`^redirect me to (.+) page$`, func(actual string) { + time.Sleep(sleep) + + if actual == "/" { + actual = "" + } + + expected := page.URL() + + assert.True(strings.Contains(expected, actual)) + }) + + sc.Then(`^the page title should be (.+)$`, func(expected string) { + actual, _ := page.Title() + assert.Equal(expected, actual) + }) + + sc.Then(`^I click on the (.+) button$`, func(name string) { + element := page.GetByText(name, playwright.PageGetByTextOptions{Exact: &exact}).First() + + err = element.Click() + + assert.NoError(err) + }) + + sc.Then(`^I open the (.+) menu$`, func(name string) { + element := page.GetByRole("heading").First() + + err = element.Click() + + assert.NoError(err) + }) + + sc.Then(`^I fill the (.+) with (.+)$`, func(placeholder, value string) { + element := page.GetByRole("textbox", playwright.PageGetByRoleOptions{Name: placeholder, Exact: &exact}).Last() + + err = element.Fill(value) + + assert.NoError(err) + }) + + sc.Then(`^I click the (.+) button$`, func(name string) { + element := page.GetByRole("button", playwright.PageGetByRoleOptions{Name: name}) + + err = element.Click() + + assert.NoError(err) + }) + + sc.Then(`^I check the (.+)$`, func(name string) { + element := page.GetByRole("checkbox") + + err = element.Check() + + assert.NoError(err) + }) + + sc.Then(`^I see (.+) notification$`, func(expected string) { + element := page.GetByRole("alert") + + actual, err := element.InnerText() + + assert.NoError(err) + + assert.Equal(expected, actual) + }) +} + +func TestAcceptanceServerFeatures(t *testing.T) { + InitializeAssert(t) + + InitializePlaywright() + + suite := godog.TestSuite{ + ScenarioInitializer: InitializeScenario, + Options: &godog.Options{ + Format: "pretty", + Paths: []string{"features"}, + TestingT: t, + }, + } + + if suite.Run() != 0 { + t.Fatal("non-zero status returned, failed to run feature tests") + } +} diff --git a/pkg/cmd/server/service/authentication/jwt/jwt.go b/pkg/cmd/server/service/authentication/jwt/jwt.go new file mode 100644 index 0000000..a70c559 --- /dev/null +++ b/pkg/cmd/server/service/authentication/jwt/jwt.go @@ -0,0 +1,14 @@ +package jwt + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/service/env" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/authentications" +) + +type Payload = authentications.Payload + +var ( + jwt = authentications.NewJWT(env.JWT.SecretKey) + Generate = jwt.Generate + Validate = jwt.Validate +) diff --git a/pkg/cmd/server/service/communication/communication.go b/pkg/cmd/server/service/communication/communication.go new file mode 100644 index 0000000..eeb6a7e --- /dev/null +++ b/pkg/cmd/server/service/communication/communication.go @@ -0,0 +1,7 @@ +package communication + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +type Broker = messages.Broker diff --git a/pkg/cmd/server/service/communication/rabbitmq/binding.go b/pkg/cmd/server/service/communication/rabbitmq/binding.go new file mode 100644 index 0000000..ec14e17 --- /dev/null +++ b/pkg/cmd/server/service/communication/rabbitmq/binding.go @@ -0,0 +1,15 @@ +package rabbitmq + +type Event struct { + CreatedSucceeded string +} + +type Key struct { + *Event +} + +var Binding = &Key{ + Event: &Event{ + CreatedSucceeded: "#.event.#.created.succeeded", + }, +} diff --git a/pkg/cmd/server/service/communication/rabbitmq/consumers.go b/pkg/cmd/server/service/communication/rabbitmq/consumers.go new file mode 100644 index 0000000..a9d511b --- /dev/null +++ b/pkg/cmd/server/service/communication/rabbitmq/consumers.go @@ -0,0 +1,7 @@ +package rabbitmq + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +type Consumers = []messages.Consumer diff --git a/pkg/cmd/server/service/communication/rabbitmq/exchange.go b/pkg/cmd/server/service/communication/rabbitmq/exchange.go new file mode 100644 index 0000000..588fee0 --- /dev/null +++ b/pkg/cmd/server/service/communication/rabbitmq/exchange.go @@ -0,0 +1,11 @@ +package rabbitmq + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +func Exchange(name string) *messages.Router { + return &messages.Router{ + Name: name, + } +} diff --git a/pkg/cmd/server/service/communication/rabbitmq/queues.go b/pkg/cmd/server/service/communication/rabbitmq/queues.go new file mode 100644 index 0000000..ea8b9cf --- /dev/null +++ b/pkg/cmd/server/service/communication/rabbitmq/queues.go @@ -0,0 +1,7 @@ +package rabbitmq + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +type Queues = []*messages.Queue diff --git a/pkg/cmd/server/service/communication/rabbitmq/rabbitmq.go b/pkg/cmd/server/service/communication/rabbitmq/rabbitmq.go new file mode 100644 index 0000000..31dc109 --- /dev/null +++ b/pkg/cmd/server/service/communication/rabbitmq/rabbitmq.go @@ -0,0 +1,57 @@ +package rabbitmq + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/communications" +) + +func New(uri string, logger models.Logger, exchange *messages.Router, queues []*messages.Queue, consumers []messages.Consumer) (messages.Broker, error) { + rabbitMQ, err := communications.NewRabbitMQ(uri, logger) + + if err != nil { + return nil, errors.BubbleUp(err, "New") + } + + err = rabbitMQ.AddRouter(exchange) + + if err != nil { + return nil, errors.BubbleUp(err, "New") + } + + for _, queue := range queues { + + err = rabbitMQ.AddQueue(queue) + + if err != nil { + return nil, errors.BubbleUp(err, "New") + } + + err = rabbitMQ.AddQueueMessageBind(queue, queue.Bindings) + + if err != nil { + return nil, errors.BubbleUp(err, "New") + } + } + + for _, consumer := range consumers { + err = rabbitMQ.AddQueueConsumer(consumer) + + if err != nil { + return nil, errors.BubbleUp(err, "New") + } + } + + return rabbitMQ, nil +} + +func Close(rabbitMQ messages.Broker) error { + err := communications.CloseRabbitMQ(rabbitMQ.(*communications.RabbitMQ)) + + if err != nil { + return errors.BubbleUp(err, "Close") + } + + return nil +} diff --git a/pkg/cmd/server/service/env/default.go b/pkg/cmd/server/service/env/default.go new file mode 100644 index 0000000..cfd61e3 --- /dev/null +++ b/pkg/cmd/server/service/env/default.go @@ -0,0 +1,48 @@ +package env + +import ( + "os" +) + +var ServerURL = os.Getenv("URL") + +var Broker = &struct { + URI string +}{ + URI: os.Getenv("BROKER_URI"), +} + +var Database = &struct { + URI string +}{ + URI: os.Getenv("DATABASE_URI"), +} + +var SMTP = &struct { + Host, Port, Username, Password, ServerURL string +}{ + Host: os.Getenv("SMTP_HOST"), + Port: os.Getenv("SMTP_PORT"), + Username: os.Getenv("SMTP_USERNAME"), + Password: os.Getenv("SMTP_PASSWORD"), + ServerURL: ServerURL, +} + +var Security = &struct { + AllowedHosts string +}{ + AllowedHosts: os.Getenv("ALLOWED_HOSTS"), +} + +var JWT = &struct { + SecretKey string +}{ + SecretKey: os.Getenv("JWT_SECRET_KEY"), +} + +var Cookie = &struct { + SecretKey, SessionName string +}{ + SecretKey: os.Getenv("COOKIE_SECRET_KEY"), + SessionName: os.Getenv("COOKIE_SESSION_NAME"), +} diff --git a/pkg/cmd/server/service/errors/errors.go b/pkg/cmd/server/service/errors/errors.go new file mode 100644 index 0000000..1236158 --- /dev/null +++ b/pkg/cmd/server/service/errors/errors.go @@ -0,0 +1,35 @@ +package errors + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" +) + +type ( + InvalidValue = errors.InvalidValue + AlreadyExist = errors.AlreadyExist + NotExist = errors.NotExist + Failure = errors.Failure + Internal = errors.Internal +) + +var ( + BubbleUp = errors.BubbleUp + As = errors.As + Is = errors.Is +) + +func NewInternal(where, what string, who error) error { + return errors.NewInternal(&errors.Bubble{ + Where: where, + What: what, + Who: who, + }) +} + +func NewFailure(where, what string, who error) error { + return errors.NewFailure(&errors.Bubble{ + Where: where, + What: what, + Who: who, + }) +} diff --git a/pkg/cmd/server/service/logger/logger.go b/pkg/cmd/server/service/logger/logger.go new file mode 100644 index 0000000..340448d --- /dev/null +++ b/pkg/cmd/server/service/logger/logger.go @@ -0,0 +1,13 @@ +package logger + +import ( + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/loggers" +) + +var ( + Logger = new(loggers.Logger) + Debug = Logger.Debug + Error = Logger.Error + Fatal = Logger.Fatal + Info = Logger.Info +) diff --git a/pkg/cmd/server/service/persistence/mongodb/mongodb.go b/pkg/cmd/server/service/persistence/mongodb/mongodb.go new file mode 100644 index 0000000..b6edbcd --- /dev/null +++ b/pkg/cmd/server/service/persistence/mongodb/mongodb.go @@ -0,0 +1,30 @@ +package mongodb + +import ( + "context" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/persistences" +) + +type MongoDB = *persistences.MongoDB + +func New(uri, name string) (*persistences.MongoDB, error) { + mongoDB, err := persistences.NewMongoDatabase(uri, name) + + if err != nil { + return nil, errors.BubbleUp(err, "New") + } + + return mongoDB, nil +} + +func Close(mongoDB *persistences.MongoDB, ctx context.Context) error { + err := persistences.CloseMongoDatabase(ctx, mongoDB) + + if err != nil { + return errors.BubbleUp(err, "Close") + } + + return nil +} diff --git a/pkg/cmd/server/service/service.go b/pkg/cmd/server/service/service.go new file mode 100644 index 0000000..aa6b274 --- /dev/null +++ b/pkg/cmd/server/service/service.go @@ -0,0 +1,163 @@ +package service + +import ( + "context" + + "github.com/bastean/codexgo/pkg/cmd/server/service/communication" + "github.com/bastean/codexgo/pkg/cmd/server/service/communication/rabbitmq" + "github.com/bastean/codexgo/pkg/cmd/server/service/env" + "github.com/bastean/codexgo/pkg/cmd/server/service/errors" + "github.com/bastean/codexgo/pkg/cmd/server/service/logger" + "github.com/bastean/codexgo/pkg/cmd/server/service/persistence/mongodb" + "github.com/bastean/codexgo/pkg/cmd/server/service/transport/smtp" + "github.com/bastean/codexgo/pkg/cmd/server/service/user" +) + +var ( + err error + RabbitMQ communication.Broker + MongoDB mongodb.MongoDB + SMTP smtp.SMTP +) + +func startSMTP() { + if env.SMTP.Host == "" { + user.InitCreated(user.TerminalConfirmation(logger.Logger, env.ServerURL), user.QueueSendConfirmation) + return + } + + SMTP = smtp.New( + env.SMTP.Host, + env.SMTP.Port, + env.SMTP.Username, + env.SMTP.Password, + env.SMTP.ServerURL, + ) + + user.InitCreated(user.MailConfirmation(SMTP), user.QueueSendConfirmation) +} + +func startRabbitMQ() error { + RabbitMQ, err = rabbitmq.New( + env.Broker.URI, + logger.Logger, + rabbitmq.Exchange("codexgo"), + rabbitmq.Queues{ + user.QueueSendConfirmation, + }, + rabbitmq.Consumers{ + user.Created, + }, + ) + + if err != nil { + return errors.BubbleUp(err, "startRabbitMQ") + } + + return nil +} + +func startMongoDB() error { + MongoDB, err = mongodb.New( + env.Database.URI, + "codexgo", + ) + + if err != nil { + return errors.BubbleUp(err, "startMongoDB") + } + + return nil +} + +func startUser() error { + collection, err := user.MongoCollection( + MongoDB, + "users", + user.Bcrypt, + ) + + if err != nil { + return errors.BubbleUp(err, "startUser") + } + + user.Init( + collection, + RabbitMQ, + user.Bcrypt, + ) + + return nil +} + +func Start() error { + logger.Info("starting smtp") + + startSMTP() + + logger.Info("starting rabbitmq") + + err = startRabbitMQ() + + if err != nil { + return errors.BubbleUp(err, "Start") + } + + logger.Info("starting mongodb") + + err = startMongoDB() + + if err != nil { + return errors.BubbleUp(err, "Start") + } + + logger.Info("starting user") + + err = startUser() + + if err != nil { + return errors.BubbleUp(err, "Start") + } + + return nil +} + +func stopRabbitMQ() error { + err = rabbitmq.Close(RabbitMQ) + + if err != nil { + return errors.BubbleUp(err, "stopRabbitMQ") + } + + return nil +} + +func stopMongoDB(ctx context.Context) error { + err = mongodb.Close(MongoDB, ctx) + + if err != nil { + return errors.BubbleUp(err, "stopMongoDB") + } + + return nil +} + +func Stop(ctx context.Context) error { + logger.Info("stopping rabbitmq") + + err = stopRabbitMQ() + + if err != nil { + return errors.BubbleUp(err, "Stop") + } + + logger.Info("stopping mongodb") + + err = stopMongoDB(ctx) + + if err != nil { + return errors.BubbleUp(err, "Stop") + } + + return nil +} diff --git a/pkg/cmd/server/service/transport/smtp/smtp.go b/pkg/cmd/server/service/transport/smtp/smtp.go new file mode 100644 index 0000000..09bc3d6 --- /dev/null +++ b/pkg/cmd/server/service/transport/smtp/smtp.go @@ -0,0 +1,11 @@ +package smtp + +import ( + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/transports" +) + +type SMTP = *transports.SMTP + +func New(host, port, username, password, serverURL string) *transports.SMTP { + return transports.NewSMTP(host, port, username, password, serverURL) +} diff --git a/pkg/cmd/server/service/user/consumer.go b/pkg/cmd/server/service/user/consumer.go new file mode 100644 index 0000000..8f4e34f --- /dev/null +++ b/pkg/cmd/server/service/user/consumer.go @@ -0,0 +1,22 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/application/created" +) + +var ( + Created *created.Consumer +) + +func InitCreated(transport models.Transport, queue *messages.Queue) { + usecase := &created.Created{ + Transport: transport, + } + + Created = &created.Consumer{ + UseCase: usecase, + Queues: []*messages.Queue{queue}, + } +} diff --git a/pkg/cmd/server/service/user/handler.go b/pkg/cmd/server/service/user/handler.go new file mode 100644 index 0000000..a326d18 --- /dev/null +++ b/pkg/cmd/server/service/user/handler.go @@ -0,0 +1,92 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/user/application/create" + "github.com/bastean/codexgo/pkg/context/user/application/delete" + "github.com/bastean/codexgo/pkg/context/user/application/login" + "github.com/bastean/codexgo/pkg/context/user/application/read" + "github.com/bastean/codexgo/pkg/context/user/application/update" + "github.com/bastean/codexgo/pkg/context/user/application/verify" + "github.com/bastean/codexgo/pkg/context/user/domain/model" +) + +type ( + CreateCommand = create.Command + UpdateCommand = update.Command + DeleteCommand = delete.Command + VerifyCommand = verify.Command +) + +type ( + ReadQuery = read.Query + LoginQuery = login.Query +) + +type ( + ReadResponse = read.Response +) + +func NewCreate(repository model.Repository, broker messages.Broker) *create.Handler { + usecase := &create.Create{ + Repository: repository, + } + + return &create.Handler{ + UseCase: usecase, + Broker: broker, + } +} + +func NewRead(repository model.Repository) *read.Handler { + usecase := &read.Read{ + Repository: repository, + } + + return &read.Handler{ + UseCase: usecase, + } +} + +func NewUpdate(repository model.Repository, hashing model.Hashing) *update.Handler { + usecase := &update.Update{ + Repository: repository, + Hashing: hashing, + } + + return &update.Handler{ + UseCase: usecase, + } +} + +func NewDelete(repository model.Repository, hashing model.Hashing) *delete.Handler { + usecase := &delete.Delete{ + Repository: repository, + Hashing: hashing, + } + + return &delete.Handler{ + UseCase: usecase, + } +} + +func NewVerify(repository model.Repository) *verify.Handler { + usecase := &verify.Verify{ + Repository: repository, + } + + return &verify.Handler{ + UseCase: usecase, + } +} + +func NewLogin(repository model.Repository, hashing model.Hashing) *login.Handler { + usecase := &login.Login{ + Repository: repository, + Hashing: hashing, + } + + return &login.Handler{ + UseCase: usecase, + } +} diff --git a/pkg/cmd/server/service/user/hashing.go b/pkg/cmd/server/service/user/hashing.go new file mode 100644 index 0000000..bcad1aa --- /dev/null +++ b/pkg/cmd/server/service/user/hashing.go @@ -0,0 +1,7 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/context/user/infrastructure/cryptographic" +) + +var Bcrypt = new(cryptographic.Bcrypt) diff --git a/pkg/cmd/server/service/user/message.go b/pkg/cmd/server/service/user/message.go new file mode 100644 index 0000000..754b446 --- /dev/null +++ b/pkg/cmd/server/service/user/message.go @@ -0,0 +1,17 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/cmd/server/service/communication/rabbitmq" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +var QueueSendConfirmation = &messages.Queue{ + Name: messages.NewRecipientName(&messages.RecipientNameComponents{ + Service: "user", + Entity: "user", + Action: "send confirmation", + Event: "created", + Status: "succeeded", + }), + Bindings: []string{rabbitmq.Binding.Event.CreatedSucceeded}, +} diff --git a/pkg/cmd/server/service/user/repository.go b/pkg/cmd/server/service/user/repository.go new file mode 100644 index 0000000..7f3295b --- /dev/null +++ b/pkg/cmd/server/service/user/repository.go @@ -0,0 +1,18 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/persistences" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" +) + +func MongoCollection(database *persistences.MongoDB, name string, hashing model.Hashing) (model.Repository, error) { + collection, err := persistence.NewMongoCollection(database, name, hashing) + + if err != nil { + return nil, errors.BubbleUp(err, "MongoCollection") + } + + return collection, nil +} diff --git a/pkg/cmd/server/service/user/transport.go b/pkg/cmd/server/service/user/transport.go new file mode 100644 index 0000000..a5f0b8c --- /dev/null +++ b/pkg/cmd/server/service/user/transport.go @@ -0,0 +1,21 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/transports" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/communication/mail" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/communication/terminal" +) + +func MailConfirmation(smtp *transports.SMTP) models.Transport { + return &mail.Confirmation{ + SMTP: smtp, + } +} + +func TerminalConfirmation(logger models.Logger, serverURL string) models.Transport { + return &terminal.Confirmation{ + Logger: logger, + ServerURL: serverURL, + } +} diff --git a/pkg/cmd/server/service/user/user.go b/pkg/cmd/server/service/user/user.go new file mode 100644 index 0000000..e44866b --- /dev/null +++ b/pkg/cmd/server/service/user/user.go @@ -0,0 +1,35 @@ +package user + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/user/application/create" + "github.com/bastean/codexgo/pkg/context/user/application/delete" + "github.com/bastean/codexgo/pkg/context/user/application/login" + "github.com/bastean/codexgo/pkg/context/user/application/read" + "github.com/bastean/codexgo/pkg/context/user/application/update" + "github.com/bastean/codexgo/pkg/context/user/application/verify" + "github.com/bastean/codexgo/pkg/context/user/domain/model" +) + +var ( + Create *create.Handler + Read *read.Handler + Update *update.Handler + Delete *delete.Handler + Verify *verify.Handler + Login *login.Handler +) + +func Init(repository model.Repository, broker messages.Broker, hashing model.Hashing) { + Create = NewCreate(repository, broker) + + Read = NewRead(repository) + + Update = NewUpdate(repository, hashing) + + Delete = NewDelete(repository, hashing) + + Verify = NewVerify(repository) + + Login = NewLogin(repository, hashing) +} diff --git a/pkg/cmd/server/static/assets/apple-touch-icon.png b/pkg/cmd/server/static/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..26001cab756a813daa0b599b83aafb95d22dd6df GIT binary patch literal 1716 zcmV;l221&gP)DRK?CtIA?Cjp(;NIWg(bCex z#KptJ#KOeHwzs#nwzpl$;i##pr>Ut~zt&m5)mgsPnw*?gw8~br$yKzmEOj9pQQ#(CBJ3T%tLP#q@M=L={D?vvnH#{geJ0~|hCpSAMHajgZ zF(fT8BrGr_EH8u9W?BFM1zkx*K~#9!?c9A=+cp#i@JAcn*6!I7niopfHUT=9V!Tq) zQcBvH(l!ZUalZeznThP!mSq$|M%i|Me{tk1>FO;pM+fkKLL$ZviW-c8m$I@5YhW9| zkN_AUMcfB5OrlB55nvx62}Ff*1)68Na>NG!Isc8JNpc;ANuvQ^ZS5h_<}Lt|TZo0WE}CrNqf*fA8>v`xA|ojM%vnJr zlV^hkD>T?nCNei!gY-vOzZG94PyGSsLXXuMDFAy+0Zff6zNM7{5R_jv;Ls;~NOZXk zA%qY@2qBgb)tGr_FFwc88Z_ZjjfNJM@{(mfut|$(`Sc${np~k*gUjoA)B_a* zynMwDQ?*iioLPqxV+X*QQtB;$d_y!LXUG}2?Im&F0mTl6G#S;55jX~68+rfu__)qY z2XjaBF5UL?krQeMD2!sGcqaPPP8ZET06wX=+5Rw%5z=De^yiysW`hQuXOo8U;_1(* z*q_jvX6t5geGDOl5JCtcgb+dqA%wV7!Ww5hd+{M|W}lgsy?6)9i&!+U7oTHELz?=C zeI{4a!;ocF#Y>?@Me+Jg?zq}h0H>S@04BOq0PmTImY<+8v@Mcfvf?SUW|9RQI*mqS z6EmOIV)Fq^F?WF&HOx`F#`TSgeM~F%(o@lY_fcD~NYg)0t_w7SyWJ};ySU)4#j(w79S(IC&Fw~HVR)5@| zH-C~9ABBfCCdSvfYrLm0Q)gKlpoO>a0*ryDO8tx(mf4Hji!VUoCUZhV-F{2^_<9TQ zR>h#Pz=yT?0~T?RZH1qdQr|;+Z+!lgi+$YC)UT7=J}nhLBHR8HLI@#*5JCtcgb+dq zA%qY@2qAUML1>R1l)A54-08Zonq3^}~!!W|P7VXzq(t3BD=uU`lC2X*g zcMiirjg|Is`-Ypm+Il}9-Qd+)=eRvOA-DB3sn0dKZAQ2?QG*NFF$U}K4!7ipCGFka zV7FVh{a37gOZ4{c?)D>=1h@@mM)e+Y22G>fGjjicoY&uC1P9D}JcTJ}lJx+vXScPdu<(pib3+{_21qDTrurj$-EP+@2eVC9SO!)*GsE&GNY z(kZ|qD-dBNI>qnOGy9QS$K0e&mnW#OVJ_-Q7hw-Yy{btK=+c9gxAGmNZDfm(H(}l$ zEVfwT!1+?;VP!f@jO|O19dyG>*DGgr`s`v=K3_2<9Txo-R{z4%9-Gl#79EzJVO6$@ z)Bi?2w>@?1=Em#R!-@=<*uu<@mtSLlm0rQzzPL`e@t(0FUa|i-N_%c>Bc~H2CQUlE z$N$vy literal 0 HcmV?d00001 diff --git a/pkg/cmd/server/static/assets/favicon.png b/pkg/cmd/server/static/assets/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..26001cab756a813daa0b599b83aafb95d22dd6df GIT binary patch literal 1716 zcmV;l221&gP)DRK?CtIA?Cjp(;NIWg(bCex z#KptJ#KOeHwzs#nwzpl$;i##pr>Ut~zt&m5)mgsPnw*?gw8~br$yKzmEOj9pQQ#(CBJ3T%tLP#q@M=L={D?vvnH#{geJ0~|hCpSAMHajgZ zF(fT8BrGr_EH8u9W?BFM1zkx*K~#9!?c9A=+cp#i@JAcn*6!I7niopfHUT=9V!Tq) zQcBvH(l!ZUalZeznThP!mSq$|M%i|Me{tk1>FO;pM+fkKLL$ZviW-c8m$I@5YhW9| zkN_AUMcfB5OrlB55nvx62}Ff*1)68Na>NG!Isc8JNpc;ANuvQ^ZS5h_<}Lt|TZo0WE}CrNqf*fA8>v`xA|ojM%vnJr zlV^hkD>T?nCNei!gY-vOzZG94PyGSsLXXuMDFAy+0Zff6zNM7{5R_jv;Ls;~NOZXk zA%qY@2qBgb)tGr_FFwc88Z_ZjjfNJM@{(mfut|$(`Sc${np~k*gUjoA)B_a* zynMwDQ?*iioLPqxV+X*QQtB;$d_y!LXUG}2?Im&F0mTl6G#S;55jX~68+rfu__)qY z2XjaBF5UL?krQeMD2!sGcqaPPP8ZET06wX=+5Rw%5z=De^yiysW`hQuXOo8U;_1(* z*q_jvX6t5geGDOl5JCtcgb+dqA%wV7!Ww5hd+{M|W}lgsy?6)9i&!+U7oTHELz?=C zeI{4a!;ocF#Y>?@Me+Jg?zq}h0H>S@04BOq0PmTImY<+8v@Mcfvf?SUW|9RQI*mqS z6EmOIV)Fq^F?WF&HOx`F#`TSgeM~F%(o@lY_fcD~NYg)0t_w7SyWJ};ySU)4#j(w79S(IC&Fw~HVR)5@| zH-C~9ABBfCCdSvfYrLm0Q)gKlpoO>a0*ryDO8tx(mf4Hji!VUoCUZhV-F{2^_<9TQ zR>h#Pz=yT?0~T?RZH1qdQr|;+Z+!lgi+$YC)UT7=J}nhLBHR8HLI@#*5JCtcgb+dq zA%qY@2qAUML1>R1l)A54-08Zonq3^}~!!W|P7VXzq(t3BD=uU`lC2X*g zcMiirjg|Is`-Ypm+Il}9-Qd+)=eRvOA-DB3sn0dKZAQ2?QG*NFF$U}K4!7ipCGFka zV7FVh{a37gOZ4{c?)D>=1h@@mM)e+Y22G>fGjjicoY&uC1P9D}JcTJ}lJx+vXScPdu<(pib3+{_21qDTrurj$-EP+@2eVC9SO!)*GsE&GNY z(kZ|qD-dBNI>qnOGy9QS$K0e&mnW#OVJ_-Q7hw-Yy{btK=+c9gxAGmNZDfm(H(}l$ zEVfwT!1+?;VP!f@jO|O19dyG>*DGgr`s`v=K3_2<9Txo-R{z4%9-Gl#79EzJVO6$@ z)Bi?2w>@?1=Em#R!-@=<*uu<@mtSLlm0rQzzPL`e@t(0FUa|i-N_%c>Bc~H2CQUlE z$N$vy literal 0 HcmV?d00001 diff --git a/pkg/cmd/server/static/assets/logo.png b/pkg/cmd/server/static/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..52ba8b9d21f0e60ba34dec197e2a6451c01307a6 GIT binary patch literal 4820 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", + "homepage_url": "https://github.com/bastean/codexgo#readme", + "start_url": ".", + "display": "standalone", + "orientation": "portrait-primary", + "background_color": "#202224", + "theme_color": "#202224", + "icons": [ + { + "src": "assets/pwa-any.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "any" + }, + { + "src": "assets/pwa-maskable.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + } + ] +} diff --git a/pkg/cmd/server/static/robots.txt b/pkg/cmd/server/static/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/pkg/cmd/server/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/pkg/cmd/server/util/errs/binding.go b/pkg/cmd/server/util/errs/binding.go new file mode 100644 index 0000000..aed9725 --- /dev/null +++ b/pkg/cmd/server/util/errs/binding.go @@ -0,0 +1,26 @@ +package errs + +import ( + "encoding/json" + "fmt" + + "github.com/bastean/codexgo/pkg/cmd/server/service/errors" +) + +func BindingJSON(who error, where string) error { + var err *json.UnmarshalTypeError + + if errors.As(who, &err) { + return errors.NewFailure( + where, + fmt.Sprintf("invalid type field [%s] required type is [%s] and [%s] was obtained", err.Field, err.Type, err.Value), + who, + ) + } + + return errors.NewInternal( + where, + "cannot bind a json to a struct", + who, + ) +} diff --git a/pkg/cmd/server/util/errs/missing.go b/pkg/cmd/server/util/errs/missing.go new file mode 100644 index 0000000..a325abd --- /dev/null +++ b/pkg/cmd/server/util/errs/missing.go @@ -0,0 +1,15 @@ +package errs + +import ( + "fmt" + + "github.com/bastean/codexgo/pkg/cmd/server/service/errors" +) + +func MissingKey(what, where string) error { + return errors.NewInternal( + where, + fmt.Sprintf("failure to obtain the value of the key [%s]", what), + nil, + ) +} diff --git a/pkg/cmd/server/util/key/key.go b/pkg/cmd/server/util/key/key.go new file mode 100644 index 0000000..4bbb6c4 --- /dev/null +++ b/pkg/cmd/server/util/key/key.go @@ -0,0 +1,9 @@ +package key + +var Authorization = "Authorization" + +var Exp = "exp" + +var Id = "id" + +var UserId = "userId" diff --git a/pkg/cmd/server/util/reply/reply.go b/pkg/cmd/server/util/reply/reply.go new file mode 100644 index 0000000..e95f1e4 --- /dev/null +++ b/pkg/cmd/server/util/reply/reply.go @@ -0,0 +1,11 @@ +package reply + +type Payload = map[string]any + +func JSON(success bool, message string, data map[string]any) map[string]any { + return map[string]any{ + "success": success, + "message": message, + "data": data, + } +} diff --git a/pkg/context/shared/domain/aggregates/root.go b/pkg/context/shared/domain/aggregates/root.go new file mode 100644 index 0000000..af22da8 --- /dev/null +++ b/pkg/context/shared/domain/aggregates/root.go @@ -0,0 +1,27 @@ +package aggregates + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +type AggregateRoot struct { + Messages []*messages.Message +} + +func (root *AggregateRoot) RecordMessage(message *messages.Message) { + root.Messages = append(root.Messages, message) +} + +func (root *AggregateRoot) PullMessages() []*messages.Message { + recordedMessages := root.Messages + + root.Messages = []*messages.Message{} + + return recordedMessages +} + +func NewAggregateRoot() *AggregateRoot { + return &AggregateRoot{ + Messages: []*messages.Message{}, + } +} diff --git a/pkg/context/shared/domain/errors/bubble.go b/pkg/context/shared/domain/errors/bubble.go new file mode 100644 index 0000000..d93008d --- /dev/null +++ b/pkg/context/shared/domain/errors/bubble.go @@ -0,0 +1,52 @@ +package errors + +import ( + "encoding/json" + "fmt" + "time" +) + +type Bubble struct { + When time.Time + Where, What string + Why map[string]any + Who error +} + +func (err *Bubble) Error() string { + message := fmt.Sprintf("%s (%s): %s", err.When.Format(time.RFC3339Nano), err.Where, err.What) + + if err.Why != nil { + why, err := json.Marshal(err.Why) + + if err != nil { + Panic(fmt.Sprintf("cannot json encoding \"why\" from error bubble: %s: [%s]", message, err.Error()), "Error") + } + + message = fmt.Sprintf("%s: %s", message, why) + } + + if err.Who != nil { + message = fmt.Sprintf("%s: [%s]", message, err.Who) + } + + return message +} + +func NewBubble(where, what string, why Meta, who error) *Bubble { + if where == "" { + Panic("cannot create a error bubble if \"where\" is not defined", "NewBubble") + } + + if what == "" { + Panic("cannot create a error bubble if \"what\" is not defined", "NewBubble") + } + + return &Bubble{ + When: time.Now().UTC(), + Where: where, + What: what, + Why: why, + Who: who, + } +} diff --git a/pkg/context/shared/domain/errors/bubbleup.go b/pkg/context/shared/domain/errors/bubbleup.go new file mode 100644 index 0000000..15e5415 --- /dev/null +++ b/pkg/context/shared/domain/errors/bubbleup.go @@ -0,0 +1,9 @@ +package errors + +import ( + "fmt" +) + +func BubbleUp(who error, where string) error { + return fmt.Errorf("(%s): [%w]", where, who) +} diff --git a/pkg/context/shared/domain/errors/default.go b/pkg/context/shared/domain/errors/default.go new file mode 100644 index 0000000..c6a2d0f --- /dev/null +++ b/pkg/context/shared/domain/errors/default.go @@ -0,0 +1,5 @@ +package errors + +func Default() error { + return new(Bubble) +} diff --git a/pkg/context/shared/domain/errors/exist.go b/pkg/context/shared/domain/errors/exist.go new file mode 100644 index 0000000..0d5eecc --- /dev/null +++ b/pkg/context/shared/domain/errors/exist.go @@ -0,0 +1,31 @@ +package errors + +type AlreadyExist struct { + *Bubble +} + +type NotExist struct { + *Bubble +} + +func NewAlreadyExist(bubble *Bubble) error { + return &AlreadyExist{ + Bubble: NewBubble( + bubble.Where, + bubble.What, + bubble.Why, + bubble.Who, + ), + } +} + +func NewNotExist(bubble *Bubble) error { + return &NotExist{ + Bubble: NewBubble( + bubble.Where, + bubble.What, + bubble.Why, + bubble.Who, + ), + } +} diff --git a/pkg/context/shared/domain/errors/failure.go b/pkg/context/shared/domain/errors/failure.go new file mode 100644 index 0000000..c330d5f --- /dev/null +++ b/pkg/context/shared/domain/errors/failure.go @@ -0,0 +1,16 @@ +package errors + +type Failure struct { + *Bubble +} + +func NewFailure(bubble *Bubble) error { + return &Failure{ + Bubble: NewBubble( + bubble.Where, + bubble.What, + bubble.Why, + bubble.Who, + ), + } +} diff --git a/pkg/context/shared/domain/errors/internal.go b/pkg/context/shared/domain/errors/internal.go new file mode 100644 index 0000000..3b06ebf --- /dev/null +++ b/pkg/context/shared/domain/errors/internal.go @@ -0,0 +1,16 @@ +package errors + +type Internal struct { + *Bubble +} + +func NewInternal(bubble *Bubble) error { + return &Internal{ + Bubble: NewBubble( + bubble.Where, + bubble.What, + bubble.Why, + bubble.Who, + ), + } +} diff --git a/pkg/context/shared/domain/errors/invalid.go b/pkg/context/shared/domain/errors/invalid.go new file mode 100644 index 0000000..8078f1e --- /dev/null +++ b/pkg/context/shared/domain/errors/invalid.go @@ -0,0 +1,16 @@ +package errors + +type InvalidValue struct { + *Bubble +} + +func NewInvalidValue(bubble *Bubble) error { + return &InvalidValue{ + Bubble: NewBubble( + bubble.Where, + bubble.What, + bubble.Why, + bubble.Who, + ), + } +} diff --git a/pkg/context/shared/domain/errors/meta.go b/pkg/context/shared/domain/errors/meta.go new file mode 100644 index 0000000..7033e59 --- /dev/null +++ b/pkg/context/shared/domain/errors/meta.go @@ -0,0 +1,3 @@ +package errors + +type Meta = map[string]any diff --git a/pkg/context/shared/domain/errors/panic.go b/pkg/context/shared/domain/errors/panic.go new file mode 100644 index 0000000..965b572 --- /dev/null +++ b/pkg/context/shared/domain/errors/panic.go @@ -0,0 +1,9 @@ +package errors + +import ( + "log" +) + +func Panic(what, where string) { + log.Panicf("(%s): %s", where, what) +} diff --git a/pkg/context/shared/domain/errors/wrap.go b/pkg/context/shared/domain/errors/wrap.go new file mode 100644 index 0000000..708aae4 --- /dev/null +++ b/pkg/context/shared/domain/errors/wrap.go @@ -0,0 +1,17 @@ +package errors + +import ( + "errors" +) + +func Join(errs ...error) error { + return errors.Join(errs...) +} + +func As(err error, target any) bool { + return errors.As(err, target) +} + +func Is(err, target error) bool { + return errors.Is(err, target) +} diff --git a/pkg/context/shared/domain/messages/broker.go b/pkg/context/shared/domain/messages/broker.go new file mode 100644 index 0000000..6182809 --- /dev/null +++ b/pkg/context/shared/domain/messages/broker.go @@ -0,0 +1,9 @@ +package messages + +type Broker interface { + AddRouter(router *Router) error + AddQueue(queue *Queue) error + AddQueueMessageBind(queue *Queue, bindingKeys []string) error + AddQueueConsumer(consumer Consumer) error + PublishMessages(messages []*Message) error +} diff --git a/pkg/context/shared/domain/messages/consumer.go b/pkg/context/shared/domain/messages/consumer.go new file mode 100644 index 0000000..c1c8809 --- /dev/null +++ b/pkg/context/shared/domain/messages/consumer.go @@ -0,0 +1,6 @@ +package messages + +type Consumer interface { + SubscribedTo() []*Queue + On(message *Message) error +} diff --git a/pkg/context/shared/domain/messages/key.go b/pkg/context/shared/domain/messages/key.go new file mode 100644 index 0000000..5c3a722 --- /dev/null +++ b/pkg/context/shared/domain/messages/key.go @@ -0,0 +1,74 @@ +package messages + +import ( + "fmt" + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" +) + +var Type = struct { + Event, Command string +}{ + Event: "event", + Command: "command", +} + +var Status = struct { + Queued, Succeeded, Failed, Done string +}{ + Queued: "queued", + Succeeded: "succeeded", + Failed: "failed", + Done: "done", +} + +// Terminology: +// - Organization = Context +// - Service = Module +// - Entity = Aggregate/Root +// +// Nomenclature of a Routing Key (Topic): +// - organization.service.version.type.entity.event/command.status +// - codexgo.user.1.event.user.created.succeeded +type RoutingKeyComponents struct { + Organization, Service, Version, Type, Entity, Event, Command, Status string +} + +func NewRoutingKey(components *RoutingKeyComponents) string { + if components.Organization == "" { + components.Organization = "codexgo" + } + + organization, errOrganization := valueobjs.NewOrganization(components.Organization) + service, errService := valueobjs.NewService(components.Service) + version, errVersion := valueobjs.NewVersion(components.Version) + types, errType := valueobjs.NewType(components.Type) + entity, errEntity := valueobjs.NewEntity(components.Entity) + + var action models.ValueObject[string] + var errAction error + + switch components.Type { + case Type.Event: + action, errAction = valueobjs.NewEvent(components.Event) + case Type.Command: + action, errAction = valueobjs.NewCommand(components.Command) + } + + status, errStatus := valueobjs.NewStatus(components.Status) + + err := errors.Join(errOrganization, errService, errVersion, errType, errEntity, errAction, errStatus) + + if err != nil { + errors.Panic(err.Error(), "NewRoutingKey") + } + + key := fmt.Sprintf("%s.%s.%s.%s.%s.%s.%s", organization.Value(), service.Value(), version.Value(), types.Value(), entity.Value(), action.Value(), status.Value()) + + key = strings.ToLower(key) + + return key +} diff --git a/pkg/context/shared/domain/messages/key_test.go b/pkg/context/shared/domain/messages/key_test.go new file mode 100644 index 0000000..3ba62e8 --- /dev/null +++ b/pkg/context/shared/domain/messages/key_test.go @@ -0,0 +1,41 @@ +package messages_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/stretchr/testify/suite" +) + +type RoutingKeyTestSuite struct { + suite.Suite +} + +func (suite *RoutingKeyTestSuite) SetupTest() {} + +func (suite *RoutingKeyTestSuite) TestWithValidValue() { + components := &messages.RoutingKeyComponents{ + Organization: "codexgo", + Service: "user", + Version: "1", + Type: messages.Type.Event, + Entity: "user", + Event: "created", + Status: messages.Status.Succeeded, + } + + expected := "codexgo.user.1.event.user.created.succeeded" + + actual := messages.NewRoutingKey(components) + + suite.Equal(expected, actual) +} + +func (suite *RoutingKeyTestSuite) TestWithInvalidValue() { + components := &messages.RoutingKeyComponents{} + suite.Panics(func() { messages.NewRoutingKey(components) }) +} + +func TestUnitRoutingKeySuite(t *testing.T) { + suite.Run(t, new(RoutingKeyTestSuite)) +} diff --git a/pkg/context/shared/domain/messages/message.go b/pkg/context/shared/domain/messages/message.go new file mode 100644 index 0000000..2e8d937 --- /dev/null +++ b/pkg/context/shared/domain/messages/message.go @@ -0,0 +1,18 @@ +package messages + +type Attributes = []byte + +type Meta = []byte + +type Message struct { + Id, Type, OccurredOn string + Attributes, Meta []byte +} + +func NewMessage(routingKey string, attributes, meta []byte) *Message { + return &Message{ + Type: routingKey, + Attributes: attributes, + Meta: meta, + } +} diff --git a/pkg/context/shared/domain/messages/queue.go b/pkg/context/shared/domain/messages/queue.go new file mode 100644 index 0000000..0894227 --- /dev/null +++ b/pkg/context/shared/domain/messages/queue.go @@ -0,0 +1,19 @@ +package messages + +type Queue struct { + Name string + Bindings []string +} + +func HasNoQueue(queues []Queue, queue *Queue) bool { + isNotPresent := true + + for _, present := range queues { + if present.Name == queue.Name { + isNotPresent = false + break + } + } + + return isNotPresent +} diff --git a/pkg/context/shared/domain/messages/recipient.go b/pkg/context/shared/domain/messages/recipient.go new file mode 100644 index 0000000..1e1d750 --- /dev/null +++ b/pkg/context/shared/domain/messages/recipient.go @@ -0,0 +1,51 @@ +package messages + +import ( + "fmt" + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" +) + +// Terminology: +// - Service = Module +// - Entity = Aggregate/Root +// +// Nomenclature of a Recipient Name: +// - service.entity.action_on_event/command_status +// - user.user.send_confirmation_on_created_succeeded +type RecipientNameComponents struct { + Service, Entity, Action, Event, Command, Status string +} + +func NewRecipientName(components *RecipientNameComponents) string { + service, errService := valueobjs.NewService(components.Service) + entity, errEntity := valueobjs.NewEntity(components.Entity) + action, errAction := valueobjs.NewAction(components.Action) + + var trigger models.ValueObject[string] + var errTrigger error + + switch { + case components.Event != "": + trigger, errTrigger = valueobjs.NewEvent(components.Event) + case components.Command != "": + trigger, errTrigger = valueobjs.NewCommand(components.Command) + } + + status, errStatus := valueobjs.NewStatus(components.Status) + + err := errors.Join(errService, errEntity, errAction, errTrigger, errStatus) + + if err != nil { + errors.Panic(err.Error(), "NewRecipientName") + } + + name := fmt.Sprintf("%s.%s.%s_on_%s_%s", service.Value(), entity.Value(), strings.ReplaceAll(action.Value(), " ", "_"), trigger.Value(), status.Value()) + + name = strings.ToLower(name) + + return name +} diff --git a/pkg/context/shared/domain/messages/recipient_test.go b/pkg/context/shared/domain/messages/recipient_test.go new file mode 100644 index 0000000..fe3d1ee --- /dev/null +++ b/pkg/context/shared/domain/messages/recipient_test.go @@ -0,0 +1,39 @@ +package messages_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/stretchr/testify/suite" +) + +type RecipientNameTestSuite struct { + suite.Suite +} + +func (suite *RecipientNameTestSuite) SetupTest() {} + +func (suite *RecipientNameTestSuite) TestWithValidValue() { + components := &messages.RecipientNameComponents{ + Service: "user", + Entity: "user", + Action: "send confirmation", + Event: "created", + Status: messages.Status.Succeeded, + } + + expected := "user.user.send_confirmation_on_created_succeeded" + + actual := messages.NewRecipientName(components) + + suite.Equal(expected, actual) +} + +func (suite *RecipientNameTestSuite) TestWithInvalidValue() { + components := &messages.RecipientNameComponents{} + suite.Panics(func() { messages.NewRecipientName(components) }) +} + +func TestUnitRecipientNameSuite(t *testing.T) { + suite.Run(t, new(RecipientNameTestSuite)) +} diff --git a/pkg/context/shared/domain/messages/router.go b/pkg/context/shared/domain/messages/router.go new file mode 100644 index 0000000..95c52d2 --- /dev/null +++ b/pkg/context/shared/domain/messages/router.go @@ -0,0 +1,5 @@ +package messages + +type Router struct { + Name string +} diff --git a/pkg/context/shared/domain/models/handler.go b/pkg/context/shared/domain/models/handler.go new file mode 100644 index 0000000..676e000 --- /dev/null +++ b/pkg/context/shared/domain/models/handler.go @@ -0,0 +1,9 @@ +package models + +type CommandHandler[Command any] interface { + Handle(Command) error +} + +type QueryHandler[Query, Response any] interface { + Handle(Query) (Response, error) +} diff --git a/pkg/context/shared/domain/models/logger.go b/pkg/context/shared/domain/models/logger.go new file mode 100644 index 0000000..302f0f4 --- /dev/null +++ b/pkg/context/shared/domain/models/logger.go @@ -0,0 +1,8 @@ +package models + +type Logger interface { + Debug(message string) + Error(message string) + Fatal(message string) + Info(message string) +} diff --git a/pkg/context/shared/domain/models/transport.go b/pkg/context/shared/domain/models/transport.go new file mode 100644 index 0000000..be94fb8 --- /dev/null +++ b/pkg/context/shared/domain/models/transport.go @@ -0,0 +1,5 @@ +package models + +type Transport interface { + Submit(any) error +} diff --git a/pkg/context/shared/domain/models/usecase.go b/pkg/context/shared/domain/models/usecase.go new file mode 100644 index 0000000..8c5f56d --- /dev/null +++ b/pkg/context/shared/domain/models/usecase.go @@ -0,0 +1,5 @@ +package models + +type UseCase[Input, Output any] interface { + Run(Input) (Output, error) +} diff --git a/pkg/context/shared/domain/models/valueobj.go b/pkg/context/shared/domain/models/valueobj.go new file mode 100644 index 0000000..5fbeffa --- /dev/null +++ b/pkg/context/shared/domain/models/valueobj.go @@ -0,0 +1,6 @@ +package models + +type ValueObject[Value any] interface { + Value() Value + IsValid() error +} diff --git a/pkg/context/shared/domain/services/context.go b/pkg/context/shared/domain/services/context.go new file mode 100644 index 0000000..61e921c --- /dev/null +++ b/pkg/context/shared/domain/services/context.go @@ -0,0 +1,17 @@ +package services + +import ( + "context" +) + +type CtxKey string + +const CtxDefaultKey CtxKey = "default" + +func SetDefaultContextValue(value any) context.Context { + return context.WithValue(context.Background(), CtxDefaultKey, value) +} + +func GetDefaultContextValue[T any](ctx context.Context) T { + return ctx.Value(CtxDefaultKey).(T) +} diff --git a/pkg/context/shared/domain/services/create.go b/pkg/context/shared/domain/services/create.go new file mode 100644 index 0000000..a426aee --- /dev/null +++ b/pkg/context/shared/domain/services/create.go @@ -0,0 +1,23 @@ +package services + +import ( + "fmt" + "strings" + + "github.com/brianvoe/gofakeit/v7" +) + +type mother struct { + *gofakeit.Faker +} + +func (create *mother) Email() string { + username := strings.Split(create.Faker.Email(), "@")[0] + domain := "example.com" + + return fmt.Sprintf("%s@%s", username, domain) +} + +var Create = &mother{ + Faker: gofakeit.New(0), +} diff --git a/pkg/context/shared/domain/types/empty.go b/pkg/context/shared/domain/types/empty.go new file mode 100644 index 0000000..c3a66d8 --- /dev/null +++ b/pkg/context/shared/domain/types/empty.go @@ -0,0 +1,3 @@ +package types + +type Empty = interface{} diff --git a/pkg/context/shared/domain/valueobjs/action.go b/pkg/context/shared/domain/valueobjs/action.go new file mode 100644 index 0000000..f41f20e --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/action.go @@ -0,0 +1,45 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const ActionMinCharactersLength = "1" +const ActionMaxCharactersLength = "20" + +type Action struct { + Action string `validate:"gte=1,lte=20"` +} + +func (value *Action) Value() string { + return value.Action +} + +func (value *Action) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewAction(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Action{ + Action: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewAction", + What: "action must be between " + ActionMinCharactersLength + " to " + ActionMaxCharactersLength + " characters", + Why: errors.Meta{ + "Action": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/action.mother.go b/pkg/context/shared/domain/valueobjs/action.mother.go new file mode 100644 index 0000000..0860dff --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/action.mother.go @@ -0,0 +1,25 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func ActionWithValidValue() models.ValueObject[string] { + value, err := NewAction(services.Create.Regex(`^[A-Za-z\s]{1,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "ActionWithValidValue") + } + + return value +} + +func ActionWithInvalidLength() (string, error) { + value := "" + + _, err := NewAction(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/action_test.go b/pkg/context/shared/domain/valueobjs/action_test.go new file mode 100644 index 0000000..7906323 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/action_test.go @@ -0,0 +1,38 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type ActionValueObjectTestSuite struct { + suite.Suite +} + +func (suite *ActionValueObjectTestSuite) SetupTest() {} + +func (suite *ActionValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobjs.ActionWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewAction", + What: "action must be between " + "1" + " to " + "20" + " characters", + Why: errors.Meta{ + "Action": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitActionValueObjectSuite(t *testing.T) { + suite.Run(t, new(ActionValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/command.go b/pkg/context/shared/domain/valueobjs/command.go new file mode 100644 index 0000000..fa35111 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/command.go @@ -0,0 +1,45 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const CommandMinCharactersLength = "1" +const CommandMaxCharactersLength = "20" + +type Command struct { + Command string `validate:"gte=1,lte=20,alpha"` +} + +func (value *Command) Value() string { + return value.Command +} + +func (value *Command) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewCommand(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Command{ + Command: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewCommand", + What: "command must be between " + CommandMinCharactersLength + " to " + CommandMaxCharactersLength + " characters and be alpha only", + Why: errors.Meta{ + "Command": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/command.mother.go b/pkg/context/shared/domain/valueobjs/command.mother.go new file mode 100644 index 0000000..3b0b869 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/command.mother.go @@ -0,0 +1,33 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func CommandWithValidValue() models.ValueObject[string] { + value, err := NewCommand(services.Create.Regex(`^[A-Za-z]{1,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "CommandWithValidValue") + } + + return value +} + +func CommandWithInvalidLength() (string, error) { + value := "" + + _, err := NewCommand(value) + + return value, err +} + +func CommandWithInvalidAlpha() (string, error) { + value := "<>" + + _, err := NewCommand(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/command_test.go b/pkg/context/shared/domain/valueobjs/command_test.go new file mode 100644 index 0000000..1f529fd --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/command_test.go @@ -0,0 +1,57 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type CommandValueObjectTestSuite struct { + suite.Suite +} + +func (suite *CommandValueObjectTestSuite) SetupTest() {} + +func (suite *CommandValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobjs.CommandWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewCommand", + What: "command must be between " + "1" + " to " + "20" + " characters and be alpha only", + Why: errors.Meta{ + "Command": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *CommandValueObjectTestSuite) TestWithInvalidAlpha() { + value, err := valueobjs.CommandWithInvalidAlpha() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewCommand", + What: "command must be between " + "1" + " to " + "20" + " characters and be alpha only", + Why: errors.Meta{ + "Command": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitCommandValueObjectSuite(t *testing.T) { + suite.Run(t, new(CommandValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/email.go b/pkg/context/shared/domain/valueobjs/email.go new file mode 100644 index 0000000..3a072d0 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/email.go @@ -0,0 +1,42 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +type Email struct { + Email string `validate:"email"` +} + +func (value *Email) Value() string { + return value.Email +} + +func (value *Email) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewEmail(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Email{ + Email: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewEmail", + What: "invalid email format", + Why: errors.Meta{ + "Email": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/entity.go b/pkg/context/shared/domain/valueobjs/entity.go new file mode 100644 index 0000000..ae78747 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/entity.go @@ -0,0 +1,45 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const EntityMinCharactersLength = "1" +const EntityMaxCharactersLength = "20" + +type Entity struct { + Entity string `validate:"gte=1,lte=20,alpha"` +} + +func (value *Entity) Value() string { + return value.Entity +} + +func (value *Entity) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewEntity(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Entity{ + Entity: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewEntity", + What: "entity must be between " + EntityMinCharactersLength + " to " + EntityMaxCharactersLength + " characters and be alpha only", + Why: errors.Meta{ + "Entity": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/entity.mother.go b/pkg/context/shared/domain/valueobjs/entity.mother.go new file mode 100644 index 0000000..84488f9 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/entity.mother.go @@ -0,0 +1,33 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func EntityWithValidValue() models.ValueObject[string] { + value, err := NewEntity(services.Create.Regex(`^[A-Za-z]{1,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "EntityWithValidValue") + } + + return value +} + +func EntityWithInvalidLength() (string, error) { + value := "" + + _, err := NewEntity(value) + + return value, err +} + +func EntityWithInvalidAlpha() (string, error) { + value := "<>" + + _, err := NewEntity(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/entity_test.go b/pkg/context/shared/domain/valueobjs/entity_test.go new file mode 100644 index 0000000..f9427d0 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/entity_test.go @@ -0,0 +1,57 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type EntityValueObjectTestSuite struct { + suite.Suite +} + +func (suite *EntityValueObjectTestSuite) SetupTest() {} + +func (suite *EntityValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobjs.EntityWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewEntity", + What: "entity must be between " + "1" + " to " + "20" + " characters and be alpha only", + Why: errors.Meta{ + "Entity": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *EntityValueObjectTestSuite) TestWithInvalidAlpha() { + value, err := valueobjs.EntityWithInvalidAlpha() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewEntity", + What: "entity must be between " + "1" + " to " + "20" + " characters and be alpha only", + Why: errors.Meta{ + "Entity": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitEntityValueObjectSuite(t *testing.T) { + suite.Run(t, new(EntityValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/event.go b/pkg/context/shared/domain/valueobjs/event.go new file mode 100644 index 0000000..6899a77 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/event.go @@ -0,0 +1,45 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const EventMinCharactersLength = "1" +const EventMaxCharactersLength = "20" + +type Event struct { + Event string `validate:"gte=1,lte=20,alpha"` +} + +func (value *Event) Value() string { + return value.Event +} + +func (value *Event) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewEvent(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Entity{ + Entity: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewEvent", + What: "event must be between " + EventMinCharactersLength + " to " + EventMaxCharactersLength + " characters and be alpha only", + Why: errors.Meta{ + "Event": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/event.mother.go b/pkg/context/shared/domain/valueobjs/event.mother.go new file mode 100644 index 0000000..b6f2f04 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/event.mother.go @@ -0,0 +1,33 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func EventWithValidValue() models.ValueObject[string] { + value, err := NewEvent(services.Create.Regex(`^[A-Za-z]{1,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "EventWithValidValue") + } + + return value +} + +func EventWithInvalidLength() (string, error) { + value := "" + + _, err := NewEvent(value) + + return value, err +} + +func EventWithInvalidAlpha() (string, error) { + value := "<>" + + _, err := NewEvent(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/event_test.go b/pkg/context/shared/domain/valueobjs/event_test.go new file mode 100644 index 0000000..143ffce --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/event_test.go @@ -0,0 +1,57 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type EventValueObjectTestSuite struct { + suite.Suite +} + +func (suite *EventValueObjectTestSuite) SetupTest() {} + +func (suite *EventValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobjs.EventWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewEvent", + What: "event must be between " + "1" + " to " + "20" + " characters and be alpha only", + Why: errors.Meta{ + "Event": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *EventValueObjectTestSuite) TestWithInvalidAlpha() { + value, err := valueobjs.EventWithInvalidAlpha() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewEvent", + What: "event must be between " + "1" + " to " + "20" + " characters and be alpha only", + Why: errors.Meta{ + "Event": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitEventValueObjectSuite(t *testing.T) { + suite.Run(t, new(EventValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/id.go b/pkg/context/shared/domain/valueobjs/id.go new file mode 100644 index 0000000..fdf9d47 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/id.go @@ -0,0 +1,42 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +type Id struct { + Id string `validate:"uuid4"` +} + +func (value *Id) Value() string { + return value.Id +} + +func (value *Id) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewId(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Id{ + Id: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewId", + What: "invalid uuid4 format", + Why: errors.Meta{ + "Id": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/organization.go b/pkg/context/shared/domain/valueobjs/organization.go new file mode 100644 index 0000000..97297c7 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/organization.go @@ -0,0 +1,45 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const OrganizationMinCharactersLength = "1" +const OrganizationMaxCharactersLength = "20" + +type Organization struct { + Organization string `validate:"gte=1,lte=20,alphanum"` +} + +func (value *Organization) Value() string { + return value.Organization +} + +func (value *Organization) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewOrganization(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Organization{ + Organization: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewOrganization", + What: "organization must be between " + OrganizationMinCharactersLength + " to " + OrganizationMaxCharactersLength + " characters and be alphanumeric only", + Why: errors.Meta{ + "Organization": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/organization.mother.go b/pkg/context/shared/domain/valueobjs/organization.mother.go new file mode 100644 index 0000000..85f403e --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/organization.mother.go @@ -0,0 +1,33 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func OrganizationWithValidValue() models.ValueObject[string] { + value, err := NewOrganization(services.Create.Regex(`^[A-Za-z0-9]{1,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "OrganizationWithValidValue") + } + + return value +} + +func OrganizationWithInvalidLength() (string, error) { + value := "" + + _, err := NewOrganization(value) + + return value, err +} + +func OrganizationWithInvalidAlphanumeric() (string, error) { + value := "<>" + + _, err := NewOrganization(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/organization_test.go b/pkg/context/shared/domain/valueobjs/organization_test.go new file mode 100644 index 0000000..c15b9f9 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/organization_test.go @@ -0,0 +1,57 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type OrganizationValueObjectTestSuite struct { + suite.Suite +} + +func (suite *OrganizationValueObjectTestSuite) SetupTest() {} + +func (suite *OrganizationValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobjs.OrganizationWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewOrganization", + What: "organization must be between " + "1" + " to " + "20" + " characters and be alphanumeric only", + Why: errors.Meta{ + "Organization": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *OrganizationValueObjectTestSuite) TestWithInvalidAlphanumeric() { + value, err := valueobjs.OrganizationWithInvalidAlphanumeric() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewOrganization", + What: "organization must be between " + "1" + " to " + "20" + " characters and be alphanumeric only", + Why: errors.Meta{ + "Organization": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitOrganizationValueObjectSuite(t *testing.T) { + suite.Run(t, new(OrganizationValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/service.go b/pkg/context/shared/domain/valueobjs/service.go new file mode 100644 index 0000000..9a23381 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/service.go @@ -0,0 +1,45 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const ServiceMinCharactersLength = "1" +const ServiceMaxCharactersLength = "20" + +type Service struct { + Service string `validate:"gte=1,lte=20,alphanum"` +} + +func (value *Service) Value() string { + return value.Service +} + +func (value *Service) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewService(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Service{ + Service: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewService", + What: "service must be between " + ServiceMinCharactersLength + " to " + ServiceMaxCharactersLength + " characters and be alphanumeric only", + Why: errors.Meta{ + "Service": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/service.mother.go b/pkg/context/shared/domain/valueobjs/service.mother.go new file mode 100644 index 0000000..09e60c5 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/service.mother.go @@ -0,0 +1,33 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func ServiceWithValidValue() models.ValueObject[string] { + value, err := NewService(services.Create.Regex(`^[A-Za-z0-9]{1,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "ServiceWithValidValue") + } + + return value +} + +func ServiceWithInvalidLength() (string, error) { + value := "" + + _, err := NewService(value) + + return value, err +} + +func ServiceWithInvalidAlphanumeric() (string, error) { + value := "<>" + + _, err := NewService(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/service_test.go b/pkg/context/shared/domain/valueobjs/service_test.go new file mode 100644 index 0000000..4a5217b --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/service_test.go @@ -0,0 +1,57 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type ServiceValueObjectTestSuite struct { + suite.Suite +} + +func (suite *ServiceValueObjectTestSuite) SetupTest() {} + +func (suite *ServiceValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobjs.ServiceWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewService", + What: "service must be between " + "1" + " to " + "20" + " characters and be alphanumeric only", + Why: errors.Meta{ + "Service": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *ServiceValueObjectTestSuite) TestWithInvalidAlphanumeric() { + value, err := valueobjs.ServiceWithInvalidAlphanumeric() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewService", + What: "service must be between " + "1" + " to " + "20" + " characters and be alphanumeric only", + Why: errors.Meta{ + "Service": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitServiceValueObjectSuite(t *testing.T) { + suite.Run(t, new(ServiceValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/status.go b/pkg/context/shared/domain/valueobjs/status.go new file mode 100644 index 0000000..768b8f1 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/status.go @@ -0,0 +1,44 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +var StatusOneOf = []string{"queued", "succeeded", "failed", "done"} + +type Status struct { + Status string `validate:"oneof=queued succeeded failed done"` +} + +func (value *Status) Value() string { + return value.Status +} + +func (value *Status) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewStatus(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Status{ + Status: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewStatus", + What: "status must be only one of these values: " + strings.Join(StatusOneOf, ", "), + Why: errors.Meta{ + "Status": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/status.mother.go b/pkg/context/shared/domain/valueobjs/status.mother.go new file mode 100644 index 0000000..73e1134 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/status.mother.go @@ -0,0 +1,25 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func StatusWithValidValue() models.ValueObject[string] { + value, err := NewStatus(services.Create.RandomString([]string{"queued", "succeeded", "failed", "done"})) + + if err != nil { + errors.Panic(err.Error(), "StatusWithValidValue") + } + + return value +} + +func StatusWithInvalidValue() (string, error) { + value := "x" + + _, err := NewStatus(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/status_test.go b/pkg/context/shared/domain/valueobjs/status_test.go new file mode 100644 index 0000000..6a25366 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/status_test.go @@ -0,0 +1,38 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type StatusValueObjectTestSuite struct { + suite.Suite +} + +func (suite *StatusValueObjectTestSuite) SetupTest() {} + +func (suite *StatusValueObjectTestSuite) TestWithInvalidValue() { + value, err := valueobjs.StatusWithInvalidValue() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewStatus", + What: "status must be only one of these values: queued, succeeded, failed, done", + Why: errors.Meta{ + "Status": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitStatusValueObjectSuite(t *testing.T) { + suite.Run(t, new(StatusValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/type.go b/pkg/context/shared/domain/valueobjs/type.go new file mode 100644 index 0000000..b1f3e90 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/type.go @@ -0,0 +1,44 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +var TypeOneOf = []string{"event", "command"} + +type Type struct { + Type string `validate:"oneof=event command"` +} + +func (value *Type) Value() string { + return value.Type +} + +func (value *Type) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewType(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Type{ + Type: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewType", + What: "type must be only one of these values: " + strings.Join(TypeOneOf, ", "), + Why: errors.Meta{ + "Type": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/type.mother.go b/pkg/context/shared/domain/valueobjs/type.mother.go new file mode 100644 index 0000000..5b4aa1a --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/type.mother.go @@ -0,0 +1,25 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func TypeWithValidValue() models.ValueObject[string] { + value, err := NewType(services.Create.RandomString([]string{"event", "command"})) + + if err != nil { + errors.Panic(err.Error(), "TypeWithValidValue") + } + + return value +} + +func TypeWithInvalidValue() (string, error) { + value := "x" + + _, err := NewType(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/type_test.go b/pkg/context/shared/domain/valueobjs/type_test.go new file mode 100644 index 0000000..f3eeff2 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/type_test.go @@ -0,0 +1,38 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type TypeValueObjectTestSuite struct { + suite.Suite +} + +func (suite *TypeValueObjectTestSuite) SetupTest() {} + +func (suite *TypeValueObjectTestSuite) TestWithInvalidValue() { + value, err := valueobjs.TypeWithInvalidValue() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewType", + What: "type must be only one of these values: event, command", + Why: errors.Meta{ + "Type": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitTypeValueObjectSuite(t *testing.T) { + suite.Run(t, new(TypeValueObjectTestSuite)) +} diff --git a/pkg/context/shared/domain/valueobjs/version.go b/pkg/context/shared/domain/valueobjs/version.go new file mode 100644 index 0000000..1105152 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/version.go @@ -0,0 +1,42 @@ +package valueobjs + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +type Version struct { + Version string `validate:"number"` +} + +func (value *Version) Value() string { + return value.Version +} + +func (value *Version) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewVersion(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Version{ + Version: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewVersion", + What: "version must be numeric only", + Why: errors.Meta{ + "Version": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/shared/domain/valueobjs/version.mother.go b/pkg/context/shared/domain/valueobjs/version.mother.go new file mode 100644 index 0000000..3281277 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/version.mother.go @@ -0,0 +1,25 @@ +package valueobjs + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func VersionWithValidValue() models.ValueObject[string] { + value, err := NewVersion(services.Create.Regex(`^[0-9]{1,2}$`)) + + if err != nil { + errors.Panic(err.Error(), "VersionWithValidValue") + } + + return value +} + +func VersionWithInvalidValue() (string, error) { + value := "x" + + _, err := NewVersion(value) + + return value, err +} diff --git a/pkg/context/shared/domain/valueobjs/version_test.go b/pkg/context/shared/domain/valueobjs/version_test.go new file mode 100644 index 0000000..faca879 --- /dev/null +++ b/pkg/context/shared/domain/valueobjs/version_test.go @@ -0,0 +1,38 @@ +package valueobjs_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" + "github.com/stretchr/testify/suite" +) + +type VersionValueObjectTestSuite struct { + suite.Suite +} + +func (suite *VersionValueObjectTestSuite) SetupTest() {} + +func (suite *VersionValueObjectTestSuite) TestWithInvalidValue() { + value, err := valueobjs.VersionWithInvalidValue() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewVersion", + What: "version must be numeric only", + Why: errors.Meta{ + "Version": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitVersionValueObjectSuite(t *testing.T) { + suite.Run(t, new(VersionValueObjectTestSuite)) +} diff --git a/pkg/context/shared/infrastructure/authentications/jwt.go b/pkg/context/shared/infrastructure/authentications/jwt.go new file mode 100644 index 0000000..66a7d19 --- /dev/null +++ b/pkg/context/shared/infrastructure/authentications/jwt.go @@ -0,0 +1,52 @@ +package authentications + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/golang-jwt/jwt/v5" +) + +type Payload = map[string]any + +type JWT struct { + secretKey []byte +} + +func (auth *JWT) Generate(payload map[string]any) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(payload)) + + signature, err := token.SignedString(auth.secretKey) + + if err != nil { + return "", errors.NewInternal(&errors.Bubble{ + Where: "Generate", + What: "failure to sign a jwt", + Who: err, + }) + } + + return signature, nil +} + +func (auth *JWT) Validate(signature string) (jwt.MapClaims, error) { + token, _ := jwt.Parse(signature, func(token *jwt.Token) (any, error) { + return auth.secretKey, nil + }) + + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + return claims, nil + } + + return nil, errors.NewFailure(&errors.Bubble{ + Where: "Validate", + What: "invalid jwt signature", + Why: errors.Meta{ + "JWT": signature, + }, + }) +} + +func NewJWT(secretKey string) *JWT { + return &JWT{ + secretKey: []byte(secretKey), + } +} diff --git a/pkg/context/shared/infrastructure/communications/broker.mock.go b/pkg/context/shared/infrastructure/communications/broker.mock.go new file mode 100644 index 0000000..b0fb66f --- /dev/null +++ b/pkg/context/shared/infrastructure/communications/broker.mock.go @@ -0,0 +1,35 @@ +package communications + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/stretchr/testify/mock" +) + +type BrokerMock struct { + mock.Mock +} + +func (broker *BrokerMock) PublishMessages(messages []*messages.Message) error { + broker.Called(messages) + return nil +} + +func (broker *BrokerMock) AddRouter(router *messages.Router) error { + broker.Called(router) + return nil +} + +func (broker *BrokerMock) AddQueue(queue *messages.Queue) error { + broker.Called(queue) + return nil +} + +func (broker *BrokerMock) AddQueueMessageBind(queue *messages.Queue, bindingKeys []string) error { + broker.Called(queue, bindingKeys) + return nil +} + +func (broker *BrokerMock) AddQueueConsumer(consumer messages.Consumer) error { + broker.Called(consumer) + return nil +} diff --git a/pkg/context/shared/infrastructure/communications/consumer.mock.go b/pkg/context/shared/infrastructure/communications/consumer.mock.go new file mode 100644 index 0000000..883cc47 --- /dev/null +++ b/pkg/context/shared/infrastructure/communications/consumer.mock.go @@ -0,0 +1,20 @@ +package communications + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/stretchr/testify/mock" +) + +type ConsumerMock struct { + mock.Mock +} + +func (consumer *ConsumerMock) SubscribedTo() []*messages.Queue { + args := consumer.Called() + return args.Get(0).([]*messages.Queue) +} + +func (consumer *ConsumerMock) On(message *messages.Message) error { + // TODO?(goroutine): consumer.Called(message) + return nil +} diff --git a/pkg/context/shared/infrastructure/communications/rabbitmq.broker_test.go b/pkg/context/shared/infrastructure/communications/rabbitmq.broker_test.go new file mode 100644 index 0000000..c05eb46 --- /dev/null +++ b/pkg/context/shared/infrastructure/communications/rabbitmq.broker_test.go @@ -0,0 +1,95 @@ +package communications_test + +import ( + "fmt" + "os" + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/communications" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/loggers" + "github.com/stretchr/testify/suite" +) + +type RabbitMQBrokerTestSuite struct { + suite.Suite + sut messages.Broker + logger *loggers.LoggerMock + router *messages.Router + queue *messages.Queue + consumer *communications.ConsumerMock + messages []*messages.Message +} + +func (suite *RabbitMQBrokerTestSuite) SetupTest() { + suite.logger = new(loggers.LoggerMock) + + uri := os.Getenv("BROKER_URI") + + suite.sut, _ = communications.NewRabbitMQ(uri, suite.logger) + + suite.router = &messages.Router{Name: "test"} + + queueName := messages.NewRecipientName(&messages.RecipientNameComponents{ + Service: "queue", + Entity: "queue", + Action: "assert", + Event: "test", + Status: "succeeded", + }) + + suite.queue = &messages.Queue{Name: queueName} + + suite.consumer = new(communications.ConsumerMock) + + messageRoutingKey := messages.NewRoutingKey(&messages.RoutingKeyComponents{ + Service: "publisher", + Version: "1", + Type: messages.Type.Event, + Entity: "publisher", + Event: "test", + Status: messages.Status.Succeeded, + }) + + messageAttributes := messages.Attributes{} + + messageMeta := messages.Meta{} + + message := messages.NewMessage(messageRoutingKey, messageAttributes, messageMeta) + + message.Id = "0" + + message.OccurredOn = "0" + + suite.messages = append(suite.messages, message) +} + +func (suite *RabbitMQBrokerTestSuite) TestBroker() { + suite.NoError(suite.sut.AddRouter(suite.router)) + + suite.NoError(suite.sut.AddQueue(suite.queue)) + + bindingKeys := []string{"#.event.#.test.succeeded"} + + bindingSucceeded := fmt.Sprintf("binding queue [%s] to exchange [%s] with binding key [%s]", suite.queue.Name, suite.router.Name, bindingKeys[0]) + + suite.logger.Mock.On("Info", bindingSucceeded) + + suite.NoError(suite.sut.AddQueueMessageBind(suite.queue, bindingKeys)) + + suite.consumer.Mock.On("SubscribedTo").Return([]*messages.Queue{suite.queue}) + + suite.NoError(suite.sut.AddQueueConsumer(suite.consumer)) + + // TODO?(goroutine): suite.consumer.Mock.On("On", suite.messages[0]) + + suite.NoError(suite.sut.PublishMessages(suite.messages)) + + suite.logger.AssertExpectations(suite.T()) + + suite.consumer.AssertExpectations(suite.T()) +} + +func TestIntegrationRabbitMQBrokerSuite(t *testing.T) { + suite.Run(t, new(RabbitMQBrokerTestSuite)) +} diff --git a/pkg/context/shared/infrastructure/communications/rabbitmq.go b/pkg/context/shared/infrastructure/communications/rabbitmq.go new file mode 100644 index 0000000..1ce12ef --- /dev/null +++ b/pkg/context/shared/infrastructure/communications/rabbitmq.go @@ -0,0 +1,288 @@ +package communications + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/google/uuid" + amqp "github.com/rabbitmq/amqp091-go" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" +) + +type RabbitMQ struct { + *amqp.Connection + *amqp.Channel + models.Logger + exchange string +} + +func (rmq *RabbitMQ) AddRouter(router *messages.Router) error { + err := rmq.Channel.ExchangeDeclare( + router.Name, + "topic", + true, + false, + false, + false, + nil, + ) + + rmq.exchange = router.Name + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "AddRouter", + What: "failure to declare a router", + Why: errors.Meta{ + "Router": router.Name, + }, + Who: err, + }) + } + + return nil +} + +func (rmq *RabbitMQ) AddQueue(queue *messages.Queue) error { + _, err := rmq.Channel.QueueDeclare( + queue.Name, + true, + false, + false, + false, + nil, + ) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "AddQueue", + What: "failure to declare a queue", + Why: errors.Meta{ + "Queue": queue.Name, + }, + Who: err, + }) + } + + return nil +} + +func (rmq *RabbitMQ) AddQueueMessageBind(queue *messages.Queue, bindingKeys []string) error { + var errWrap error + + for _, bindingKey := range bindingKeys { + rmq.Logger.Info(fmt.Sprintf("binding queue [%s] to exchange [%s] with binding key [%s]", queue.Name, rmq.exchange, bindingKey)) + + err := rmq.Channel.QueueBind( + queue.Name, + bindingKey, + rmq.exchange, + false, + nil) + + if err != nil { + errToWrap := errors.NewInternal(&errors.Bubble{ + Where: "AddQueueMessageBind", + What: "failure to bind a queue", + Why: errors.Meta{ + "Queue": queue.Name, + "Binding Key": bindingKey, + "Exchange": rmq.exchange, + }, + Who: err, + }) + + errWrap = errors.Join(errWrap, errToWrap) + } + } + + if errWrap != nil { + return errors.BubbleUp(errWrap, "AddQueueMessageBind") + } + + return nil +} + +func (rmq *RabbitMQ) AddQueueConsumer(consumer messages.Consumer) error { + var errWrap error + + for _, queue := range consumer.SubscribedTo() { + deliveries, err := rmq.Channel.Consume( + queue.Name, + "", + false, + false, + false, + false, + nil, + ) + + if err != nil { + errToWrap := errors.NewInternal(&errors.Bubble{ + Where: "AddQueueConsumer", + What: "failure to register a consumer", + Why: errors.Meta{ + "Queue": queue.Name, + "Exchange": rmq.exchange, + }, + Who: err, + }) + + errWrap = errors.Join(errWrap, errToWrap) + + continue + } + + go func() { + for delivery := range deliveries { + message := new(messages.Message) + + err := json.Unmarshal(delivery.Body, message) + + if err != nil { + rmq.Logger.Error(fmt.Sprintf("failed to deliver a message with id [%s] from queue [%s]", message.Id, queue.Name)) + continue + } + + err = consumer.On(message) + + if err != nil { + rmq.Logger.Error(fmt.Sprintf("failed to consume a message with id [%s] from queue [%s]", message.Id, queue.Name)) + continue + } + + delivery.Ack(false) + } + }() + } + + if errWrap != nil { + return errors.BubbleUp(errWrap, "AddQueueConsumer") + } + + return nil +} + +func (rmq *RabbitMQ) PublishMessages(messages []*messages.Message) error { + var errWrap error + + for _, message := range messages { + if message.Id == "" { + message.Id = uuid.NewString() + } + + if message.OccurredOn == "" { + message.OccurredOn = time.Now().UTC().Format(time.RFC3339Nano) + } + + body, err := json.Marshal(message) + + if err != nil { + errToWrap := errors.NewInternal(&errors.Bubble{ + Where: "PublishMessages", + What: "cannot encode message to json", + Why: errors.Meta{ + "Exchange": rmq.exchange, + "Message": message.Id, + }, + Who: err, + }) + + errWrap = errors.Join(errWrap, errToWrap) + + continue + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + + defer cancel() + + err = rmq.Channel.PublishWithContext(ctx, + rmq.exchange, + message.Type, + false, + false, + amqp.Publishing{ + DeliveryMode: amqp.Persistent, + ContentType: "application/json", + Body: body, + }) + + if err != nil { + errToWrap := errors.NewInternal(&errors.Bubble{ + Where: "PublishMessages", + What: "failure to publish a message", + Why: errors.Meta{ + "Exchange": rmq.exchange, + "Message": message.Id, + }, + Who: err, + }) + + errWrap = errors.Join(errWrap, errToWrap) + } + } + + if errWrap != nil { + return errors.BubbleUp(errWrap, "PublishMessages") + } + + return nil +} + +func CloseRabbitMQ(rmq *RabbitMQ) error { + err := rmq.Channel.Close() + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "CloseRabbitMQ", + What: "failure to close channel", + Who: err, + }) + } + + err = rmq.Connection.Close() + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "CloseRabbitMQ", + What: "failure to close rabbitmq connection", + Who: err, + }) + } + + return nil +} + +func NewRabbitMQ(uri string, logger models.Logger) (messages.Broker, error) { + conn, err := amqp.Dial(uri) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "NewRabbitMQ", + What: "failure connecting to rabbitmq", + Who: err, + }) + } + + ch, err := conn.Channel() + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "NewRabbitMQ", + What: "failure to open a channel", + Who: err, + }) + } + + return &RabbitMQ{ + Connection: conn, + Channel: ch, + Logger: logger, + }, nil +} diff --git a/pkg/context/shared/infrastructure/loggers/logger.go b/pkg/context/shared/infrastructure/loggers/logger.go new file mode 100644 index 0000000..fd6b110 --- /dev/null +++ b/pkg/context/shared/infrastructure/loggers/logger.go @@ -0,0 +1,23 @@ +package loggers + +import ( + "log" +) + +type Logger struct{} + +func (logger *Logger) Debug(message string) { + log.Print(message) +} + +func (logger *Logger) Error(message string) { + log.Print(message) +} + +func (logger *Logger) Fatal(message string) { + log.Fatal(message) +} + +func (logger *Logger) Info(message string) { + log.Print(message) +} diff --git a/pkg/context/shared/infrastructure/loggers/logger.mock.go b/pkg/context/shared/infrastructure/loggers/logger.mock.go new file mode 100644 index 0000000..16bbc9a --- /dev/null +++ b/pkg/context/shared/infrastructure/loggers/logger.mock.go @@ -0,0 +1,25 @@ +package loggers + +import ( + "github.com/stretchr/testify/mock" +) + +type LoggerMock struct { + mock.Mock +} + +func (logger *LoggerMock) Debug(message string) { + logger.Called(message) +} + +func (logger *LoggerMock) Error(message string) { + logger.Called(message) +} + +func (logger *LoggerMock) Fatal(message string) { + logger.Called(message) +} + +func (logger *LoggerMock) Info(message string) { + logger.Called(message) +} diff --git a/pkg/context/shared/infrastructure/persistences/mongo.go b/pkg/context/shared/infrastructure/persistences/mongo.go new file mode 100644 index 0000000..ecc2058 --- /dev/null +++ b/pkg/context/shared/infrastructure/persistences/mongo.go @@ -0,0 +1,93 @@ +package persistences + +import ( + "context" + "regexp" + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +type MongoDB struct { + *mongo.Client + *mongo.Database +} + +func NewMongoDatabase(uri, databaseName string) (*MongoDB, error) { + var err error + + clientOptions := options.Client().ApplyURI(uri) + + client, err := mongo.Connect(context.Background(), clientOptions) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "NewMongoDatabase", + What: "failure to create a mongodb client", + Who: err, + }) + } + + err = client.Ping(context.Background(), nil) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "NewMongoDatabase", + What: "failure connecting to mongodb", + Who: err, + }) + } + + return &MongoDB{ + Client: client, + Database: client.Database(databaseName), + }, nil +} + +func HandleMongoDuplicateKeyError(err error) error { + re := regexp.MustCompile(`{ [A-Za-z0-9]+:`) + + rawField := re.FindString(err.Error()) + + toTitle := cases.Title(language.English) + + field := toTitle.String(strings.TrimSuffix(strings.Split(rawField, " ")[1], ":")) + + return errors.NewAlreadyExist(&errors.Bubble{ + Where: "HandleMongoDuplicateKeyError", + What: "already registered", + Why: errors.Meta{ + "Field": field, + }, + Who: err, + }) +} + +func HandleMongoDocumentNotFound(index string, err error) error { + return errors.NewNotExist(&errors.Bubble{ + Where: "HandleMongoDocumentNotFound", + What: "not found", + Why: errors.Meta{ + "Index": index, + }, + Who: err, + }) +} + +func CloseMongoDatabase(ctx context.Context, mdb *MongoDB) error { + err := mdb.Client.Disconnect(ctx) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "CloseMongoDatabase", + What: "failure to close connection with mongodb", + Who: err, + }) + } + + return nil +} diff --git a/pkg/context/shared/infrastructure/transports/smtp.go b/pkg/context/shared/infrastructure/transports/smtp.go new file mode 100644 index 0000000..70ef300 --- /dev/null +++ b/pkg/context/shared/infrastructure/transports/smtp.go @@ -0,0 +1,21 @@ +package transports + +import ( + "net/smtp" +) + +type SMTP struct { + smtp.Auth + MIMEHeaders, SMTPServerURL, Username, Password, ServerURL string +} + +func NewSMTP(host, port, username, password, serverURL string) *SMTP { + return &SMTP{ + Auth: smtp.PlainAuth("", username, password, host), + MIMEHeaders: "MIME-version: 1.0;\n" + "Content-Type: text/html; charset=\"UTF-8\";\n\n", + SMTPServerURL: host + ":" + port, + Username: username, + Password: password, + ServerURL: serverURL, + } +} diff --git a/pkg/context/user/application/create/command.go b/pkg/context/user/application/create/command.go new file mode 100644 index 0000000..1431c5a --- /dev/null +++ b/pkg/context/user/application/create/command.go @@ -0,0 +1,5 @@ +package create + +type Command struct { + Id, Email, Username, Password string +} diff --git a/pkg/context/user/application/create/command.handler.go b/pkg/context/user/application/create/command.handler.go new file mode 100644 index 0000000..8a4fef5 --- /dev/null +++ b/pkg/context/user/application/create/command.handler.go @@ -0,0 +1,37 @@ +package create + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" +) + +type Handler struct { + models.UseCase[*aggregate.User, types.Empty] + messages.Broker +} + +func (handler *Handler) Handle(command *Command) error { + user, err := aggregate.NewUser(&aggregate.UserPrimitive{ + Id: command.Id, + Email: command.Email, + Username: command.Username, + Password: command.Password, + }) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + _, err = handler.UseCase.Run(user) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + handler.Broker.PublishMessages(user.PullMessages()) + + return nil +} diff --git a/pkg/context/user/application/create/command.handler_test.go b/pkg/context/user/application/create/command.handler_test.go new file mode 100644 index 0000000..4b95d39 --- /dev/null +++ b/pkg/context/user/application/create/command.handler_test.go @@ -0,0 +1,63 @@ +package create_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/communications" + "github.com/bastean/codexgo/pkg/context/user/application/create" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type CreateHandlerTestSuite struct { + suite.Suite + sut models.CommandHandler[*create.Command] + usecase models.UseCase[*aggregate.User, types.Empty] + repository *persistence.RepositoryMock + broker *communications.BrokerMock +} + +func (suite *CreateHandlerTestSuite) SetupTest() { + suite.broker = new(communications.BrokerMock) + + suite.repository = new(persistence.RepositoryMock) + + suite.usecase = &create.Create{ + Repository: suite.repository, + } + + suite.sut = &create.Handler{ + UseCase: suite.usecase, + Broker: suite.broker, + } +} + +func (suite *CreateHandlerTestSuite) TestCreate() { + command := create.RandomCommand() + + user, _ := aggregate.NewUser(&aggregate.UserPrimitive{ + Id: command.Id, + Email: command.Email, + Username: command.Username, + Password: command.Password, + }) + + messages := user.Messages + + suite.repository.On("Save", user) + + suite.broker.On("PublishMessages", messages) + + suite.NoError(suite.sut.Handle(command)) + + suite.repository.AssertExpectations(suite.T()) + + suite.broker.AssertExpectations(suite.T()) +} + +func TestUnitCreateHandlerSuite(t *testing.T) { + suite.Run(t, new(CreateHandlerTestSuite)) +} diff --git a/pkg/context/user/application/create/command.mother.go b/pkg/context/user/application/create/command.mother.go new file mode 100644 index 0000000..e4f7d24 --- /dev/null +++ b/pkg/context/user/application/create/command.mother.go @@ -0,0 +1,19 @@ +package create + +import ( + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomCommand() *Command { + id := valueobj.IdWithValidValue() + email := valueobj.EmailWithValidValue() + username := valueobj.UsernameWithValidValue() + password := valueobj.PasswordWithValidValue() + + return &Command{ + Id: id.Value(), + Email: email.Value(), + Username: username.Value(), + Password: password.Value(), + } +} diff --git a/pkg/context/user/application/create/create.go b/pkg/context/user/application/create/create.go new file mode 100644 index 0000000..f177532 --- /dev/null +++ b/pkg/context/user/application/create/create.go @@ -0,0 +1,22 @@ +package create + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" +) + +type Create struct { + model.Repository +} + +func (create *Create) Run(user *aggregate.User) (types.Empty, error) { + err := create.Repository.Save(user) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return nil, nil +} diff --git a/pkg/context/user/application/created/created.consumer.go b/pkg/context/user/application/created/created.consumer.go new file mode 100644 index 0000000..ab568f1 --- /dev/null +++ b/pkg/context/user/application/created/created.consumer.go @@ -0,0 +1,49 @@ +package created + +import ( + "encoding/json" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/event" +) + +type Consumer struct { + models.UseCase[*event.CreatedSucceeded, types.Empty] + Queues []*messages.Queue +} + +func (consumer *Consumer) SubscribedTo() []*messages.Queue { + return consumer.Queues +} + +func (consumer *Consumer) On(message *messages.Message) error { + user := new(event.CreatedSucceeded) + + user.Attributes = new(event.CreatedSucceededAttributes) + + err := json.Unmarshal(message.Attributes, user.Attributes) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "On", + What: "failure to obtain message attributes", + Why: errors.Meta{ + "Id": message.Id, + "Routing Key": message.Type, + "Occurred On": message.OccurredOn, + }, + Who: err, + }) + } + + _, err = consumer.UseCase.Run(user) + + if err != nil { + return errors.BubbleUp(err, "On") + } + + return nil +} diff --git a/pkg/context/user/application/created/created.consumer_test.go b/pkg/context/user/application/created/created.consumer_test.go new file mode 100644 index 0000000..bbc2347 --- /dev/null +++ b/pkg/context/user/application/created/created.consumer_test.go @@ -0,0 +1,67 @@ +package created_test + +import ( + "encoding/json" + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/application/created" + "github.com/bastean/codexgo/pkg/context/user/domain/event" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/communication" + "github.com/stretchr/testify/suite" +) + +type CreatedConsumerTestSuite struct { + suite.Suite + sut messages.Consumer + usecase models.UseCase[*event.CreatedSucceeded, types.Empty] + transport *communication.TransportMock + queues []*messages.Queue +} + +func (suite *CreatedConsumerTestSuite) SetupTest() { + queueName := messages.NewRecipientName(&messages.RecipientNameComponents{ + Service: "queue", + Entity: "queue", + Action: "assert", + Event: "test", + Status: "succeeded", + }) + + suite.queues = append(suite.queues, &messages.Queue{ + Name: queueName, + }) + + suite.transport = new(communication.TransportMock) + + suite.usecase = &created.Created{ + Transport: suite.transport, + } + + suite.sut = &created.Consumer{ + UseCase: suite.usecase, + Queues: suite.queues, + } +} + +func (suite *CreatedConsumerTestSuite) TestCreatedSucceeded() { + message := event.RandomCreatedSucceeded() + + user := new(event.CreatedSucceeded) + + user.Attributes = new(event.CreatedSucceededAttributes) + + suite.NoError(json.Unmarshal(message.Attributes, user.Attributes)) + + suite.transport.On("Submit", user.Attributes) + + suite.NoError(suite.sut.On(message)) + + suite.transport.AssertExpectations(suite.T()) +} + +func TestUnitCreatedConsumerSuite(t *testing.T) { + suite.Run(t, new(CreatedConsumerTestSuite)) +} diff --git a/pkg/context/user/application/created/created.go b/pkg/context/user/application/created/created.go new file mode 100644 index 0000000..5414ece --- /dev/null +++ b/pkg/context/user/application/created/created.go @@ -0,0 +1,22 @@ +package created + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/event" +) + +type Created struct { + models.Transport +} + +func (created *Created) Run(user *event.CreatedSucceeded) (types.Empty, error) { + err := created.Transport.Submit(user.Attributes) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return nil, nil +} diff --git a/pkg/context/user/application/delete/command.go b/pkg/context/user/application/delete/command.go new file mode 100644 index 0000000..0cf11b1 --- /dev/null +++ b/pkg/context/user/application/delete/command.go @@ -0,0 +1,5 @@ +package delete + +type Command struct { + Id, Password string +} diff --git a/pkg/context/user/application/delete/command.handler.go b/pkg/context/user/application/delete/command.handler.go new file mode 100644 index 0000000..87f171c --- /dev/null +++ b/pkg/context/user/application/delete/command.handler.go @@ -0,0 +1,38 @@ +package delete + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +type Input struct { + Id, Password models.ValueObject[string] +} + +type Handler struct { + models.UseCase[*Input, types.Empty] +} + +func (handler *Handler) Handle(command *Command) error { + id, errId := valueobj.NewId(command.Id) + password, errPassword := valueobj.NewPassword(command.Password) + + err := errors.Join(errId, errPassword) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + _, err = handler.UseCase.Run(&Input{ + Id: id, + Password: password, + }) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + return nil +} diff --git a/pkg/context/user/application/delete/command.handler_test.go b/pkg/context/user/application/delete/command.handler_test.go new file mode 100644 index 0000000..a25d5c7 --- /dev/null +++ b/pkg/context/user/application/delete/command.handler_test.go @@ -0,0 +1,64 @@ +package delete_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/application/delete" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/cryptographic" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type DeleteHandlerTestSuite struct { + suite.Suite + sut models.CommandHandler[*delete.Command] + usecase models.UseCase[*delete.Input, types.Empty] + hashing *cryptographic.HashingMock + repository *persistence.RepositoryMock +} + +func (suite *DeleteHandlerTestSuite) SetupTest() { + suite.repository = new(persistence.RepositoryMock) + + suite.hashing = new(cryptographic.HashingMock) + + suite.usecase = &delete.Delete{ + Repository: suite.repository, + Hashing: suite.hashing, + } + + suite.sut = &delete.Handler{ + UseCase: suite.usecase, + } +} + +func (suite *DeleteHandlerTestSuite) TestDelete() { + user := aggregate.RandomUser() + + command := &delete.Command{ + Id: user.Id.Value(), + Password: user.Password.Value(), + } + + criteria := &model.RepositorySearchCriteria{ + Id: user.Id, + } + + suite.repository.On("Search", criteria).Return(user) + + suite.hashing.On("IsNotEqual", user.Password.Value(), user.Password.Value()).Return(false) + + suite.repository.On("Delete", user.Id) + + suite.NoError(suite.sut.Handle(command)) + + suite.repository.AssertExpectations(suite.T()) +} + +func TestUnitDeleteHandlerSuite(t *testing.T) { + suite.Run(t, new(DeleteHandlerTestSuite)) +} diff --git a/pkg/context/user/application/delete/command.mother.go b/pkg/context/user/application/delete/command.mother.go new file mode 100644 index 0000000..8b22b0c --- /dev/null +++ b/pkg/context/user/application/delete/command.mother.go @@ -0,0 +1,15 @@ +package delete + +import ( + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomCommand() *Command { + id := valueobj.IdWithValidValue() + password := valueobj.PasswordWithValidValue() + + return &Command{ + Id: id.Value(), + Password: password.Value(), + } +} diff --git a/pkg/context/user/application/delete/delete.go b/pkg/context/user/application/delete/delete.go new file mode 100644 index 0000000..05dc0a4 --- /dev/null +++ b/pkg/context/user/application/delete/delete.go @@ -0,0 +1,37 @@ +package delete + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/service" +) + +type Delete struct { + model.Repository + model.Hashing +} + +func (delete *Delete) Run(input *Input) (types.Empty, error) { + user, err := delete.Repository.Search(&model.RepositorySearchCriteria{ + Id: input.Id, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + err = service.IsPasswordInvalid(delete.Hashing, user.Password.Value(), input.Password.Value()) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + err = delete.Repository.Delete(user.Id) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return nil, nil +} diff --git a/pkg/context/user/application/login/login.go b/pkg/context/user/application/login/login.go new file mode 100644 index 0000000..d090289 --- /dev/null +++ b/pkg/context/user/application/login/login.go @@ -0,0 +1,31 @@ +package login + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/service" +) + +type Login struct { + model.Repository + model.Hashing +} + +func (login *Login) Run(input *Input) (*aggregate.User, error) { + user, err := login.Repository.Search(&model.RepositorySearchCriteria{ + Email: input.Email, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + err = service.IsPasswordInvalid(login.Hashing, user.Password.Value(), input.Password.Value()) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return user, nil +} diff --git a/pkg/context/user/application/login/query.go b/pkg/context/user/application/login/query.go new file mode 100644 index 0000000..f087799 --- /dev/null +++ b/pkg/context/user/application/login/query.go @@ -0,0 +1,5 @@ +package login + +type Query struct { + Email, Password string +} diff --git a/pkg/context/user/application/login/query.handler.go b/pkg/context/user/application/login/query.handler.go new file mode 100644 index 0000000..5f65ea7 --- /dev/null +++ b/pkg/context/user/application/login/query.handler.go @@ -0,0 +1,40 @@ +package login + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +type Input struct { + Email, Password models.ValueObject[string] +} + +type Handler struct { + models.UseCase[*Input, *aggregate.User] +} + +func (handler *Handler) Handle(query *Query) (*Response, error) { + email, errEmail := valueobj.NewEmail(query.Email) + password, errPassword := valueobj.NewPassword(query.Password) + + err := errors.Join(errEmail, errPassword) + + if err != nil { + return nil, errors.BubbleUp(err, "Handle") + } + + user, err := handler.UseCase.Run(&Input{ + Email: email, + Password: password, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "Handle") + } + + response := Response(*user.ToPrimitives()) + + return &response, nil +} diff --git a/pkg/context/user/application/login/query.handler_test.go b/pkg/context/user/application/login/query.handler_test.go new file mode 100644 index 0000000..5883145 --- /dev/null +++ b/pkg/context/user/application/login/query.handler_test.go @@ -0,0 +1,69 @@ +package login_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/application/login" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/cryptographic" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type LoginHandlerTestSuite struct { + suite.Suite + sut models.QueryHandler[*login.Query, *login.Response] + usecase models.UseCase[*login.Input, *aggregate.User] + hashing *cryptographic.HashingMock + repository *persistence.RepositoryMock +} + +func (suite *LoginHandlerTestSuite) SetupTest() { + suite.repository = new(persistence.RepositoryMock) + + suite.hashing = new(cryptographic.HashingMock) + + suite.usecase = &login.Login{ + Repository: suite.repository, + Hashing: suite.hashing, + } + + suite.sut = &login.Handler{ + UseCase: suite.usecase, + } +} + +func (suite *LoginHandlerTestSuite) TestLogin() { + user := aggregate.RandomUser() + + query := &login.Query{ + Email: user.Email.Value(), + Password: user.Password.Value(), + } + + criteria := &model.RepositorySearchCriteria{ + Email: user.Email, + } + + suite.repository.On("Search", criteria).Return(user) + + suite.hashing.On("IsNotEqual", user.Password.Value(), user.Password.Value()).Return(false) + + expected := user.ToPrimitives() + + actual, err := suite.sut.Handle(query) + + suite.NoError(err) + + suite.repository.AssertExpectations(suite.T()) + + suite.hashing.AssertExpectations(suite.T()) + + suite.EqualValues(expected, actual) +} + +func TestUnitLoginHandlerSuite(t *testing.T) { + suite.Run(t, new(LoginHandlerTestSuite)) +} diff --git a/pkg/context/user/application/login/query.mother.go b/pkg/context/user/application/login/query.mother.go new file mode 100644 index 0000000..0ac933f --- /dev/null +++ b/pkg/context/user/application/login/query.mother.go @@ -0,0 +1,15 @@ +package login + +import ( + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomQuery() *Query { + email := valueobj.EmailWithValidValue() + password := valueobj.PasswordWithValidValue() + + return &Query{ + Email: email.Value(), + Password: password.Value(), + } +} diff --git a/pkg/context/user/application/login/response.go b/pkg/context/user/application/login/response.go new file mode 100644 index 0000000..b7bf61e --- /dev/null +++ b/pkg/context/user/application/login/response.go @@ -0,0 +1,6 @@ +package login + +type Response struct { + Id, Email, Username, Password string + Verified bool +} diff --git a/pkg/context/user/application/read/query.go b/pkg/context/user/application/read/query.go new file mode 100644 index 0000000..0c83d00 --- /dev/null +++ b/pkg/context/user/application/read/query.go @@ -0,0 +1,5 @@ +package read + +type Query struct { + Id string +} diff --git a/pkg/context/user/application/read/query.handler.go b/pkg/context/user/application/read/query.handler.go new file mode 100644 index 0000000..87109d2 --- /dev/null +++ b/pkg/context/user/application/read/query.handler.go @@ -0,0 +1,30 @@ +package read + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +type Handler struct { + models.UseCase[models.ValueObject[string], *aggregate.User] +} + +func (handler *Handler) Handle(query *Query) (*Response, error) { + id, err := valueobj.NewId(query.Id) + + if err != nil { + return nil, errors.BubbleUp(err, "Handle") + } + + user, err := handler.UseCase.Run(id) + + if err != nil { + return nil, errors.BubbleUp(err, "Handle") + } + + response := Response(*user.ToPrimitives()) + + return &response, nil +} diff --git a/pkg/context/user/application/read/query.handler_test.go b/pkg/context/user/application/read/query.handler_test.go new file mode 100644 index 0000000..29cca3f --- /dev/null +++ b/pkg/context/user/application/read/query.handler_test.go @@ -0,0 +1,59 @@ +package read_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/application/read" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type ReadHandlerTestSuite struct { + suite.Suite + sut models.QueryHandler[*read.Query, *read.Response] + usecase models.UseCase[models.ValueObject[string], *aggregate.User] + repository *persistence.RepositoryMock +} + +func (suite *ReadHandlerTestSuite) SetupTest() { + suite.repository = new(persistence.RepositoryMock) + + suite.usecase = &read.Read{ + Repository: suite.repository, + } + + suite.sut = &read.Handler{ + UseCase: suite.usecase, + } +} + +func (suite *ReadHandlerTestSuite) TestLogin() { + user := aggregate.RandomUser() + + query := &read.Query{ + Id: user.Id.Value(), + } + + criteria := &model.RepositorySearchCriteria{ + Id: user.Id, + } + + suite.repository.On("Search", criteria).Return(user) + + expected := user.ToPrimitives() + + actual, err := suite.sut.Handle(query) + + suite.NoError(err) + + suite.repository.AssertExpectations(suite.T()) + + suite.EqualValues(expected, actual) +} + +func TestUnitReadHandlerSuite(t *testing.T) { + suite.Run(t, new(ReadHandlerTestSuite)) +} diff --git a/pkg/context/user/application/read/query.mother.go b/pkg/context/user/application/read/query.mother.go new file mode 100644 index 0000000..0bffba5 --- /dev/null +++ b/pkg/context/user/application/read/query.mother.go @@ -0,0 +1,13 @@ +package read + +import ( + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomQuery() *Query { + id := valueobj.IdWithValidValue() + + return &Query{ + Id: id.Value(), + } +} diff --git a/pkg/context/user/application/read/read.go b/pkg/context/user/application/read/read.go new file mode 100644 index 0000000..343e9c8 --- /dev/null +++ b/pkg/context/user/application/read/read.go @@ -0,0 +1,24 @@ +package read + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" +) + +type Read struct { + model.Repository +} + +func (read *Read) Run(id models.ValueObject[string]) (*aggregate.User, error) { + user, err := read.Repository.Search(&model.RepositorySearchCriteria{ + Id: id, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return user, nil +} diff --git a/pkg/context/user/application/read/response.go b/pkg/context/user/application/read/response.go new file mode 100644 index 0000000..c1eb152 --- /dev/null +++ b/pkg/context/user/application/read/response.go @@ -0,0 +1,6 @@ +package read + +type Response struct { + Id, Email, Username, Password string + Verified bool +} diff --git a/pkg/context/user/application/update/command.go b/pkg/context/user/application/update/command.go new file mode 100644 index 0000000..8daf24f --- /dev/null +++ b/pkg/context/user/application/update/command.go @@ -0,0 +1,5 @@ +package update + +type Command struct { + Id, Email, Username, Password, UpdatedPassword string +} diff --git a/pkg/context/user/application/update/command.handler.go b/pkg/context/user/application/update/command.handler.go new file mode 100644 index 0000000..d34e410 --- /dev/null +++ b/pkg/context/user/application/update/command.handler.go @@ -0,0 +1,52 @@ +package update + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +type Input struct { + User *aggregate.User + UpdatedPassword models.ValueObject[string] +} + +type Handler struct { + models.UseCase[*Input, types.Empty] +} + +func (handler *Handler) Handle(command *Command) error { + user, err := aggregate.NewUser(&aggregate.UserPrimitive{ + Id: command.Id, + Email: command.Email, + Username: command.Username, + Password: command.Password, + }) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + var updatedPassword models.ValueObject[string] + + if command.UpdatedPassword != "" { + updatedPassword, err = valueobj.NewPassword(command.UpdatedPassword) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + } + + _, err = handler.UseCase.Run(&Input{ + User: user, + UpdatedPassword: updatedPassword, + }) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + return nil +} diff --git a/pkg/context/user/application/update/command.handler_test.go b/pkg/context/user/application/update/command.handler_test.go new file mode 100644 index 0000000..fe8f52d --- /dev/null +++ b/pkg/context/user/application/update/command.handler_test.go @@ -0,0 +1,71 @@ +package update_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/application/update" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/cryptographic" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type UpdateHandlerTestSuite struct { + suite.Suite + sut models.CommandHandler[*update.Command] + usecase models.UseCase[*update.Input, types.Empty] + hashing *cryptographic.HashingMock + repository *persistence.RepositoryMock +} + +func (suite *UpdateHandlerTestSuite) SetupTest() { + suite.repository = new(persistence.RepositoryMock) + + suite.hashing = new(cryptographic.HashingMock) + + suite.usecase = &update.Update{ + Repository: suite.repository, + Hashing: suite.hashing, + } + + suite.sut = &update.Handler{ + UseCase: suite.usecase, + } +} + +func (suite *UpdateHandlerTestSuite) TestUpdate() { + command := update.RandomCommand() + + user, _ := aggregate.NewUser(&aggregate.UserPrimitive{ + Id: command.Id, + Email: command.Email, + Username: command.Username, + Password: command.UpdatedPassword, + }) + + idVO, _ := valueobj.NewId(command.Id) + + criteria := &model.RepositorySearchCriteria{ + Id: idVO, + } + + suite.repository.On("Search", criteria).Return(user) + + suite.hashing.On("IsNotEqual", user.Password.Value(), command.Password).Return(false) + + suite.repository.On("Update", user) + + suite.NoError(suite.sut.Handle(command)) + + suite.repository.AssertExpectations(suite.T()) + + suite.hashing.AssertExpectations(suite.T()) +} + +func TestUnitUpdateHandlerSuite(t *testing.T) { + suite.Run(t, new(UpdateHandlerTestSuite)) +} diff --git a/pkg/context/user/application/update/command.mother.go b/pkg/context/user/application/update/command.mother.go new file mode 100644 index 0000000..a01a478 --- /dev/null +++ b/pkg/context/user/application/update/command.mother.go @@ -0,0 +1,21 @@ +package update + +import ( + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomCommand() *Command { + id := valueobj.IdWithValidValue() + email := valueobj.EmailWithValidValue() + username := valueobj.UsernameWithValidValue() + password := valueobj.PasswordWithValidValue() + updatedPassword := valueobj.PasswordWithValidValue() + + return &Command{ + Id: id.Value(), + Email: email.Value(), + Username: username.Value(), + Password: password.Value(), + UpdatedPassword: updatedPassword.Value(), + } +} diff --git a/pkg/context/user/application/update/update.go b/pkg/context/user/application/update/update.go new file mode 100644 index 0000000..6084818 --- /dev/null +++ b/pkg/context/user/application/update/update.go @@ -0,0 +1,43 @@ +package update + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/service" +) + +type Update struct { + model.Repository + model.Hashing +} + +func (update *Update) Run(input *Input) (types.Empty, error) { + user, err := update.Repository.Search(&model.RepositorySearchCriteria{ + Id: input.User.Id, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + err = service.IsPasswordInvalid(update.Hashing, user.Password.Value(), input.User.Password.Value()) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + if input.UpdatedPassword != nil { + input.User.Password = input.UpdatedPassword + } + + input.User.Verified = user.Verified + + err = update.Repository.Update(input.User) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return nil, nil +} diff --git a/pkg/context/user/application/verify/command.go b/pkg/context/user/application/verify/command.go new file mode 100644 index 0000000..25b17f7 --- /dev/null +++ b/pkg/context/user/application/verify/command.go @@ -0,0 +1,5 @@ +package verify + +type Command struct { + Id string +} diff --git a/pkg/context/user/application/verify/command.handler.go b/pkg/context/user/application/verify/command.handler.go new file mode 100644 index 0000000..c03f2d5 --- /dev/null +++ b/pkg/context/user/application/verify/command.handler.go @@ -0,0 +1,28 @@ +package verify + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +type Handler struct { + models.UseCase[models.ValueObject[string], types.Empty] +} + +func (handler *Handler) Handle(command *Command) error { + idVO, err := valueobj.NewId(command.Id) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + _, err = handler.UseCase.Run(idVO) + + if err != nil { + return errors.BubbleUp(err, "Handle") + } + + return nil +} diff --git a/pkg/context/user/application/verify/command.handler_test.go b/pkg/context/user/application/verify/command.handler_test.go new file mode 100644 index 0000000..94c3635 --- /dev/null +++ b/pkg/context/user/application/verify/command.handler_test.go @@ -0,0 +1,59 @@ +package verify_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/application/verify" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type VerifyHandlerTestSuite struct { + suite.Suite + sut models.CommandHandler[*verify.Command] + usecase models.UseCase[models.ValueObject[string], types.Empty] + repository *persistence.RepositoryMock +} + +func (suite *VerifyHandlerTestSuite) SetupTest() { + suite.repository = new(persistence.RepositoryMock) + + suite.usecase = &verify.Verify{ + Repository: suite.repository, + } + + suite.sut = &verify.Handler{ + UseCase: suite.usecase, + } +} + +func (suite *VerifyHandlerTestSuite) TestVerify() { + command := verify.RandomCommand() + + user := aggregate.RandomUser() + + idVO, _ := valueobj.NewId(command.Id) + + user.Id = idVO + + criteria := &model.RepositorySearchCriteria{ + Id: idVO, + } + + suite.repository.On("Search", criteria).Return(user) + + suite.repository.On("Verify", idVO) + + suite.NoError(suite.sut.Handle(command)) + + suite.repository.AssertExpectations(suite.T()) +} + +func TestUnitVerifyHandlerSuite(t *testing.T) { + suite.Run(t, new(VerifyHandlerTestSuite)) +} diff --git a/pkg/context/user/application/verify/command.mother.go b/pkg/context/user/application/verify/command.mother.go new file mode 100644 index 0000000..ef7f11a --- /dev/null +++ b/pkg/context/user/application/verify/command.mother.go @@ -0,0 +1,13 @@ +package verify + +import ( + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomCommand() *Command { + id := valueobj.IdWithValidValue() + + return &Command{ + Id: id.Value(), + } +} diff --git a/pkg/context/user/application/verify/verify.go b/pkg/context/user/application/verify/verify.go new file mode 100644 index 0000000..51030f9 --- /dev/null +++ b/pkg/context/user/application/verify/verify.go @@ -0,0 +1,34 @@ +package verify + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/types" + "github.com/bastean/codexgo/pkg/context/user/domain/model" +) + +type Verify struct { + model.Repository +} + +func (verify *Verify) Run(id models.ValueObject[string]) (types.Empty, error) { + user, err := verify.Repository.Search(&model.RepositorySearchCriteria{ + Id: id, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + if user.Verified.Value() { + return nil, nil + } + + err = verify.Repository.Verify(id) + + if err != nil { + return nil, errors.BubbleUp(err, "Run") + } + + return nil, nil +} diff --git a/pkg/context/user/domain/aggregate/user.mother.go b/pkg/context/user/domain/aggregate/user.mother.go new file mode 100644 index 0000000..af5a72c --- /dev/null +++ b/pkg/context/user/domain/aggregate/user.mother.go @@ -0,0 +1,26 @@ +package aggregate + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomUser() *User { + id := valueobj.IdWithValidValue() + email := valueobj.EmailWithValidValue() + username := valueobj.UsernameWithValidValue() + password := valueobj.PasswordWithValidValue() + + user, err := NewUser(&UserPrimitive{ + Id: id.Value(), + Email: email.Value(), + Username: username.Value(), + Password: password.Value(), + }) + + if err != nil { + errors.Panic(err.Error(), "RandomUser") + } + + return user +} diff --git a/pkg/context/user/domain/aggregate/user.root.go b/pkg/context/user/domain/aggregate/user.root.go new file mode 100644 index 0000000..dd21433 --- /dev/null +++ b/pkg/context/user/domain/aggregate/user.root.go @@ -0,0 +1,91 @@ +package aggregate + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/aggregates" + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/event" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +type User struct { + *aggregates.AggregateRoot + Id, Email, Username, Password models.ValueObject[string] + Verified models.ValueObject[bool] +} + +type UserPrimitive struct { + Id, Email, Username, Password string + Verified bool +} + +func create(primitive *UserPrimitive) (*User, error) { + aggregateRoot := aggregates.NewAggregateRoot() + + idVO, errId := valueobj.NewId(primitive.Id) + emailVO, errEmail := valueobj.NewEmail(primitive.Email) + usernameVO, errUsername := valueobj.NewUsername(primitive.Username) + passwordVO, errPassword := valueobj.NewPassword(primitive.Password) + verifiedVO, errVerified := valueobj.NewVerified(primitive.Verified) + + err := errors.Join(errId, errEmail, errUsername, errPassword, errVerified) + + if err != nil { + return nil, errors.BubbleUp(err, "create") + } + + return &User{ + AggregateRoot: aggregateRoot, + Id: idVO, + Email: emailVO, + Username: usernameVO, + Password: passwordVO, + Verified: verifiedVO, + }, nil +} + +func (user *User) ToPrimitives() *UserPrimitive { + return &UserPrimitive{ + Id: user.Id.Value(), + Email: user.Email.Value(), + Username: user.Username.Value(), + Password: user.Password.Value(), + Verified: user.Verified.Value(), + } +} + +func FromPrimitives(primitive *UserPrimitive) (*User, error) { + user, err := create(primitive) + + if err != nil { + return nil, errors.BubbleUp(err, "FromPrimitives") + } + + return user, nil +} + +func NewUser(primitive *UserPrimitive) (*User, error) { + primitive.Verified = false + + user, err := create(primitive) + + if err != nil { + return nil, errors.BubbleUp(err, "NewUser") + } + + message, err := event.NewCreatedSucceeded(&event.CreatedSucceeded{ + Attributes: &event.CreatedSucceededAttributes{ + Id: user.Id.Value(), + Email: user.Email.Value(), + Username: user.Username.Value(), + }, + }) + + if err != nil { + return nil, errors.BubbleUp(err, "NewUser") + } + + user.RecordMessage(message) + + return user, nil +} diff --git a/pkg/context/user/domain/event/created.go b/pkg/context/user/domain/event/created.go new file mode 100644 index 0000000..8ab742c --- /dev/null +++ b/pkg/context/user/domain/event/created.go @@ -0,0 +1,42 @@ +package event + +import ( + "encoding/json" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" +) + +var CreatedSucceededTypeRoutingKey = messages.NewRoutingKey(&messages.RoutingKeyComponents{ + Service: "user", + Version: "1", + Type: messages.Type.Event, + Entity: "user", + Event: "created", + Status: messages.Status.Succeeded, +}) + +type CreatedSucceededAttributes struct { + Id, Email, Username string +} + +type CreatedSucceeded struct { + Attributes *CreatedSucceededAttributes +} + +func NewCreatedSucceeded(event *CreatedSucceeded) (*messages.Message, error) { + attributes, err := json.Marshal(event.Attributes) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "NewCreatedSucceeded", + What: "failure to create event message attributes", + Why: errors.Meta{ + "Routing Key": CreatedSucceededTypeRoutingKey, + }, + Who: err, + }) + } + + return messages.NewMessage(CreatedSucceededTypeRoutingKey, attributes, messages.Meta{}), nil +} diff --git a/pkg/context/user/domain/event/created.mother.go b/pkg/context/user/domain/event/created.mother.go new file mode 100644 index 0000000..cac8cc6 --- /dev/null +++ b/pkg/context/user/domain/event/created.mother.go @@ -0,0 +1,27 @@ +package event + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/messages" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" +) + +func RandomCreatedSucceeded() *messages.Message { + id := valueobj.IdWithValidValue() + email := valueobj.EmailWithValidValue() + username := valueobj.UsernameWithValidValue() + + event, err := NewCreatedSucceeded(&CreatedSucceeded{ + Attributes: &CreatedSucceededAttributes{ + Id: id.Value(), + Email: email.Value(), + Username: username.Value(), + }, + }) + + if err != nil { + errors.Panic(err.Error(), "RandomCreatedSucceeded") + } + + return event +} diff --git a/pkg/context/user/domain/model/hashing.go b/pkg/context/user/domain/model/hashing.go new file mode 100644 index 0000000..9010d45 --- /dev/null +++ b/pkg/context/user/domain/model/hashing.go @@ -0,0 +1,6 @@ +package model + +type Hashing interface { + Hash(plain string) (string, error) + IsNotEqual(hashed, plain string) bool +} diff --git a/pkg/context/user/domain/model/repository.go b/pkg/context/user/domain/model/repository.go new file mode 100644 index 0000000..303b6c1 --- /dev/null +++ b/pkg/context/user/domain/model/repository.go @@ -0,0 +1,18 @@ +package model + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" +) + +type RepositorySearchCriteria struct { + Id, Email models.ValueObject[string] +} + +type Repository interface { + Save(user *aggregate.User) error + Verify(id models.ValueObject[string]) error + Update(user *aggregate.User) error + Delete(id models.ValueObject[string]) error + Search(criteria *RepositorySearchCriteria) (*aggregate.User, error) +} diff --git a/pkg/context/user/domain/service/password.go b/pkg/context/user/domain/service/password.go new file mode 100644 index 0000000..55d75b9 --- /dev/null +++ b/pkg/context/user/domain/service/password.go @@ -0,0 +1,17 @@ +package service + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/model" +) + +func IsPasswordInvalid(hashing model.Hashing, hashed, plain string) error { + if hashing.IsNotEqual(hashed, plain) { + return errors.NewFailure(&errors.Bubble{ + Where: "IsPasswordInvalid", + What: "passwords do not match", + }) + } + + return nil +} diff --git a/pkg/context/user/domain/valueobj/email.go b/pkg/context/user/domain/valueobj/email.go new file mode 100644 index 0000000..d422a15 --- /dev/null +++ b/pkg/context/user/domain/valueobj/email.go @@ -0,0 +1,10 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" +) + +func NewEmail(email string) (models.ValueObject[string], error) { + return valueobjs.NewEmail(email) +} diff --git a/pkg/context/user/domain/valueobj/email.mother.go b/pkg/context/user/domain/valueobj/email.mother.go new file mode 100644 index 0000000..db45b17 --- /dev/null +++ b/pkg/context/user/domain/valueobj/email.mother.go @@ -0,0 +1,25 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func EmailWithValidValue() models.ValueObject[string] { + value, err := NewEmail(services.Create.Email()) + + if err != nil { + errors.Panic(err.Error(), "EmailWithValidValue") + } + + return value +} + +func EmailWithInvalidValue() (string, error) { + value := "x" + + _, err := NewEmail(value) + + return value, err +} diff --git a/pkg/context/user/domain/valueobj/email_test.go b/pkg/context/user/domain/valueobj/email_test.go new file mode 100644 index 0000000..5491366 --- /dev/null +++ b/pkg/context/user/domain/valueobj/email_test.go @@ -0,0 +1,38 @@ +package valueobj_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/stretchr/testify/suite" +) + +type EmailValueObjectTestSuite struct { + suite.Suite +} + +func (suite *EmailValueObjectTestSuite) SetupTest() {} + +func (suite *EmailValueObjectTestSuite) TestWithInvalidValue() { + value, err := valueobj.EmailWithInvalidValue() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewEmail", + What: "invalid email format", + Why: errors.Meta{ + "Email": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitEmailValueObjectSuite(t *testing.T) { + suite.Run(t, new(EmailValueObjectTestSuite)) +} diff --git a/pkg/context/user/domain/valueobj/id.go b/pkg/context/user/domain/valueobj/id.go new file mode 100644 index 0000000..4735b52 --- /dev/null +++ b/pkg/context/user/domain/valueobj/id.go @@ -0,0 +1,10 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/valueobjs" +) + +func NewId(id string) (models.ValueObject[string], error) { + return valueobjs.NewId(id) +} diff --git a/pkg/context/user/domain/valueobj/id.mother.go b/pkg/context/user/domain/valueobj/id.mother.go new file mode 100644 index 0000000..bb575d4 --- /dev/null +++ b/pkg/context/user/domain/valueobj/id.mother.go @@ -0,0 +1,26 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func IdWithValidValue() models.ValueObject[string] { + value, err := NewId(services.Create.UUID()) + + if err != nil { + errors.Panic(err.Error(), "IdWithValidValue") + } + + return value + +} + +func IdWithInvalidValue() (string, error) { + value := "x" + + _, err := NewId(value) + + return value, err +} diff --git a/pkg/context/user/domain/valueobj/id_test.go b/pkg/context/user/domain/valueobj/id_test.go new file mode 100644 index 0000000..7c9afad --- /dev/null +++ b/pkg/context/user/domain/valueobj/id_test.go @@ -0,0 +1,38 @@ +package valueobj_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/stretchr/testify/suite" +) + +type IdValueObjectTestSuite struct { + suite.Suite +} + +func (suite *IdValueObjectTestSuite) SetupTest() {} + +func (suite *IdValueObjectTestSuite) TestWithInvalidValue() { + value, err := valueobj.IdWithInvalidValue() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewId", + What: "invalid uuid4 format", + Why: errors.Meta{ + "Id": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitIdValueObjectSuite(t *testing.T) { + suite.Run(t, new(IdValueObjectTestSuite)) +} diff --git a/pkg/context/user/domain/valueobj/password.go b/pkg/context/user/domain/valueobj/password.go new file mode 100644 index 0000000..b725758 --- /dev/null +++ b/pkg/context/user/domain/valueobj/password.go @@ -0,0 +1,41 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const PasswordMinCharactersLength = "8" +const PasswordMaxCharactersLength = "64" + +type Password struct { + Password string `validate:"gte=8,lte=64"` +} + +func (value *Password) Value() string { + return value.Password +} + +func (value *Password) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewPassword(value string) (models.ValueObject[string], error) { + valueObj := &Password{ + Password: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewPassword", + What: "password must be between " + PasswordMinCharactersLength + " to " + PasswordMaxCharactersLength + " characters", + Why: errors.Meta{ + "Password": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/user/domain/valueobj/password.mother.go b/pkg/context/user/domain/valueobj/password.mother.go new file mode 100644 index 0000000..a9d8077 --- /dev/null +++ b/pkg/context/user/domain/valueobj/password.mother.go @@ -0,0 +1,25 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func PasswordWithValidValue() models.ValueObject[string] { + value, err := NewPassword(services.Create.Regex(`^[\W\w]{8,64}$`)) + + if err != nil { + errors.Panic(err.Error(), "PasswordWithValidValue") + } + + return value +} + +func PasswordWithInvalidLength() (string, error) { + value := "x" + + _, err := NewPassword(value) + + return value, err +} diff --git a/pkg/context/user/domain/valueobj/password_test.go b/pkg/context/user/domain/valueobj/password_test.go new file mode 100644 index 0000000..94f03e7 --- /dev/null +++ b/pkg/context/user/domain/valueobj/password_test.go @@ -0,0 +1,38 @@ +package valueobj_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/stretchr/testify/suite" +) + +type PasswordValueObjectTestSuite struct { + suite.Suite +} + +func (suite *PasswordValueObjectTestSuite) SetupTest() {} + +func (suite *PasswordValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobj.PasswordWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewPassword", + What: "password must be between " + "8" + " to " + "64" + " characters", + Why: errors.Meta{ + "Password": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitPasswordValueObjectSuite(t *testing.T) { + suite.Run(t, new(PasswordValueObjectTestSuite)) +} diff --git a/pkg/context/user/domain/valueobj/username.go b/pkg/context/user/domain/valueobj/username.go new file mode 100644 index 0000000..ca5c9c1 --- /dev/null +++ b/pkg/context/user/domain/valueobj/username.go @@ -0,0 +1,45 @@ +package valueobj + +import ( + "strings" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/go-playground/validator/v10" +) + +const UsernameMinCharactersLength = "2" +const UsernameMaxCharactersLength = "20" + +type Username struct { + Username string `validate:"gte=2,lte=20,alphanum"` +} + +func (value *Username) Value() string { + return value.Username +} + +func (value *Username) IsValid() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + return validate.Struct(value) +} + +func NewUsername(value string) (models.ValueObject[string], error) { + value = strings.TrimSpace(value) + + valueObj := &Username{ + Username: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewUsername", + What: "username must be between " + UsernameMinCharactersLength + " to " + UsernameMaxCharactersLength + " characters and be alphanumeric only", + Why: errors.Meta{ + "Username": value, + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/user/domain/valueobj/username.mother.go b/pkg/context/user/domain/valueobj/username.mother.go new file mode 100644 index 0000000..0518d1d --- /dev/null +++ b/pkg/context/user/domain/valueobj/username.mother.go @@ -0,0 +1,33 @@ +package valueobj + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/domain/services" +) + +func UsernameWithValidValue() models.ValueObject[string] { + value, err := NewUsername(services.Create.Regex(`^[A-Za-z0-9]{2,20}$`)) + + if err != nil { + errors.Panic(err.Error(), "UsernameWithValidValue") + } + + return value +} + +func UsernameWithInvalidLength() (string, error) { + value := "x" + + _, err := NewUsername(value) + + return value, err +} + +func UsernameWithInvalidAlphanumeric() (string, error) { + value := "<>" + + _, err := NewUsername(value) + + return value, err +} diff --git a/pkg/context/user/domain/valueobj/username_test.go b/pkg/context/user/domain/valueobj/username_test.go new file mode 100644 index 0000000..0d2ce53 --- /dev/null +++ b/pkg/context/user/domain/valueobj/username_test.go @@ -0,0 +1,57 @@ +package valueobj_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/stretchr/testify/suite" +) + +type UsernameValueObjectTestSuite struct { + suite.Suite +} + +func (suite *UsernameValueObjectTestSuite) SetupTest() {} + +func (suite *UsernameValueObjectTestSuite) TestWithInvalidLength() { + value, err := valueobj.UsernameWithInvalidLength() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewUsername", + What: "username must be between " + "2" + " to " + "20" + " characters and be alphanumeric only", + Why: errors.Meta{ + "Username": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *UsernameValueObjectTestSuite) TestWithInvalidAlphanumeric() { + value, err := valueobj.UsernameWithInvalidAlphanumeric() + + var actual *errors.InvalidValue + + suite.ErrorAs(err, &actual) + + expected := &errors.InvalidValue{Bubble: &errors.Bubble{ + When: actual.When, + Where: "NewUsername", + What: "username must be between " + "2" + " to " + "20" + " characters and be alphanumeric only", + Why: errors.Meta{ + "Username": value, + }, + }} + + suite.EqualError(expected, actual.Error()) +} + +func TestUnitUsernameValueObjectSuite(t *testing.T) { + suite.Run(t, new(UsernameValueObjectTestSuite)) +} diff --git a/pkg/context/user/domain/valueobj/verified.go b/pkg/context/user/domain/valueobj/verified.go new file mode 100644 index 0000000..440c289 --- /dev/null +++ b/pkg/context/user/domain/valueobj/verified.go @@ -0,0 +1,38 @@ +package valueobj + +import ( + "fmt" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" +) + +type Verified struct { + Verified bool +} + +func (value *Verified) Value() bool { + return value.Verified +} + +func (value *Verified) IsValid() error { + return nil +} + +func NewVerified(value bool) (models.ValueObject[bool], error) { + valueObj := &Verified{ + Verified: value, + } + + if valueObj.IsValid() != nil { + return nil, errors.NewInvalidValue(&errors.Bubble{ + Where: "NewVerified", + What: "invalid verified value", + Why: errors.Meta{ + "Verified": fmt.Sprintf("%t", value), + }, + }) + } + + return valueObj, nil +} diff --git a/pkg/context/user/infrastructure/communication/mail/confirmation.go b/pkg/context/user/infrastructure/communication/mail/confirmation.go new file mode 100644 index 0000000..88823d7 --- /dev/null +++ b/pkg/context/user/infrastructure/communication/mail/confirmation.go @@ -0,0 +1,57 @@ +package mail + +import ( + "bytes" + "context" + "fmt" + "net/smtp" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/transports" + "github.com/bastean/codexgo/pkg/context/user/domain/event" +) + +type Confirmation struct { + *transports.SMTP +} + +func (client *Confirmation) Submit(data any) error { + user, ok := data.(*event.CreatedSucceededAttributes) + + if !ok { + return errors.NewInternal(&errors.Bubble{ + Where: "Submit", + What: "failure in type assertion", + Why: errors.Meta{ + "Expected": new(event.CreatedSucceededAttributes), + "Actual": data, + }, + }) + } + + var message bytes.Buffer + + headers := fmt.Sprintf("From: %s\n"+"To: %s\n"+"Subject: Account Confirmation", client.Username, user.Email) + + _, _ = message.Write([]byte(fmt.Sprintf("%s\n%s\n", headers, client.MIMEHeaders))) + + link := fmt.Sprintf("%s/verify/%s", client.ServerURL, user.Id) + + ConfirmationTemplate(user.Username, link).Render(context.Background(), &message) + + err := smtp.SendMail(client.SMTPServerURL, client.Auth, client.Username, []string{user.Email}, message.Bytes()) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "Submit", + What: "failure to send an account confirmation mail", + Why: errors.Meta{ + "User Id": user.Id, + "SMTP Server URL": client.SMTPServerURL, + }, + Who: err, + }) + } + + return nil +} diff --git a/pkg/context/user/infrastructure/communication/mail/confirmation.templ b/pkg/context/user/infrastructure/communication/mail/confirmation.templ new file mode 100644 index 0000000..41daaa6 --- /dev/null +++ b/pkg/context/user/infrastructure/communication/mail/confirmation.templ @@ -0,0 +1,48 @@ +package mail + +templ ConfirmationTemplate(username, link string) { +
+
+
+ +
+
+

Hi { username }

+

+ Please confirm your account by clicking the button below +

+ + CONFIRM + + +
+
+
+} diff --git a/pkg/context/user/infrastructure/communication/mail/confirmation.transport_test.go b/pkg/context/user/infrastructure/communication/mail/confirmation.transport_test.go new file mode 100644 index 0000000..6882388 --- /dev/null +++ b/pkg/context/user/infrastructure/communication/mail/confirmation.transport_test.go @@ -0,0 +1,49 @@ +package mail_test + +import ( + "encoding/json" + "os" + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/transports" + "github.com/bastean/codexgo/pkg/context/user/domain/event" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/communication/mail" + "github.com/stretchr/testify/suite" +) + +type MailConfirmationTransportTestSuite struct { + suite.Suite + sut models.Transport + smtp *transports.SMTP +} + +func (suite *MailConfirmationTransportTestSuite) SetupTest() { + suite.smtp = transports.NewSMTP( + os.Getenv("SMTP_HOST"), + os.Getenv("SMTP_PORT"), + os.Getenv("SMTP_USERNAME"), + os.Getenv("SMTP_PASSWORD"), + os.Getenv("URL"), + ) + + suite.sut = &mail.Confirmation{ + SMTP: suite.smtp, + } +} + +func (suite *MailConfirmationTransportTestSuite) TestSubmit() { + message := event.RandomCreatedSucceeded() + + user := new(event.CreatedSucceeded) + + user.Attributes = new(event.CreatedSucceededAttributes) + + suite.NoError(json.Unmarshal(message.Attributes, user.Attributes)) + + suite.NoError(suite.sut.Submit(user.Attributes)) +} + +func TestIntegrationMailConfirmationTransportSuite(t *testing.T) { + suite.Run(t, new(MailConfirmationTransportTestSuite)) +} diff --git a/pkg/context/user/infrastructure/communication/terminal/confirmation.go b/pkg/context/user/infrastructure/communication/terminal/confirmation.go new file mode 100644 index 0000000..a248649 --- /dev/null +++ b/pkg/context/user/infrastructure/communication/terminal/confirmation.go @@ -0,0 +1,35 @@ +package terminal + +import ( + "fmt" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/event" +) + +type Confirmation struct { + models.Logger + ServerURL string +} + +func (client *Confirmation) Submit(data any) error { + user, ok := data.(*event.CreatedSucceededAttributes) + + if !ok { + return errors.NewInternal(&errors.Bubble{ + Where: "Submit", + What: "failure in type assertion", + Why: errors.Meta{ + "Expected": new(event.CreatedSucceededAttributes), + "Actual": data, + }, + }) + } + + link := fmt.Sprintf("Hi %s, please confirm your account through this link: %s/verify/%s", user.Username, client.ServerURL, user.Id) + + client.Logger.Info(link) + + return nil +} diff --git a/pkg/context/user/infrastructure/communication/terminal/confirmation.transport_test.go b/pkg/context/user/infrastructure/communication/terminal/confirmation.transport_test.go new file mode 100644 index 0000000..ea65ab2 --- /dev/null +++ b/pkg/context/user/infrastructure/communication/terminal/confirmation.transport_test.go @@ -0,0 +1,54 @@ +package terminal_test + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/loggers" + "github.com/bastean/codexgo/pkg/context/user/domain/event" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/communication/terminal" + "github.com/stretchr/testify/suite" +) + +type TerminalConfirmationTransportTestSuite struct { + suite.Suite + sut models.Transport + logger *loggers.LoggerMock + serverURL string +} + +func (suite *TerminalConfirmationTransportTestSuite) SetupTest() { + suite.logger = new(loggers.LoggerMock) + + suite.serverURL = os.Getenv("URL") + + suite.sut = &terminal.Confirmation{ + Logger: suite.logger, + ServerURL: suite.serverURL, + } +} + +func (suite *TerminalConfirmationTransportTestSuite) TestSubmit() { + message := event.RandomCreatedSucceeded() + + user := new(event.CreatedSucceeded) + + user.Attributes = new(event.CreatedSucceededAttributes) + + suite.NoError(json.Unmarshal(message.Attributes, user.Attributes)) + + link := fmt.Sprintf("Hi %s, please confirm your account through this link: %s/verify/%s", user.Attributes.Username, suite.serverURL, user.Attributes.Id) + + suite.logger.Mock.On("Info", link) + + suite.NoError(suite.sut.Submit(user.Attributes)) + + suite.logger.AssertExpectations(suite.T()) +} + +func TestIntegrationTerminalConfirmationTransportSuite(t *testing.T) { + suite.Run(t, new(TerminalConfirmationTransportTestSuite)) +} diff --git a/pkg/context/user/infrastructure/communication/transport.mock.go b/pkg/context/user/infrastructure/communication/transport.mock.go new file mode 100644 index 0000000..8d8faa6 --- /dev/null +++ b/pkg/context/user/infrastructure/communication/transport.mock.go @@ -0,0 +1,14 @@ +package communication + +import ( + "github.com/stretchr/testify/mock" +) + +type TransportMock struct { + mock.Mock +} + +func (transport *TransportMock) Submit(data any) error { + transport.Called(data) + return nil +} diff --git a/pkg/context/user/infrastructure/cryptographic/bcrypt.go b/pkg/context/user/infrastructure/cryptographic/bcrypt.go new file mode 100644 index 0000000..56a5382 --- /dev/null +++ b/pkg/context/user/infrastructure/cryptographic/bcrypt.go @@ -0,0 +1,28 @@ +package cryptographic + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "golang.org/x/crypto/bcrypt" +) + +type Bcrypt struct{} + +func (hashing *Bcrypt) Hash(plain string) (string, error) { + salt := 10 + bytes, err := bcrypt.GenerateFromPassword([]byte(plain), salt) + + if err != nil { + return "", errors.NewInternal(&errors.Bubble{ + Where: "Hash", + What: "failure to generate a hash", + Who: err, + }) + } + + return string(bytes), nil +} + +func (hashing *Bcrypt) IsNotEqual(hashed, plain string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashed), []byte(plain)) + return err != nil +} diff --git a/pkg/context/user/infrastructure/cryptographic/bcrypt.hashing_test.go b/pkg/context/user/infrastructure/cryptographic/bcrypt.hashing_test.go new file mode 100644 index 0000000..2d824db --- /dev/null +++ b/pkg/context/user/infrastructure/cryptographic/bcrypt.hashing_test.go @@ -0,0 +1,49 @@ +package cryptographic_test + +import ( + "testing" + + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/cryptographic" + "github.com/stretchr/testify/suite" +) + +type BcryptHashingTestSuite struct { + suite.Suite + sut model.Hashing +} + +func (suite *BcryptHashingTestSuite) SetupTest() { + suite.sut = new(cryptographic.Bcrypt) +} + +func (suite *BcryptHashingTestSuite) TestHash() { + password := valueobj.PasswordWithValidValue() + + plain := password.Value() + + hashed, err := suite.sut.Hash(plain) + + suite.NoError(err) + + suite.NotEqual(plain, hashed) +} + +func (suite *BcryptHashingTestSuite) TestIsNotEqual() { + password := valueobj.PasswordWithValidValue() + + plain := password.Value() + + hashed, err := suite.sut.Hash(plain) + + suite.NoError(err) + + isNotEqual := suite.sut.IsNotEqual(hashed, plain) + + suite.False(isNotEqual) +} + +func TestIntegrationBcryptHashingSuite(t *testing.T) { + suite.Run(t, new(BcryptHashingTestSuite)) +} diff --git a/pkg/context/user/infrastructure/cryptographic/hashing.mock.go b/pkg/context/user/infrastructure/cryptographic/hashing.mock.go new file mode 100644 index 0000000..4c6ca84 --- /dev/null +++ b/pkg/context/user/infrastructure/cryptographic/hashing.mock.go @@ -0,0 +1,19 @@ +package cryptographic + +import ( + "github.com/stretchr/testify/mock" +) + +type HashingMock struct { + mock.Mock +} + +func (hashing *HashingMock) Hash(plain string) (string, error) { + args := hashing.Called(plain) + return args.Get(0).(string), nil +} + +func (hashing *HashingMock) IsNotEqual(hashed, plain string) bool { + args := hashing.Called(hashed, plain) + return args.Get(0).(bool) +} diff --git a/pkg/context/user/infrastructure/persistence/mongo.go b/pkg/context/user/infrastructure/persistence/mongo.go new file mode 100644 index 0000000..e65f074 --- /dev/null +++ b/pkg/context/user/infrastructure/persistence/mongo.go @@ -0,0 +1,215 @@ +package persistence + +import ( + "context" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/persistences" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type UserDocument struct { + Id string `bson:"id,omitempty"` + Email string `bson:"email,omitempty"` + Username string `bson:"username,omitempty"` + Password string `bson:"password,omitempty"` + Verified bool `bson:"verified,omitempty"` +} + +type UserCollection struct { + collection *mongo.Collection + hashing model.Hashing +} + +func (db *UserCollection) Save(user *aggregate.User) error { + newUser := UserDocument(*user.ToPrimitives()) + + hashed, err := db.hashing.Hash(newUser.Password) + + if err != nil { + return errors.BubbleUp(err, "Save") + } + + newUser.Password = hashed + + _, err = db.collection.InsertOne(context.Background(), &newUser) + + if mongo.IsDuplicateKeyError(err) { + return errors.BubbleUp(persistences.HandleMongoDuplicateKeyError(err), "Save") + } + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "Save", + What: "failure to save a user", + Why: errors.Meta{ + "Id": user.Id.Value(), + }, + Who: err, + }) + } + + return nil +} + +func (db *UserCollection) Verify(id models.ValueObject[string]) error { + filter := bson.D{{Key: "id", Value: id.Value()}} + + _, err := db.collection.UpdateOne(context.Background(), filter, bson.D{ + {Key: "$set", Value: bson.D{ + {Key: "verified", Value: true}, + }}, + }) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "Verify", + What: "failure to verify a user", + Why: errors.Meta{ + "Id": id.Value(), + }, + Who: err, + }) + } + + return nil +} + +func (db *UserCollection) Update(user *aggregate.User) error { + updatedUser := UserDocument(*user.ToPrimitives()) + + filter := bson.D{{Key: "id", Value: user.Id.Value()}} + + hashed, err := db.hashing.Hash(user.Password.Value()) + + if err != nil { + return errors.BubbleUp(err, "Update") + } + + updatedUser.Password = hashed + + _, err = db.collection.ReplaceOne(context.Background(), filter, &updatedUser) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "Update", + What: "failure to update a user", + Why: errors.Meta{ + "Id": user.Id.Value(), + }, + Who: err, + }) + } + + return nil +} + +func (db *UserCollection) Delete(id models.ValueObject[string]) error { + filter := bson.D{{Key: "id", Value: id.Value()}} + + _, err := db.collection.DeleteOne(context.Background(), filter) + + if err != nil { + return errors.NewInternal(&errors.Bubble{ + Where: "Delete", + What: "failure to delete a user", + Why: errors.Meta{ + "Id": id.Value(), + }, + Who: err, + }) + } + + return nil +} + +func (db *UserCollection) Search(criteria *model.RepositorySearchCriteria) (*aggregate.User, error) { + var filter bson.D + var index string + + switch { + case criteria.Id != nil: + filter = bson.D{{Key: "id", Value: criteria.Id.Value()}} + index = criteria.Id.Value() + case criteria.Email != nil: + filter = bson.D{{Key: "email", Value: criteria.Email.Value()}} + index = criteria.Email.Value() + } + + result := db.collection.FindOne(context.Background(), filter) + + if err := result.Err(); err != nil { + return nil, persistences.HandleMongoDocumentNotFound(index, err) + } + + primitive := new(aggregate.UserPrimitive) + + err := result.Decode(primitive) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "Search", + What: "failure to decode a result", + Why: errors.Meta{ + "Index": index, + }, + Who: err, + }) + } + + user, err := aggregate.FromPrimitives(primitive) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "Search", + What: "failure to create an aggregate from a primitive", + Why: errors.Meta{ + "Primitive": primitive, + "Index": index, + }, + Who: err, + }) + } + + return user, nil +} + +func NewMongoCollection(mdb *persistences.MongoDB, collectionName string, hashing model.Hashing) (model.Repository, error) { + collection := mdb.Database.Collection(collectionName) + + _, err := collection.Indexes().CreateMany(context.Background(), []mongo.IndexModel{ + { + Keys: bson.D{{Key: "id", Value: 1}}, + Options: options.Index().SetUnique(true), + }, + { + Keys: bson.D{{Key: "email", Value: 1}}, + Options: options.Index().SetUnique(true), + }, + { + Keys: bson.D{{Key: "username", Value: 1}}, + Options: options.Index().SetUnique(true), + }, + }) + + if err != nil { + return nil, errors.NewInternal(&errors.Bubble{ + Where: "NewMongoCollection", + What: "failure to create indexes for user collection", + Why: errors.Meta{ + "Collection": collectionName, + }, + Who: err, + }) + } + + return &UserCollection{ + collection: collection, + hashing: hashing, + }, nil +} diff --git a/pkg/context/user/infrastructure/persistence/mongo.repository_test.go b/pkg/context/user/infrastructure/persistence/mongo.repository_test.go new file mode 100644 index 0000000..d8be484 --- /dev/null +++ b/pkg/context/user/infrastructure/persistence/mongo.repository_test.go @@ -0,0 +1,137 @@ +package persistence_test + +import ( + "os" + "testing" + + "github.com/bastean/codexgo/pkg/context/shared/domain/errors" + "github.com/bastean/codexgo/pkg/context/shared/infrastructure/persistences" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/bastean/codexgo/pkg/context/user/domain/valueobj" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/cryptographic" + "github.com/bastean/codexgo/pkg/context/user/infrastructure/persistence" + "github.com/stretchr/testify/suite" +) + +type MongoRepositoryTestSuite struct { + suite.Suite + sut model.Repository + hashing *cryptographic.HashingMock +} + +func (suite *MongoRepositoryTestSuite) SetupTest() { + uri := os.Getenv("DATABASE_URI") + + databaseName := "codexgo-test" + + database, _ := persistences.NewMongoDatabase(uri, databaseName) + + collectionName := "users-test" + + suite.hashing = new(cryptographic.HashingMock) + + suite.sut, _ = persistence.NewMongoCollection(database, collectionName, suite.hashing) +} + +func (suite *MongoRepositoryTestSuite) TestSave() { + user := aggregate.RandomUser() + + suite.hashing.On("Hash", user.Password.Value()).Return(user.Password.Value()) + + suite.NoError(suite.sut.Save(user)) + + suite.hashing.AssertExpectations(suite.T()) +} + +func (suite *MongoRepositoryTestSuite) TestSaveDuplicate() { + user := aggregate.RandomUser() + + suite.hashing.On("Hash", user.Password.Value()).Return(user.Password.Value()) + + suite.NoError(suite.sut.Save(user)) + + err := suite.sut.Save(user) + + suite.hashing.AssertExpectations(suite.T()) + + var actual *errors.AlreadyExist + + suite.ErrorAs(err, &actual) + + expected := &errors.AlreadyExist{Bubble: &errors.Bubble{ + When: actual.When, + Where: "HandleMongoDuplicateKeyError", + What: "already registered", + Why: errors.Meta{ + "Field": "Id", + }, + Who: actual.Who, + }} + + suite.EqualError(expected, actual.Error()) +} + +func (suite *MongoRepositoryTestSuite) TestVerify() { + user := aggregate.RandomUser() + + suite.hashing.On("Hash", user.Password.Value()).Return(user.Password.Value()) + + suite.NoError(suite.sut.Save(user)) + + suite.NoError(suite.sut.Verify(user.Id)) +} + +func (suite *MongoRepositoryTestSuite) TestUpdate() { + user := aggregate.RandomUser() + + suite.hashing.On("Hash", user.Password.Value()).Return(user.Password.Value()) + + suite.NoError(suite.sut.Save(user)) + + password := valueobj.PasswordWithValidValue() + + user.Password = password + + suite.hashing.On("Hash", user.Password.Value()).Return(user.Password.Value()) + + suite.NoError(suite.sut.Update(user)) + + suite.hashing.AssertExpectations(suite.T()) +} + +func (suite *MongoRepositoryTestSuite) TestDelete() { + user := aggregate.RandomUser() + + suite.hashing.On("Hash", user.Password.Value()).Return(user.Password.Value()) + + suite.NoError(suite.sut.Save(user)) + + suite.NoError(suite.sut.Delete(user.Id)) +} + +func (suite *MongoRepositoryTestSuite) TestSearch() { + expected := aggregate.RandomUser() + + expected.PullMessages() + + suite.hashing.On("Hash", expected.Password.Value()).Return(expected.Password.Value()) + + suite.NoError(suite.sut.Save(expected)) + + criteria := &model.RepositorySearchCriteria{ + Id: expected.Id, + } + + user, err := suite.sut.Search(criteria) + + suite.NoError(err) + + actual := user + + suite.Equal(expected, actual) +} + +func TestIntegrationMongoRepositorySuite(t *testing.T) { + suite.Run(t, new(MongoRepositoryTestSuite)) +} diff --git a/pkg/context/user/infrastructure/persistence/repository.mock.go b/pkg/context/user/infrastructure/persistence/repository.mock.go new file mode 100644 index 0000000..def4f7c --- /dev/null +++ b/pkg/context/user/infrastructure/persistence/repository.mock.go @@ -0,0 +1,37 @@ +package persistence + +import ( + "github.com/bastean/codexgo/pkg/context/shared/domain/models" + "github.com/bastean/codexgo/pkg/context/user/domain/aggregate" + "github.com/bastean/codexgo/pkg/context/user/domain/model" + "github.com/stretchr/testify/mock" +) + +type RepositoryMock struct { + mock.Mock +} + +func (repository *RepositoryMock) Save(user *aggregate.User) error { + repository.Called(user) + return nil +} + +func (repository *RepositoryMock) Verify(id models.ValueObject[string]) error { + repository.Called(id) + return nil +} + +func (repository *RepositoryMock) Update(user *aggregate.User) error { + repository.Called(user) + return nil +} + +func (repository *RepositoryMock) Delete(id models.ValueObject[string]) error { + repository.Called(id) + return nil +} + +func (repository *RepositoryMock) Search(criteria *model.RepositorySearchCriteria) (*aggregate.User, error) { + args := repository.Called(criteria) + return args.Get(0).(*aggregate.User), nil +} diff --git a/scripts/copydeps/copydeps.go b/scripts/copydeps/copydeps.go new file mode 100644 index 0000000..759abfd --- /dev/null +++ b/scripts/copydeps/copydeps.go @@ -0,0 +1,103 @@ +package main + +import ( + "errors" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" +) + +const regExpEveryMinFile = `^.+\.min\.(js|css)$` +const regExpEveryWoff2File = `^.+\.woff2$` + +const staticPath = "pkg/cmd/server/static/dist" + +const jquerySourcePath = "node_modules/jquery/dist" +const jqueryStaticPath = staticPath + "/jquery.com" + +const fomanticSourcePath = "node_modules/fomantic-ui/dist" +const fomanticStaticPath = staticPath + "/fomantic-ui.com" + +const lodashSourcePath = "node_modules/lodash" +const lodashStaticPath = staticPath + "/lodash.com" + +func Panic(who error, what, where string) { + log.Panicf("(%s): %s: [%s]", where, what, who) +} + +func CreateDirectory(path string) { + err := os.MkdirAll(path, os.ModePerm) + + if err != nil { + Panic(err, fmt.Sprintf("failed to create \"%s\"", path), "CreateDirectory") + } + + log.Printf("created: \"%s\"", path) +} + +func CopyFile(filename, sourcePath, targetPath string) { + data, err := os.ReadFile(filepath.Join(sourcePath, filepath.Base(filename))) + + if err != nil { + Panic(err, fmt.Sprintf("failed to read \"%s\" from \"%s\"", filename, sourcePath), "CopyFile") + } + + err = os.WriteFile(filepath.Join(targetPath, filepath.Base(filename)), data, os.ModePerm) + + if err != nil { + Panic(err, fmt.Sprintf("failed to write \"%s\" on \"%s\"", filename, targetPath), "CopyFile") + } + + log.Printf("created: \"%s\"", filepath.Join(targetPath, filepath.Base(filename))) +} + +func CopyDeps(filenames []string, sourcePath, targetPath string) { + files, err := os.ReadDir(sourcePath) + + if err != nil { + Panic(err, fmt.Sprintf("failed to copy \"%s\" from \"%s\"", filenames, sourcePath), "CopyDeps") + } + + CreateDirectory(targetPath) + + if strings.HasPrefix(filenames[0], "^") && strings.HasSuffix(filenames[0], "$") { + isMinFile := regexp.MustCompile(filenames[0]).MatchString + + for _, file := range files { + if isMinFile(file.Name()) { + CopyFile(file.Name(), sourcePath, targetPath) + } + } + + return + } + + for _, filename := range filenames { + for _, file := range files { + if filepath.Base(filename) == file.Name() { + CopyFile(filename, sourcePath, targetPath) + } + } + } +} + +func main() { + err := os.RemoveAll(staticPath) + + if err != nil && !errors.Is(err, os.ErrNotExist) { + Panic(err, fmt.Sprintf("failed to remove \"%s\"", staticPath), "main") + } + + CreateDirectory(staticPath) + + CopyDeps([]string{"jquery.min.js"}, jquerySourcePath, jqueryStaticPath) + + CopyDeps([]string{"semantic.min.js", "semantic.min.css"}, fomanticSourcePath, fomanticStaticPath) + CopyDeps([]string{regExpEveryMinFile}, filepath.Join(fomanticSourcePath, "components"), filepath.Join(fomanticStaticPath, "components")) + CopyDeps([]string{regExpEveryWoff2File}, filepath.Join(fomanticSourcePath, "themes/default/assets/fonts"), filepath.Join(fomanticStaticPath, "themes/default/assets/fonts")) + + CopyDeps([]string{"lodash.min.js"}, lodashSourcePath, lodashStaticPath) +} diff --git a/scripts/syncenv/syncenv.go b/scripts/syncenv/syncenv.go new file mode 100644 index 0000000..4644c64 --- /dev/null +++ b/scripts/syncenv/syncenv.go @@ -0,0 +1,215 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" +) + +const cli = "syncenv" + +var envFilesDir string +var envFileModel string + +var envFileBackupRegex = regexp.MustCompile(`\.env\..*\.tmp`) + +func RestoreEnvFilesBackup() { + files, err := os.ReadDir(envFilesDir) + + if err != nil { + panic(err) + } + + for _, file := range files { + if envFileBackupRegex.Match([]byte(file.Name())) { + renamed, _ := strings.CutSuffix(file.Name(), ".tmp") + os.Rename(filepath.Join(envFilesDir, file.Name()), filepath.Join(envFilesDir, renamed)) + } + } +} + +func Panic(who error, where string) { + log.Println("Sync .env* failed!") + + log.Println("Restoring .env* from backups") + RestoreEnvFilesBackup() + + log.Println("Please, check 'Error' or undo changes with: make sync-env-reset") + + log.Panicf("Error: (%s): [%s]", where, who) +} + +func usage() { + fmt.Printf("Usage: %s [OPTIONS]\n", cli) + fmt.Printf("\nE.g.: %s -dir . -model .env.example\n\n", cli) + flag.PrintDefaults() +} + +func BackupEnvFiles() { + files, err := os.ReadDir(envFilesDir) + + if err != nil { + Panic(err, "BackupEnvFiles") + } + + for _, file := range files { + if strings.Contains(file.Name(), ".env") { + data, err := os.ReadFile(file.Name()) + + if err != nil { + Panic(err, "BackupEnvFiles") + } + + err = os.WriteFile(file.Name()+".tmp", data, 0644) + + if err != nil { + Panic(err, "BackupEnvFiles") + } + } + } +} + +func RemoveEnvFilesBackup() { + files, err := os.ReadDir(envFilesDir) + + if err != nil { + Panic(err, "RemoveEnvFilesBackup") + } + + for _, file := range files { + if envFileBackupRegex.Match([]byte(file.Name())) { + err = os.Remove(file.Name()) + + if err != nil { + Panic(err, "RemoveEnvFilesBackup") + } + } + } +} + +func GetEnvFiles() (envFiles []string) { + files, err := os.ReadDir(envFilesDir) + + if err != nil { + Panic(err, "GetEnvFiles") + } + + for _, file := range files { + if strings.Contains(file.Name(), ".env") && file.Name() != envFileModel { + envFiles = append(envFiles, file.Name()) + } + } + + return +} + +func GetEnvFileModelVars() []string { + dataBytes, err := os.ReadFile(envFileModel) + + if err != nil { + Panic(err, "GetEnvFileModelVars") + } + + enVars := strings.Split(string(dataBytes), "\n") + + for i, enVar := range enVars { + enVars[i] = strings.Split(enVar, "=")[0] + } + + return enVars +} + +func SyncEnv(envModelVars []string, envFile string) { + data, err := os.ReadFile(envFile) + + if err != nil { + Panic(err, "SyncEnv") + } + + envFileVars := strings.Split(string(data), "\n") + + envFileVarsCleaned := []string{} + + for _, envFileVar := range envFileVars { + if envFileVar == "" { + continue + } + + envFileVarsCleaned = append(envFileVarsCleaned, envFileVar) + } + + envFileUpdatedVars := "" + + updatedVar := false + + for i, envModelVar := range envModelVars { + updatedVar = false + + if i+1 == len(envModelVars) { + break + } + + if envModelVar == "" { + envFileUpdatedVars += "\n" + continue + } + + for _, envFileVar := range envFileVarsCleaned { + values := strings.SplitN(envFileVar, "=", 2) + enVarName := values[0] + enVarValue := values[1] + + if envModelVar == enVarName { + envFileUpdatedVars += envModelVar + "=" + enVarValue + "\n" + updatedVar = true + break + } + } + + if !updatedVar { + envFileUpdatedVars += envModelVar + "=" + "\n" + } + } + + file, err := os.Create(envFile) + + if err != nil { + Panic(err, "SyncEnv") + } + + _, err = file.WriteString(envFileUpdatedVars) + + if err != nil { + Panic(err, "SyncEnv") + } +} + +func main() { + flag.StringVar(&envFilesDir, "dir", ".", ".env files directory") + flag.StringVar(&envFileModel, "model", ".env.example", ".env file model") + + flag.Usage = usage + + flag.Parse() + + log.Println("Creating .env* backups") + BackupEnvFiles() + + log.Println("Searching .env*") + envFiles := GetEnvFiles() + envFileModelVars := GetEnvFileModelVars() + + log.Println("Syncing .env*") + for _, envFile := range envFiles { + SyncEnv(envFileModelVars, envFile) + } + + log.Println("Removing .env* backups") + RemoveEnvFilesBackup() + + log.Println("Sync .env* completed!") +} diff --git a/scripts/upgrade/upgrade.go b/scripts/upgrade/upgrade.go new file mode 100644 index 0000000..38b7ec1 --- /dev/null +++ b/scripts/upgrade/upgrade.go @@ -0,0 +1,72 @@ +package main + +import ( + "log" + "os/exec" +) + +func Panic(who error, where string) { + log.Println("Upgrade failed!") + + log.Println("Please, check 'Error' or undo changes with: make upgrade-reset") + + log.Panicf("Error: (%s): [%s]", where, who) +} + +func UpgradeGo() { + if err := exec.Command("make", "upgrade-go").Run(); err != nil { + Panic(err, "UpgradeGo") + } +} + +func UpgradeNode() { + if err := exec.Command("make", "upgrade-node").Run(); err != nil { + Panic(err, "UpgradeNode") + } +} + +func RunLint() { + if err := exec.Command("make", "lint-check").Run(); err != nil { + Panic(err, "RunLint") + } +} + +func RunTest() { + if err := exec.Command("make", "test-unit").Run(); err != nil { + Panic(err, "RunTest") + } +} + +func Commit() { + if err := exec.Command("git", "add", ".", "--update").Run(); err != nil { + Panic(err, "Commit") + } + + if err := exec.Command("git", "commit", "-m", "chore(deps): upgrade dependencies").Run(); err != nil { + Panic(err, "Commit") + } +} + +func main() { + log.Println("Upgrading dependencies") + + log.Println("Running Go Tidy") + RunLint() + + log.Println("Upgrading Go dependencies") + UpgradeGo() + + log.Println("Upgrading Node dependencies") + UpgradeNode() + + log.Println("Running Lint") + RunLint() + + log.Println("Running Test") + RunTest() + + log.Println("Commit changes") + Commit() + + log.Println("Upgrade completed!") +} diff --git a/test/.gitkeep b/test/.gitkeep new file mode 100644 index 0000000..e69de29