From 49db2b512530551f532608ad801abd4762315c42 Mon Sep 17 00:00:00 2001 From: Ricardo Vera Date: Sun, 12 Apr 2026 23:46:10 -0500 Subject: [PATCH 1/2] docs(readme): add demo screenshot --- visual-yape.png | Bin 0 -> 361692 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 visual-yape.png diff --git a/visual-yape.png b/visual-yape.png new file mode 100644 index 0000000000000000000000000000000000000000..64188ca699cdeaa9dd1dd822d1b76c78578ef6e2 GIT binary patch literal 361692 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

Rk7o5If&+=J18GT( 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 Date: Mon, 13 Apr 2026 00:30:33 -0500 Subject: [PATCH 2/2] feat(core): add initial source code and project setup --- .env.country.example | 3 + .env.example | 21 + Dockerfile | 21 + Dockerfile.visual-demo | 13 + README.md | 419 +- docker-compose.country.yml | 42 + docker-compose.yml | 173 + jest.config.ts | 15 + package-lock.json | 6724 +++++++++++++++++ package.json | 47 + run-init.md | 167 + scripts/README-run-kafka-scenario.md | 88 + scripts/add-new-country.sh | 324 + scripts/cleanup-docker.sh | 291 + scripts/init-local.sh | 360 + scripts/init-topics.sh | 48 + scripts/run-kafka-scenario.sh | 467 ++ scripts/run-test.sh | 35 + src/apps/fraud-consumer/main.ts | 36 + src/apps/ledger-consumer/main.ts | 41 + src/apps/outbox-relay/main.ts | 31 + src/apps/payment-api/main.ts | 23 + src/apps/status-saga/main.ts | 46 + src/config/env.ts | 41 + src/domain/entities/outbox-event.ts | 22 + src/domain/entities/payment-step.ts | 13 + src/domain/entities/payment.ts | 16 + src/domain/events/event-types.ts | 51 + src/domain/repositories/outbox.repository.ts | 20 + .../repositories/payment-step.repository.ts | 10 + src/domain/repositories/payment.repository.ts | 17 + src/domain/repositories/ports.ts | 15 + .../processed-event.repository.ts | 5 + src/domain/repositories/unit-of-work.ts | 5 + .../services/consumer-executor.service.ts | 52 + src/domain/services/fraud-consumer.service.ts | 64 + .../services/ledger-consumer.service.ts | 64 + src/domain/services/outbox-relay.service.ts | 34 + src/domain/services/payment.service.ts | 85 + src/domain/services/status-saga.service.ts | 140 + src/domain/services/topic-resolver.ts | 11 + .../messaging/kafka-consumer-runner.ts | 66 + .../messaging/kafka-publisher.ts | 75 + .../entities/outbox-event.orm-entity.ts | 44 + .../entities/payment-step.orm-entity.ts | 24 + .../entities/payment.orm-entity.ts | 29 + .../entities/processed-event.orm-entity.ts | 20 + .../repositories/outbox.typeorm-repository.ts | 112 + .../payment-step.typeorm-repository.ts | 62 + .../payment.typeorm-repository.ts | 59 + .../processed-event.typeorm-repository.ts | 22 + .../persistence/transaction-context.ts | 16 + .../persistence/typeorm-unit-of-work.ts | 18 + src/modules/common/core.module.ts | 23 + src/modules/common/messaging.module.ts | 29 + src/modules/common/persistence.module.ts | 76 + .../consumers/fraud/fraud-consumer.module.ts | 11 + .../ledger/ledger-consumer.module.ts | 11 + src/modules/outbox/outbox-relay.module.ts | 12 + src/modules/payment/dto/create-payment.dto.ts | 18 + src/modules/payment/payment-api.module.ts | 13 + src/modules/payment/payment-status.query.ts | 44 + src/modules/payment/payment.controller.ts | 37 + src/modules/status-saga/status-saga.module.ts | 11 + test-visual/public/app.js | 478 ++ test-visual/public/index.html | 141 + test-visual/public/styles.css | 713 ++ test-visual/server.js | 937 +++ test/dlt-handler.spec.ts | 28 + test/helpers/fakes.ts | 221 + test/idempotency-consumers.spec.ts | 72 + test/payment-service.spec.ts | 55 + test/status-saga.spec.ts | 97 + tsconfig.build.json | 7 + tsconfig.json | 20 + 75 files changed, 13578 insertions(+), 123 deletions(-) create mode 100644 .env.country.example create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 Dockerfile.visual-demo create mode 100644 docker-compose.country.yml create mode 100644 docker-compose.yml create mode 100644 jest.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 run-init.md create mode 100644 scripts/README-run-kafka-scenario.md create mode 100755 scripts/add-new-country.sh create mode 100755 scripts/cleanup-docker.sh create mode 100755 scripts/init-local.sh create mode 100755 scripts/init-topics.sh create mode 100755 scripts/run-kafka-scenario.sh create mode 100755 scripts/run-test.sh create mode 100644 src/apps/fraud-consumer/main.ts create mode 100644 src/apps/ledger-consumer/main.ts create mode 100644 src/apps/outbox-relay/main.ts create mode 100644 src/apps/payment-api/main.ts create mode 100644 src/apps/status-saga/main.ts create mode 100644 src/config/env.ts create mode 100644 src/domain/entities/outbox-event.ts create mode 100644 src/domain/entities/payment-step.ts create mode 100644 src/domain/entities/payment.ts create mode 100644 src/domain/events/event-types.ts create mode 100644 src/domain/repositories/outbox.repository.ts create mode 100644 src/domain/repositories/payment-step.repository.ts create mode 100644 src/domain/repositories/payment.repository.ts create mode 100644 src/domain/repositories/ports.ts create mode 100644 src/domain/repositories/processed-event.repository.ts create mode 100644 src/domain/repositories/unit-of-work.ts create mode 100644 src/domain/services/consumer-executor.service.ts create mode 100644 src/domain/services/fraud-consumer.service.ts create mode 100644 src/domain/services/ledger-consumer.service.ts create mode 100644 src/domain/services/outbox-relay.service.ts create mode 100644 src/domain/services/payment.service.ts create mode 100644 src/domain/services/status-saga.service.ts create mode 100644 src/domain/services/topic-resolver.ts create mode 100644 src/infrastructure/messaging/kafka-consumer-runner.ts create mode 100644 src/infrastructure/messaging/kafka-publisher.ts create mode 100644 src/infrastructure/persistence/entities/outbox-event.orm-entity.ts create mode 100644 src/infrastructure/persistence/entities/payment-step.orm-entity.ts create mode 100644 src/infrastructure/persistence/entities/payment.orm-entity.ts create mode 100644 src/infrastructure/persistence/entities/processed-event.orm-entity.ts create mode 100644 src/infrastructure/persistence/repositories/outbox.typeorm-repository.ts create mode 100644 src/infrastructure/persistence/repositories/payment-step.typeorm-repository.ts create mode 100644 src/infrastructure/persistence/repositories/payment.typeorm-repository.ts create mode 100644 src/infrastructure/persistence/repositories/processed-event.typeorm-repository.ts create mode 100644 src/infrastructure/persistence/transaction-context.ts create mode 100644 src/infrastructure/persistence/typeorm-unit-of-work.ts create mode 100644 src/modules/common/core.module.ts create mode 100644 src/modules/common/messaging.module.ts create mode 100644 src/modules/common/persistence.module.ts create mode 100644 src/modules/consumers/fraud/fraud-consumer.module.ts create mode 100644 src/modules/consumers/ledger/ledger-consumer.module.ts create mode 100644 src/modules/outbox/outbox-relay.module.ts create mode 100644 src/modules/payment/dto/create-payment.dto.ts create mode 100644 src/modules/payment/payment-api.module.ts create mode 100644 src/modules/payment/payment-status.query.ts create mode 100644 src/modules/payment/payment.controller.ts create mode 100644 src/modules/status-saga/status-saga.module.ts create mode 100644 test-visual/public/app.js create mode 100644 test-visual/public/index.html create mode 100644 test-visual/public/styles.css create mode 100644 test-visual/server.js create mode 100644 test/dlt-handler.spec.ts create mode 100644 test/helpers/fakes.ts create mode 100644 test/idempotency-consumers.spec.ts create mode 100644 test/payment-service.spec.ts create mode 100644 test/status-saga.spec.ts create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json diff --git a/.env.country.example b/.env.country.example new file mode 100644 index 00000000..0e7c2f10 --- /dev/null +++ b/.env.country.example @@ -0,0 +1,3 @@ +# Copy this file as .env.pe / .env.mx / .env.co +COUNTRY_CODE=pe +COUNTRY_CURRENCY=PEN diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..d0fa98b9 --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +NODE_ENV=development +PORT=3000 + +# Default values for Docker Compose network +DB_HOST=yape-postgres +DB_PORT=5432 +DB_USER=postgres +DB_PASSWORD=p0stgre$$ +DB_NAME=yape + +KAFKA_BROKERS=yape-kafka:9092 +KAFKA_CLUSTER_ID=4L6g3nShT-eMCtK--X86sw +KAFKA_CLIENT_ID=payment-platform +KAFKA_GROUP_PREFIX=challenge +SUPPORTED_COUNTRIES=pe,mx + +RELAY_POLL_MS=1000 +MAX_CONSUMER_RETRIES=3 + +# Enables per-country topic namespace: pe.payments.*, mx.payments.* +COUNTRY_NAMESPACE_ENABLED=true \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..78874008 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1.7 + +FROM node:20-alpine AS deps +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci + +FROM deps AS build +COPY tsconfig.json tsconfig.build.json jest.config.ts ./ +COPY src ./src +COPY test ./test +RUN npm run build + +FROM node:20-alpine AS runtime +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev && npm cache clean --force +COPY --from=build /app/dist ./dist +EXPOSE 3000 +CMD ["node", "dist/apps/payment-api/main.js"] diff --git a/Dockerfile.visual-demo b/Dockerfile.visual-demo new file mode 100644 index 00000000..f2bca3ba --- /dev/null +++ b/Dockerfile.visual-demo @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.7 + +FROM node:20-alpine +WORKDIR /app + +RUN apk add --no-cache docker-cli + +COPY test-visual ./test-visual + +ENV PORT=4000 +EXPOSE 4000 + +CMD ["node", "test-visual/server.js"] diff --git a/README.md b/README.md index 14377fae..ceb14deb 100644 --- a/README.md +++ b/README.md @@ -1,197 +1,370 @@ -# Yape Code Challenge ๐Ÿš€ +# Challenge 1 - Payment settlement pipeline -Welcome. This challenge is designed for experienced engineers being considered for **tech lead and staff-level** roles. It tests your ability to reason about distributed systems, event-driven architecture, and platform design โ€” not just your ability to ship working code. +## ยฟPor quรฉ elegรญ este challenge? -There are three challenges. **Pick one.** Go deep rather than broad. +Elegรญ el **Challenge 1 - Payment settlement pipeline** porque representa mejor los problemas que aparecen en un sistema fintech real: escrituras dobles, entrega confiable de eventos, redelivery, idempotencia, fallas parciales y consistencia eventual. ---- +No lo tomรฉ como un ejercicio de endpoints CRUD. Lo tomรฉ como un problema de garantรญas distribuidas: cรณmo crear un pago sin perder eventos, cรณmo procesarlo aunque Kafka reentregue mensajes, cรณmo responder un estado honesto mientras los consumidores terminan, y cรณmo aislar el impacto entre paรญses cuando un servicio crรญtico falla o tiene alto trรกfico. -## Table of Contents +Tambiรฉn elegรญ este reto porque permite explicar trade-offs importantes de arquitectura. En pagos no basta con que el happy path funcione; el diseรฑo debe seguir siendo correcto si el relay se cae, si un consumer procesa dos veces el mismo mensaje, si un paรญs se degrada o si un mensaje termina siendo imposible de procesar. -- [What we're evaluating](#what-were-evaluating) -- [Challenge 1 โ€” Payment settlement pipeline](#challenge-1--payment-settlement-pipeline) -- [Challenge 2 โ€” Wallet transfer with distributed saga](#challenge-2--wallet-transfer-with-distributed-saga) -- [Challenge 3 โ€” Shared platform library design](#challenge-3--shared-platform-library-design) -- [Tech stack](#tech-stack) -- [Submission](#submission) +## Resumen de la soluciรณn ---- +La soluciรณn implementa un pipeline de settlement basado en eventos con estos componentes: -## What we're evaluating +```text +Payment API -> PostgreSQL outbox -> Outbox Relay -> Kafka -> Fraud / Ledger -> Status Saga -> settled | failed | DLT +``` + +Los procesos principales son: + +```text +payment-api +outbox-relay +fraud-consumer +ledger-consumer +status-saga +``` + +La soluciรณn tambiรฉn incluye Docker Compose, scripts de bootstrap local, tests automatizados, namespace por paรญs, configuraciรณn por paรญs y un demo visual tipo Yape que permite ejecutar escenarios reales y ver eventos Kafka, evidencia en base de datos y disponibilidad por paรญs. -We're not looking for a perfect system. We're looking for evidence that you think like a tech lead: +## Decisiones arquitectรณnicas -- You treat trade-offs as first-class decisions, not implementation details. -- You build for the engineer who reads your code six months from now, not for the PR reviewer today. -- You can identify the antipattern in a brief before someone points it out to you. -- You know what you deliberately left out โ€” and why. +### 1. Transactional Outbox -Every challenge includes an **optional escalation**. It's genuinely optional; finishing the core well beats rushing to the escalation. +Decidรญ usar el patrรณn **Transactional Outbox** para evitar el problema clรกsico de dual write: ---- +```text +DB commit OK, pero Kafka publish falla +``` + +Cuando se crea un pago, el `PaymentService` guarda en la misma transacciรณn local: -## Challenge 1 โ€” Payment settlement pipeline +```text +payments +outbox_events +``` -### Premise +El broker no se llama dentro de esa transacciรณn. Esto es importante porque llamar Kafka dentro de una transacciรณn SQL no hace que Kafka participe de esa transacciรณn; solo crea una falsa sensaciรณn de atomicidad. -You're building the payment processing backbone for a multi-country wallet. A payment initiated in one country may touch ledger entries, fraud scoring, and notification services that are fully independent. Your solution must remain correct under partial failures and message redelivery. +La alternativa rechazada fue publicar directamente a Kafka desde el API durante la creaciรณn del pago. Es mรกs simple de programar, pero puede producir pรฉrdida silenciosa de eventos si la base de datos confirma y el broker falla despuรฉs. -### Architecture overview +### 2. Relay separado del API +El `outbox-relay` corre como proceso separado. Lee filas pendientes de `outbox_events`, publica a Kafka y marca la fila como publicada. +Esta separaciรณn permite que el ciclo de vida del relay no dependa del API HTTP. Tambiรฉn hace mรกs claro el modelo de fallas: + +```text +Si el relay muere antes de publicar, la fila queda pending y se reintenta. +Si el relay publica pero muere antes de marcar published, el evento puede publicarse otra vez. +Ese duplicado es aceptable porque los consumers son idempotentes. ``` -Payment API โ”€โ”€โ–บ Outbox table โ”€โ”€[relay]โ”€โ”€โ–บ Kafka topic - (payment.created.v1) - โ”‚ - โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ–ผ โ–ผ โ–ผ - FraudConsumer LedgerConsumer NotifyConsumer - (risk scoring) (double-entry write) (push / email) - โ”‚ โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ–ผ - Status saga - (eventual consistency) - โ”‚ - โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ–ผ (on failure) โ–ผ (on success) - DLT topic payment.settled.v1 - (payment.failed.v1) + +Para concurrencia en el relay se usa locking pesimista con `FOR UPDATE SKIP LOCKED`, de forma que varias instancias puedan reclamar lotes sin procesar la misma fila al mismo tiempo. + +### 3. PostgreSQL como base de datos local + +Elegรญ PostgreSQL porque ofrece garantรญas ACID sรณlidas para la transacciรณn local `payment + outbox`. Ademรกs soporta locks transaccionales como `FOR UPDATE SKIP LOCKED`, que encajan bien con un relay concurrente basado en polling. + +La alternativa de usar una base mรกs simple o almacenamiento en memoria habrรญa reducido la complejidad inicial, pero no demostrarรญa bien las garantรญas transaccionales que el challenge busca evaluar. + +### 4. Kafka con topics por paรญs + +Implementรฉ namespace por paรญs para los topics: + +```text +pe.payments.payment.created.v1 +mx.payments.payment.created.v1 +pe.payments.fraud.assessed.v1 +mx.payments.fraud.assessed.v1 +pe.payments.ledger.posted.v1 +mx.payments.ledger.posted.v1 ``` -### Required deliverables +Esto permite separar trรกfico, lag, DLTs y operaciรณn por paรญs. Si MX tiene alto trรกfico o una falla operativa, PE no debe quedar bloqueado por compartir el mismo flujo crรญtico. -1. **Transactional outbox** โ€” A `PaymentService` (NestJS) that writes a payment record and its outbox entry in a single local transaction. A separate relay process publishes to Kafka. The broker must never be called inside the database transaction. +La alternativa rechazada fue usar un รบnico topic global con `countryCode` dentro del payload. Esa opciรณn simplifica nombres de topics, pero mezcla lag y operaciรณn entre paรญses, lo cual complica aislamiento y respuesta ante incidentes. -2. **Idempotent consumers** โ€” At least two downstream consumers (`FraudConsumer`, `LedgerConsumer`) in separate NestJS modules. Reprocessing the same event twice must produce no observable side effect. +### 5. Estrategia hรญbrida multi-paรญs -3. **DLT handler** โ€” When a consumer exceeds its retry budget, emit a compensating event to a Dead Letter Topic rather than silently dropping the message. +No elegรญ que todo fuera global ni que todo fuera aislado por paรญs. Elegรญ una estrategia hรญbrida: -4. **Status query endpoint** โ€” A GET endpoint that reflects eventual consistency honestly. A payment may return `pending` after creation and only transition to `settled` or `failed` once both consumers have acknowledged. +```text +Globales: +payment-api +outbox-relay +fraud-consumer -### What a strong solution looks like +Aislados por paรญs: +ledger-consumer +status-saga +``` -- The outbox relay is a distinct process boundary โ€” not a `setInterval` in the same NestJS app. -- Idempotency keys live on the consumer side (keyed by `eventId`), not on the producer side. -- The status endpoint documents its consistency guarantees explicitly โ€” either in code comments or in an API response envelope. -- The candidate can explain what happens if the relay crashes between writing the outbox entry and publishing to Kafka. +`fraud-consumer` se mantiene global porque en esta soluciรณn representa una evaluaciรณn compartida y de menor costo operativo. En cambio, `ledger-consumer` y `status-saga` estรกn aislados por paรญs porque son parte crรญtica del settlement y del cierre del estado del pago. -### What disqualifies a solution +La alternativa de aislar todo por paรญs aumenta control operativo, pero tambiรฉn multiplica costo y complejidad desde el inicio. La alternativa de dejar todo global reduce costo, pero debilita el aislamiento ante picos o fallas localizadas. -- Calling `kafkaClient.emit()` directly inside a `@Transaction()` decorator. This is the most common mistake at this level and it produces silent data loss. +### 6. Consumer groups por paรญs -### Optional escalation +Los consumers crรญticos usan grupos por paรญs: -Add a per-country topic namespace (`pe.payments.payment.created.v1`, `mx.payments.payment.created.v1`) and document what that implies for consumer group strategy across countries. +```text +challenge.ledger-consumer.pe +challenge.ledger-consumer.mx +challenge.status-saga.pe +challenge.status-saga.mx +``` ---- +El consumer global usa un grupo global: -## Challenge 2 โ€” Wallet transfer with distributed saga +```text +challenge.fraud-consumer.global +``` -### Premise +Esto permite que el offset y el lag de los procesos crรญticos estรฉn separados por paรญs. Tambiรฉn deja el sistema preparado para escalar horizontalmente un paรญs sin tocar otro. -Transferring funds between two wallets in different countries requires debiting one ledger and crediting another atomically โ€” without a distributed transaction. You will implement a saga that is safe to replay from any step. +Por ejemplo, si MX recibe mรกs trรกfico, puedo escalar los workers de MX sin escalar PE: -### Required deliverables +```bash +docker compose -p yape-mx -f docker-compose.country.yml --env-file .env --env-file .env.mx --profile country up -d --scale ledger-consumer=3 --scale status-saga=2 +``` -1. **Transfer orchestrator** โ€” Implement a `TransferOrchestrator` that drives the following steps in order: +No implementรฉ autoscaling automรกtico porque el reto apunta a un entorno local con Docker Compose. En producciรณn agregarรญa HPA basado en consumer lag, retry rate, CPU y latencia por paรญs. - ``` - DebitWallet โ†’ CreditWallet โ†’ SettleFX โ†’ EmitReceipt - ``` +### 7. Idempotencia del lado consumidor - You may use Temporal, a hand-rolled state machine, or pure Kafka choreography. You must justify the choice in writing. +La idempotencia vive del lado consumidor, no del productor. Cada consumer registra una marca en `processed_events` antes de generar efectos observables. -2. **Compensation on failure** โ€” If `CreditWallet` fails after `DebitWallet` succeeds, the orchestrator must issue a `ReverseDebit` compensation event. Silent failure is not acceptable. +La clave lรณgica es: -3. **CQRS read model** โ€” A `TransferReadModel` updated via projected events, not by reading the write-side database. The read model must be consistent enough to serve a GET within 500ms of the saga completing. +```text +consumerName + countryCode + eventId +``` + +Esto es mรกs seguro que usar solo `eventId`, porque en un sistema multi-paรญs se evita una colisiรณn accidental entre eventos de distintos paรญses. Tambiรฉn es mรกs correcto que deduplicar por `paymentId`, porque un mismo pago puede producir varios eventos distintos. + +La alternativa rechazada fue confiar solo en Kafka o en el productor para evitar duplicados. Kafka puede entregar mรกs de una vez bajo ciertos escenarios; por eso cada consumer debe ser capaz de recibir el mismo evento nuevamente sin duplicar side effects. -4. **Concurrency safety** โ€” If two transfers attempt to debit the same wallet simultaneously, the second must detect the conflict and fail fast. A negative balance is never acceptable. +### 8. Status Saga basada en eventos -### What a strong solution looks like +Implementรฉ una `status-saga` que escucha: + +```text +fraud.assessed.v1 +ledger.posted.v1 +``` -- The candidate picks a clear position on choreography vs. orchestration and can articulate the trade-off: choreography reduces coupling but makes the overall saga state invisible; orchestration makes state explicit but introduces a coordinator as a single point of failure. -- The idempotency key is placed on the saga instance, not on individual commands, and the candidate can explain why. -- The read model answers the question: "how do I know the read model isn't serving stale data immediately after the saga closes?" โ€” whether via versioned events, a subscription mechanism, or a documented staleness window. +La saga actualiza `payment_steps` y reconcilia el estado final del pago: -### What disqualifies a solution +```text +fraud succeeded + ledger succeeded -> payment.settled.v1 +fraud failed o ledger failed -> payment.failed.v1 +faltan ACKs -> payment sigue pending +``` -A single database transaction spanning two service databases. This is the antipattern the challenge is explicitly designed to surface. +Esto hace que el endpoint de status sea honesto con la consistencia eventual. Un pago reciรฉn creado puede devolver `pending` hasta que los consumidores confirmen. -### Optional escalation +La alternativa rechazada fue marcar el pago como `settled` inmediatamente despuรฉs de publicarlo a Kafka. Eso serรญa incorrecto porque publicar un evento no significa que fraude y ledger ya hayan terminado. -Model the FX settlement step as an external API call with a timeout. Show how the saga handles a timeout that leaves the FX state ambiguous โ€” neither confirmed nor rejected. +### 9. DLT por topic original ---- +Si un consumer agota su presupuesto de retries, el mensaje se envรญa a un Dead Letter Topic: -## Challenge 3 โ€” Shared platform library design +```text +{original-topic}.dlt +``` -### Premise +Ejemplos: -Your platform team owns the internal libraries that all product squads import. You've been asked to design and ship `@yape/kafka-module` โ€” a NestJS dynamic module that wraps Kafka producer and consumer setup, enforces topic naming conventions, wires DLT automatically, and exposes typed event contracts. You are the only author. Four squads will consume it within the quarter. +```text +pe.payments.payment.created.v1.dlt +mx.payments.ledger.posted.v1.dlt +``` -### Required deliverables +Esto evita perder mensajes silenciosamente. Tambiรฉn conserva trazabilidad por paรญs y por tipo de evento. -1. **Dynamic module API** โ€” A `KafkaModule.forFeature({ topics, consumerGroup })` dynamic module. The module must register producers and consumers via NestJS dependency injection, not global singletons. +Separรฉ conceptualmente DLT de `payment.failed.v1`. El DLT representa una falla tรฉcnica de procesamiento o un mensaje invรกlido. `payment.failed.v1` representa un resultado de negocio o de saga donde el pago sรญ llegรณ a un estado terminal fallido. -2. **`@KafkaEvent()` decorator** โ€” A `@KafkaEvent(topicName)` decorator that binds a handler method to a Kafka consumer, analogous to how NestJS `@MessagePattern` works internally. +### 10. Configuraciรณn de paรญs y moneda -3. **Automatic DLT wiring** โ€” If a handler throws and exceeds `maxRetries`, the module routes the message to `{original-topic}.dlt` without any code change required in the consuming squad. +Cada paรญs tiene su propio archivo de configuraciรณn: -4. **`EventContract` type** โ€” A generic type that enforces schema shape at compile time. Squads must not be able to publish to a topic with a payload that doesn't match the declared contract. A type mismatch must be a TypeScript compile error, not a runtime exception. +```text +.env.pe -> COUNTRY_CODE=pe, COUNTRY_CURRENCY=PEN +.env.mx -> COUNTRY_CODE=mx, COUNTRY_CURRENCY=MXN +.env.co -> COUNTRY_CODE=co, COUNTRY_CURRENCY=COP +``` -5. **ADR (Architecture Decision Record)** โ€” Written in MADR format, covering: - - Why NestJS dynamic modules over a plain exported class. - - How you handle schema evolution without breaking consumers who still reference an older version. - - What you would add with two more weeks. +Decidรญ guardar la moneda en `.env.` porque la moneda default es una propiedad operativa del proceso paรญs. Si maรฑana se agrega Colombia, no deberรญa tocarse el cรณdigo central para saber que `co` usa `COP`. -### What a strong solution looks like +Los scripts `init-local.sh` y `add-new-country.sh` preguntan y validan la moneda usando formato ISO de tres letras. -- The module API feels native to NestJS. A squad importing it should not need to understand Kafka internals to publish an event. -- Schema evolution is addressed concretely: additive fields, topic versioning (`payment.created.v2`), or a schema registry โ€” the candidate picks one and defends it, with trade-offs acknowledged. -- The ADR reads like it was written for a real team, not as a post-hoc justification. It documents the options that were rejected and why. +### 11. Docker Compose reproducible -### What a weak solution looks like +Incluรญ un entorno local con Docker Compose para levantar infraestructura y servicios sin depender de instalaciones locales de Node o npm. -- The module wraps Kafka imperatively and tells squads to call `producer.send()` directly. -- `EventContract` is a runtime validation only (e.g. a Zod schema), with no compile-time enforcement. -- The ADR is a bulleted list with no trade-off reasoning. +La estructura estรก separada en dos capas: -### Optional escalation +```text +docker-compose.yml -> core compartido +docker-compose.country.yml -> workers aislados por paรญs +``` -Publish the module to a local [Verdaccio](https://verdaccio.org/) registry. Document your versioning and release strategy, including how you would communicate breaking changes to consuming squads. +Esto evita duplicar YAML por cada paรญs. Para agregar un paรญs nuevo se crea `.env.` y se levanta el mismo compose con otro project name: + +```bash +docker compose -p yape-co -f docker-compose.country.yml --env-file .env --env-file .env.co --profile country up -d --build +``` + +### 12. Visual demo para explicar el sistema + +Ademรกs de los tests, agreguรฉ un demo visual tipo Yape. No lo hice como reemplazo de pruebas, sino como herramienta de explicaciรณn. + +El demo permite ver al mismo tiempo: + +```text +pantalla de usuario +estado de servicios +lรญnea de tiempo del pipeline +eventos Kafka +evidencia en Postgres +estado por paรญs +``` + +Tambiรฉn incluye escenarios como: + +```text +success +fraud rejected +ledger down + retry +timeout pending +DLT invalid payload +replay idempotente +paรญs aislado +MX caรญdo, PE disponible +``` ---- +El escenario `MX caรญdo, PE disponible` apaga workers crรญticos de MX y ejecuta un pago PE. La intenciรณn es demostrar visualmente que una caรญda operativa en MX no afecta la disponibilidad del settlement de PE. -## Tech stack +## Alternativas rechazadas -The following is the expected stack. Deviations are acceptable if you document the reason. +### Publicar en Kafka dentro de la transacciรณn SQL -| Layer | Expected | -|---|---| -| Runtime | Node.js 20+ | -| Framework | NestJS | -| Messaging | Kafka (local via Docker, or Confluent Cloud) | -| Database | Your choice โ€” document why | -| Orchestration | Temporal, native Kafka, or a state machine โ€” justify the choice | -| Language | TypeScript (strict mode) | -| Containers | Docker Compose for local environment | +La rechacรฉ porque no garantiza atomicidad real entre PostgreSQL y Kafka. Si una parte falla, el sistema puede quedar inconsistente. ---- +### Usar una transacciรณn distribuida o 2PC -## Submission +La rechacรฉ porque aumenta complejidad y no es una soluciรณn prรกctica para este escenario local. El outbox ofrece una garantรญa suficiente y mรกs operable: persistir primero, publicar despuรฉs, y tolerar duplicados con idempotencia. -1. Fork this repository. -2. Create a branch named `challenge/{your-name}`. -3. Open a pull request against `main` in this repository. +### Usar Debezium o CDC desde el inicio -Your PR description must include: +CDC serรญa una buena evoluciรณn para reducir polling y mejorar throughput, pero para el challenge preferรญ una implementaciรณn explรญcita del relay. Es mรกs fรกcil de revisar y deja clara la decisiรณn de no llamar Kafka dentro del transaction boundary. -- Which challenge you chose and why. -- The key architectural decisions you made and the alternatives you rejected. -- What you would do differently with more time. -- Any known limitations or shortcuts taken. +### Usar Temporal -**There is no time limit stated intentionally.** A focused solution delivered in four hours tells us more than an exhaustive one delivered in two days. Prioritise depth of reasoning over breadth of features. +Temporal serรญa รบtil para sagas mรกs largas, con timers, compensaciones complejas y pasos externos ambiguos. Para este challenge, una saga ligera basada en eventos era suficiente y mantiene menos moving parts. -If you have questions, open an issue on this repository. We respond to issues within one business day. +### Hacer todos los servicios por paรญs + +Lo rechacรฉ como primera versiรณn porque multiplica despliegues y costo operativo. Preferรญ aislar los procesos crรญticos y mantener globales los componentes que no necesitan aislamiento fuerte en esta etapa. + +### Hacer todo global + +Tambiรฉn lo rechacรฉ porque debilita el aislamiento. Si un paรญs genera lag o tiene una falla de ledger, no deberรญa impactar el cierre de pagos de otro paรญs. + +### Usar BIAN como eje principal + +No modelรฉ la soluciรณn bajo BIAN porque el challenge no lo pedรญa. Preferรญ concentrarme en garantรญas distribuidas, idempotencia, DLT, outbox y operaciรณn multi-paรญs. Si la organizaciรณn usa BIAN como marco de gobierno, los mรณdulos podrรญan mapearse posteriormente a dominios como Payments, Fraud, Ledger y Notifications. + +## Quรฉ pasa ante fallas + +### Si Kafka falla cuando se crea el pago + +No se pierde el pago ni el evento, porque el API no llama Kafka. El pago y la fila outbox ya quedaron persistidos en PostgreSQL. El relay seguirรก intentando publicar cuando Kafka vuelva. + +### Si el relay muere antes de publicar + +La fila queda pendiente en `outbox_events`. Al reiniciar el relay, la fila se toma nuevamente y se publica. + +### Si el relay publica y muere antes de marcar published + +El evento puede publicarse otra vez. Este duplicado es tolerado porque Fraud, Ledger y Status Saga son idempotentes por `consumerName + countryCode + eventId`. + +### Si un consumer recibe el mismo evento dos veces + +Consulta `processed_events`. Si ya existe la marca de procesamiento, no ejecuta de nuevo el side effect. + +### Si un consumer agota retries + +El mensaje se envรญa al DLT correspondiente. No se descarta silenciosamente. + +### Si MX cae o tiene alto trรกfico + +Los workers crรญticos de PE siguen con su propio consumer group y sus propios topics. El diseรฑo permite operar y escalar por paรญs. + +## Quรฉ harรญa diferente con mรกs tiempo + +- Agregarรญa migraciones TypeORM versionadas en lugar de depender de sincronizaciรณn automรกtica de esquema en local. +- Agregarรญa Schema Registry o validaciรณn formal de contratos para eventos, con reglas de compatibilidad backward/forward. +- Agregarรญa OpenTelemetry para trazabilidad end-to-end desde `POST /payments` hasta `payment.settled.v1` o DLT. +- Agregarรญa mรฉtricas por paรญs: consumer lag, retry rate, DLT rate, tiempo en pending y throughput por topic. +- Agregarรญa dashboards y alertas por paรญs para detectar degradaciรณn localizada. +- Agregarรญa limpieza/retenciรณn de `processed_events` para controlar crecimiento de la tabla de idempotencia. +- Evaluarรญa CDC con Debezium para reemplazar polling del outbox si el volumen crece. +- Implementarรญa autoscaling en Kubernetes con HPA basado en Kafka lag por paรญs. +- Separarรญa fรญsicamente bases o esquemas por dominio si el sistema evolucionara hacia microservicios mรกs independientes. +- Agregarรญa runbooks de replay de DLT y procedimientos seguros para reprocesamiento. + +## Limitaciones conocidas + +- Uso una sola base PostgreSQL para simplificar el entorno local del challenge. En producciรณn, Ledger, Fraud y Payments podrรญan tener ownership de datos mรกs separado. +- Los eventos downstream de algunos workers se publican directamente desde el consumer. Con mรกs tiempo aplicarรญa outbox tambiรฉn para esos eventos si se requiere la misma garantรญa fuerte que en `payment.created.v1`. +- El entorno Kafka local usa una topologรญa simple de desarrollo, no un cluster productivo multi-broker. +- El relay usa polling. Es correcto para el challenge, pero en producciรณn evaluarรญa CDC o tuning de polling/backoff. +- El fraud scoring es intencionalmente simple. El foco del reto estรก en garantรญas distribuidas, no en un motor real de riesgo. +- No implementรฉ autenticaciรณn/autorizaciรณn porque no era el foco del challenge. +- No implementรฉ autoscaling automรกtico; dejรฉ la arquitectura lista para escalar por paรญs, pero las reglas productivas vivirรญan en Kubernetes/observabilidad. +- El visual demo monta Docker socket para controlar containers en laboratorio local. Es รบtil para demostrar fallas, pero no es un patrรณn recomendado para producciรณn. + +## Cรณmo validรฉ la soluciรณn + +Incluรญ pruebas automatizadas para los puntos crรญticos: + +```text +payment-service +idempotency-consumers +status-saga +DLT handler +``` + +Tambiรฉn agreguรฉ scripts para operaciรณn local: + +```bash +./scripts/init-local.sh +./scripts/run-test.sh +./scripts/run-kafka-scenario.sh +./scripts/add-new-country.sh +./scripts/cleanup-docker.sh +``` + +Y un demo visual en: + +```text +http://localhost:4000 +``` + +El objetivo del demo visual es facilitar la explicaciรณn en entrevista: se puede ejecutar un pago, ver el evento en Kafka, ver las filas reales en Postgres y demostrar quรฉ ocurre bajo fallas parciales. + +## Cierre + +La decisiรณn principal fue priorizar garantรญas operativas sobre simplicidad superficial. El sistema acepta que Kafka puede redeliver, que los consumidores pueden fallar, que la consistencia es eventual y que un paรญs puede degradarse sin bloquear a otro. + +La promesa de esta soluciรณn no es consistencia inmediata. La promesa es: + +```text +no perder eventos, +no duplicar efectos, +reflejar el estado honestamente, +y aislar fallas crรญticas por paรญs. +``` diff --git a/docker-compose.country.yml b/docker-compose.country.yml new file mode 100644 index 00000000..74b2b484 --- /dev/null +++ b/docker-compose.country.yml @@ -0,0 +1,42 @@ +x-country-env: &country-env + NODE_ENV: ${NODE_ENV:-production} + DB_HOST: ${DB_HOST:-yape-postgres} + DB_PORT: ${DB_PORT:-5432} + DB_USER: ${DB_USER:-postgres} + DB_PASSWORD: ${DB_PASSWORD:-postgres} + DB_NAME: ${DB_NAME:-yape} + KAFKA_BROKERS: ${KAFKA_BROKERS:-yape-kafka:9092} + KAFKA_GROUP_PREFIX: ${KAFKA_GROUP_PREFIX:-challenge} + SUPPORTED_COUNTRIES: ${SUPPORTED_COUNTRIES:-pe,mx} + MAX_CONSUMER_RETRIES: ${MAX_CONSUMER_RETRIES:-3} + COUNTRY_NAMESPACE_ENABLED: ${COUNTRY_NAMESPACE_ENABLED:-true} + CONSUMER_COUNTRY: ${COUNTRY_CODE:?COUNTRY_CODE is required} + COUNTRY_CURRENCY: ${COUNTRY_CURRENCY:?COUNTRY_CURRENCY is required} + +x-country-service: &country-service + build: + context: . + dockerfile: Dockerfile + networks: [yape-shared] + +services: + ledger-consumer: + <<: *country-service + profiles: ["country", "ledger"] + environment: + <<: *country-env + KAFKA_CLIENT_ID: ledger-consumer-${COUNTRY_CODE} + command: ["npm", "run", "start:ledger-consumer"] + + status-saga: + <<: *country-service + profiles: ["country", "saga"] + environment: + <<: *country-env + KAFKA_CLIENT_ID: status-saga-${COUNTRY_CODE} + command: ["npm", "run", "start:status-saga"] + +networks: + yape-shared: + external: true + name: yape-shared diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..f8a3d1c8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,173 @@ +x-app-env: &app-env + NODE_ENV: ${NODE_ENV:-production} + PORT: ${PORT:-3000} + DB_HOST: ${DB_HOST:-yape-postgres} + DB_PORT: ${DB_PORT:-5432} + DB_USER: ${DB_USER:-postgres} + DB_PASSWORD: ${DB_PASSWORD:-postgres} + DB_NAME: ${DB_NAME:-yape} + KAFKA_BROKERS: ${KAFKA_BROKERS:-yape-kafka:9092} + KAFKA_GROUP_PREFIX: ${KAFKA_GROUP_PREFIX:-challenge} + SUPPORTED_COUNTRIES: ${SUPPORTED_COUNTRIES:-pe,mx} + RELAY_POLL_MS: ${RELAY_POLL_MS:-1000} + MAX_CONSUMER_RETRIES: ${MAX_CONSUMER_RETRIES:-3} + COUNTRY_NAMESPACE_ENABLED: ${COUNTRY_NAMESPACE_ENABLED:-true} + +x-app-service: &app-service + build: + context: . + dockerfile: Dockerfile + depends_on: + postgres: + condition: service_healthy + kafka: + condition: service_healthy + topic-init: + condition: service_completed_successfully + networks: [yape-shared] + +services: + postgres: + image: postgres:16-alpine + container_name: yape-postgres + profiles: ["core", "all", "db"] + environment: + POSTGRES_DB: ${DB_NAME:-yape} + POSTGRES_USER: ${DB_USER:-postgres} + POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres} + ports: + - "${DB_PORT:-5432}:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + networks: [yape-shared] + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-yape}"] + interval: 5s + timeout: 5s + retries: 15 + + kafka: + image: apache/kafka:3.9.0 + container_name: yape-kafka + profiles: ["core", "all", "kafka"] + environment: + CLUSTER_ID: ${KAFKA_CLUSTER_ID:-4L6g3nShT-eMCtK--X86sw} + KAFKA_NODE_ID: "1" + KAFKA_PROCESS_ROLES: "broker,controller" + KAFKA_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9093" + KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://yape-kafka:9092" + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" + KAFKA_CONTROLLER_QUORUM_VOTERS: "1@yape-kafka:9093" + KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER" + KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT" + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false" + KAFKA_NUM_PARTITIONS: "3" + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1" + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1" + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1" + KAFKA_LOG_DIRS: "/var/lib/kafka/data" + ports: + - "9092:9092" + volumes: + - kafka_data:/var/lib/kafka/data + networks: [yape-shared] + healthcheck: + test: ["CMD-SHELL", "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list >/dev/null 2>&1"] + interval: 10s + timeout: 10s + retries: 12 + + kafka-ui: + image: provectuslabs/kafka-ui:latest + container_name: yape-kafka-ui + profiles: ["kafka-ui", "all"] + environment: + KAFKA_CLUSTERS_0_NAME: local + KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: yape-kafka:9092 + ports: + - "8080:8080" + networks: [yape-shared] + depends_on: + kafka: + condition: service_healthy + + topic-init: + image: apache/kafka:3.9.0 + container_name: yape-topic-init + profiles: ["init-topics", "all"] + restart: "no" + depends_on: + kafka: + condition: service_healthy + environment: + KAFKA_BROKER: yape-kafka:9092 + SUPPORTED_COUNTRIES: ${SUPPORTED_COUNTRIES:-pe,mx} + TOPIC_PARTITIONS: ${TOPIC_PARTITIONS:-3} + TOPIC_REPLICATION_FACTOR: ${TOPIC_REPLICATION_FACTOR:-1} + volumes: + - ./scripts/init-topics.sh:/init-topics.sh:ro + networks: [yape-shared] + entrypoint: ["/bin/bash", "/init-topics.sh"] + + payment-api: + <<: *app-service + container_name: yape-payment-api + profiles: ["payment-api", "all"] + ports: + - "${PORT:-3000}:3000" + environment: + <<: *app-env + KAFKA_CLIENT_ID: ${PAYMENT_API_CLIENT_ID:-payment-api} + command: ["npm", "run", "start:payment-api"] + + outbox-relay: + <<: *app-service + container_name: yape-outbox-relay + profiles: ["outbox-relay", "all"] + environment: + <<: *app-env + KAFKA_CLIENT_ID: ${OUTBOX_RELAY_CLIENT_ID:-outbox-relay} + command: ["npm", "run", "start:outbox-relay"] + + fraud-consumer: + <<: *app-service + container_name: yape-fraud-consumer + profiles: ["fraud-consumer", "all"] + environment: + <<: *app-env + KAFKA_CLIENT_ID: ${FRAUD_CLIENT_ID:-fraud-consumer} + command: ["npm", "run", "start:fraud-consumer"] + + visual-demo: + build: + context: . + dockerfile: Dockerfile.visual-demo + container_name: yape-visual-demo + profiles: ["visual-demo", "all"] + depends_on: + kafka: + condition: service_healthy + payment-api: + condition: service_started + environment: + PORT: 4000 + PAYMENT_API_URL: http://payment-api:3000 + KAFKA_CONTAINER: yape-kafka + POSTGRES_CONTAINER: yape-postgres + SUPPORTED_COUNTRIES: ${SUPPORTED_COUNTRIES:-pe,mx} + DB_USER: ${DB_USER:-postgres} + DB_PASSWORD: ${DB_PASSWORD:-postgres} + DB_NAME: ${DB_NAME:-yape} + ports: + - "4000:4000" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + networks: [yape-shared] + +networks: + yape-shared: + name: yape-shared + +volumes: + postgres_data: + kafka_data: diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000..9860bf67 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,15 @@ +import type { Config } from 'jest'; + +const config: Config = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/test'], + moduleFileExtensions: ['ts', 'js', 'json'], + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + '^test/(.*)$': '/test/$1', + }, + clearMocks: true, +}; + +export default config; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..86eb26ec --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6724 @@ +{ + "name": "yape-challenge-payment-settlement", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "yape-challenge-payment-settlement", + "version": "1.0.0", + "dependencies": { + "@nestjs/common": "^10.4.8", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.4.8", + "@nestjs/platform-express": "^10.4.8", + "@nestjs/typeorm": "^10.0.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "kafkajs": "^2.2.4", + "pg": "^8.13.1", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "typeorm": "^0.3.20", + "uuid": "^11.0.3" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^22.10.2", + "@types/uuid": "^10.0.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "tsc-alias": "^1.8.10", + "typescript": "^5.7.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "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.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "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.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "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", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "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.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "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", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "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/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nestjs/common": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", + "integrity": "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==", + "license": "MIT", + "dependencies": { + "file-type": "20.4.1", + "iterare": "1.2.1", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/config": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", + "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.5", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/core": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.22.tgz", + "integrity": "sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "3.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@nestjs/websockets": "^10.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/platform-express": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.22.tgz", + "integrity": "sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==", + "license": "MIT", + "dependencies": { + "body-parser": "1.20.4", + "cors": "2.8.5", + "express": "4.22.1", + "multer": "2.0.2", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0" + } + }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.2.tgz", + "integrity": "sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==", + "license": "MIT", + "dependencies": { + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, + "node_modules/@nestjs/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "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", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/inflate/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@tokenizer/inflate/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "devOptional": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", + "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001787", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", + "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.4.tgz", + "integrity": "sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.22" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.335", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.335.tgz", + "integrity": "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/file-type": { + "version": "20.4.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", + "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "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/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "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-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kafkajs": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-2.2.4.tgz", + "integrity": "sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.41", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.41.tgz", + "integrity": "sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA==", + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/mylas": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.14.tgz", + "integrity": "sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "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/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "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.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "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/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "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", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-jest": { + "version": "29.4.9", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", + "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.4", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsc-alias": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.16.tgz", + "integrity": "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "get-tsconfig": "^4.10.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" + }, + "engines": { + "node": ">=16.20.2" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typeorm": { + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", + "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", + "license": "MIT", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "ansis": "^4.2.0", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "dayjs": "^1.11.19", + "debug": "^4.4.3", + "dedent": "^1.7.0", + "dotenv": "^16.6.1", + "glob": "^10.5.0", + "reflect-metadata": "^0.2.2", + "sha.js": "^2.4.12", + "sql-highlight": "^6.1.0", + "tslib": "^2.8.1", + "uuid": "^11.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@sap/hana-client": "^2.14.22", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typeorm/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "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", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..e5b6ae15 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "yape-challenge-payment-settlement", + "version": "1.0.0", + "private": true, + "description": "Challenge 1 - Payment settlement pipeline", + "scripts": { + "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", + "start:payment-api": "node dist/apps/payment-api/main.js", + "start:outbox-relay": "node dist/apps/outbox-relay/main.js", + "start:fraud-consumer": "node dist/apps/fraud-consumer/main.js", + "start:ledger-consumer": "node dist/apps/ledger-consumer/main.js", + "start:status-saga": "node dist/apps/status-saga/main.js", + "start:visual-demo": "node test-visual/server.js", + "dev:payment-api": "ts-node src/apps/payment-api/main.ts", + "dev:outbox-relay": "ts-node src/apps/outbox-relay/main.ts", + "dev:fraud-consumer": "ts-node src/apps/fraud-consumer/main.ts", + "dev:ledger-consumer": "ts-node src/apps/ledger-consumer/main.ts", + "dev:status-saga": "ts-node src/apps/status-saga/main.ts", + "test": "jest --runInBand", + "test:watch": "jest --watch" + }, + "dependencies": { + "@nestjs/common": "^10.4.8", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.4.8", + "@nestjs/platform-express": "^10.4.8", + "@nestjs/typeorm": "^10.0.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "kafkajs": "^2.2.4", + "pg": "^8.13.1", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "typeorm": "^0.3.20", + "uuid": "^11.0.3" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^22.10.2", + "@types/uuid": "^10.0.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", + "tsc-alias": "^1.8.10", + "typescript": "^5.7.2" + } +} diff --git a/run-init.md b/run-init.md new file mode 100644 index 00000000..cd8c1a99 --- /dev/null +++ b/run-init.md @@ -0,0 +1,167 @@ +# Run local con Docker + +Esta guรญa explica el flujo mรกs simple para levantar y limpiar el proyecto usando los scripts incluidos. + +## Requisito + +Solo necesitas tener Docker Desktop instalado y corriendo. + +## Iniciar el proyecto + +Desde la raรญz del proyecto ejecuta: + +```bash +./scripts/init-local.sh +``` + +El script te irรก preguntando: + +```text +Use default DB password? +Add SUPPORTED_COUNTRIES? +Currency for pe? +Currency for mx? +Enable Kafka UI? +Enable Visual Demo? +Start Docker services now? +``` + +Para una primera corrida recomendada puedes usar: + +```text +SUPPORTED_COUNTRIES=pe,mx +pe -> PEN +mx -> MXN +Kafka UI -> yes +Visual Demo -> yes +Start Docker services -> yes +``` + +Al finalizar deberรญas ver URLs similares a: + +```text +Payment API : http://localhost:3000 +Kafka broker: localhost:9092 +Postgres : localhost:5432 +Kafka UI : http://localhost:8080 +Visual Demo : http://localhost:4000 +``` + +## Quรฉ levanta el init + +El script levanta el stack core: + +```text +postgres +kafka +topic-init +payment-api +outbox-relay +fraud-consumer +``` + +Y levanta workers aislados por paรญs: + +```text +yape-pe: ledger-consumer, status-saga +yape-mx: ledger-consumer, status-saga +``` + +Si configuraste mรกs paรญses, crearรก un proyecto Docker por cada paรญs: + +```text +yape-co +yape-cl +yape-ar +``` + +## Ver el demo visual + +Abre: + +```text +http://localhost:4000 +``` +![Diagrama de arquitectura](visual-yape.png) +Escenarios recomendados para probar: + +```text +Success: settled +Fraud rejected: failed +Ledger down + retry +Timeout: pending honesto +DLT: payload invalido +Replay: idempotencia +MX caido, PE disponible +``` + +## Ver Kafka UI + +Abre: + +```text +http://localhost:8080 +``` + +Ahรญ puedes ver topics como: + +```text +pe.payments.payment.created.v1 +mx.payments.payment.created.v1 +pe.payments.payment.created.v1.dlt +mx.payments.payment.created.v1.dlt +``` + +## Borrar todo el Docker del proyecto + +Si quieres limpiar el laboratorio y volver a correr el init desde cero: + +```bash +./scripts/cleanup-docker.sh +``` + +El script mostrarรก un resumen de lo que va a eliminar: + +```text +containers +networks +volumes +imagenes locales del proyecto +``` + +Para evitar borrados accidentales, te pedirรก escribir exactamente: + +```text +CONFIRM +``` + +Despuรฉs de limpiar, puedes volver a iniciar todo con: + +```bash +./scripts/init-local.sh +``` + +## Cuรกndo usar cada script + +Usa `init-local.sh` cuando quieras: + +```text +crear .env inicial +crear .env.pe / .env.mx +configurar monedas por paรญs +levantar Docker +abrir Kafka UI y Visual Demo +``` + +Usa `cleanup-docker.sh` cuando quieras: + +```text +apagar todo el proyecto +borrar volรบmenes de Postgres/Kafka +probar el bootstrap desde cero +cambiar paรญses o monedas desde cero +``` + +## Nota importante + +`cleanup-docker.sh` estรก diseรฑado para borrar recursos de este proyecto, no todo tu Docker local. Aun asรญ, revisa el preview antes de escribir `CONFIRM`. diff --git a/scripts/README-run-kafka-scenario.md b/scripts/README-run-kafka-scenario.md new file mode 100644 index 00000000..cf83d0c5 --- /dev/null +++ b/scripts/README-run-kafka-scenario.md @@ -0,0 +1,88 @@ +# run-kafka-scenario.sh + +Script de demo E2E para ver eventos reales del pipeline en Kafka, con salida guiada en consola. + +## Quรฉ hace + +`./scripts/run-kafka-scenario.sh` automatiza: + +1. Verifica/levanta servicios base necesarios (`kafka`, `payment-api`, `outbox-relay`, `fraud-consumer`, `ledger-consumer`, `status-saga`). +2. Crea un pago real vรญa `POST /payments`. +3. Observa topics Kafka del paรญs seleccionado. +4. Consulta estado en `GET /payments/:id/status` hasta estado final o timeout. +5. Muestra resumen final del escenario. + +## Escenarios + +## 1) `success` (implementado) + +Flujo normal: +- `payment.created.v1` +- `fraud.assessed.v1` +- `ledger.posted.v1` +- `payment.settled.v1` + +Ejemplo: + +```bash +./scripts/run-kafka-scenario.sh --scenario success --country pe +``` + +## 2) `failure-stop-ledger` (implementado) + +Fallo operacional controlado: +- Detiene `ledger-consumer` del paรญs. +- Crea el pago (debe quedarse `pending` mientras ledger estรก caรญdo). +- Vuelve a levantar `ledger-consumer`. +- Verifica recuperaciรณn y cierre eventual. + +Ejemplo: + +```bash +./scripts/run-kafka-scenario.sh --scenario failure-stop-ledger --country pe +``` + +Alias vรกlido: + +```bash +./scripts/run-kafka-scenario.sh --scenario failure --country pe +``` + +## 3) Escenarios recomendados a futuro (no implementados aรบn) + +- `failure-stop-fraud`: simular caรญda de fraude. +- `failure-dlt-invalid-payload`: inyectar payload invรกlido para observar retries + DLT. +- `country-parallel`: disparar pagos simultรกneos en `pe` y `mx` para mostrar aislamiento por paรญs. + +## Parรกmetros รบtiles + +```bash +./scripts/run-kafka-scenario.sh \ + --scenario success \ + --country pe \ + --wallet-id wallet-e2e-001 \ + --amount 10.50 \ + --currency PEN \ + --api-url http://localhost:3000 \ + --timeout 90 \ + --failure-hold 12 \ + --watch-timeout-ms 120000 +``` + +## Requisitos + +- Docker + Docker Compose +- `curl` +- `node` +- `.env` y `.env.` existentes + +## Salida esperada + +El script imprime: +- `paymentId` creado +- snapshots del estado eventual +- topics con eventos capturados para ese `paymentId` +- enlaces/comandos de logs para troubleshooting + +Kafka UI: +- [http://localhost:8080](http://localhost:8080) diff --git a/scripts/add-new-country.sh b/scripts/add-new-country.sh new file mode 100755 index 00000000..290c3576 --- /dev/null +++ b/scripts/add-new-country.sh @@ -0,0 +1,324 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +ENV_FILE=".env" +ENV_EXAMPLE=".env.example" +COUNTRY_TEMPLATE=".env.country.example" +COUNTRY_COMPOSE_FILE="docker-compose.country.yml" +CORE_COMPOSE_FILE="docker-compose.yml" + +log() { + printf '[country] %s\n' "$*" +} + +error() { + printf '[country][error] %s\n' "$*" >&2 +} + +ask_yes_no() { + local question="$1" + local default_answer="$2" + + while true; do + local suffix="[y/N]" + if [[ "$default_answer" == "y" ]]; then + suffix="[Y/n]" + fi + + read -r -p "$question $suffix: " reply + reply="${reply:-$default_answer}" + + case "$reply" in + y|Y|yes|YES) + return 0 + ;; + n|N|no|NO) + return 1 + ;; + *) + echo "Please answer y or n." + ;; + esac + done +} + +normalize_country() { + echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d ' ' +} + +validate_country() { + local country="$1" + [[ "$country" =~ ^[a-z]{2}$ ]] +} + +default_currency_for_country() { + case "$1" in + pe) echo "PEN" ;; + mx) echo "MXN" ;; + co) echo "COP" ;; + cl) echo "CLP" ;; + ar) echo "ARS" ;; + br) echo "BRL" ;; + ec) echo "USD" ;; + *) echo "$(echo "$1" | tr '[:lower:]' '[:upper:]')" ;; + esac +} + +validate_currency() { + local currency="$1" + [[ "$currency" =~ ^[A-Z]{3}$ ]] +} + +ensure_env_file() { + if [[ -f "$ENV_FILE" ]]; then + return + fi + + if [[ ! -f "$ENV_EXAMPLE" ]]; then + error "No existe ${ENV_FILE} ni ${ENV_EXAMPLE}." + exit 1 + fi + + cp "$ENV_EXAMPLE" "$ENV_FILE" + log "Se creรณ ${ENV_FILE} desde ${ENV_EXAMPLE}." +} + +get_env_var() { + local key="$1" + grep -E "^[[:space:]]*${key}=" "$ENV_FILE" | head -n 1 | cut -d '=' -f 2- || true +} + +set_env_var() { + local key="$1" + local value="$2" + local tmp + + tmp="$(mktemp)" + awk -v key="$key" -v value="$value" ' + BEGIN { updated = 0 } + $0 ~ "^[[:space:]]*" key "=" { + if (!updated) { + print key "=" value + updated = 1 + } + next + } + { print } + END { + if (!updated) { + print key "=" value + } + } + ' "$ENV_FILE" > "$tmp" + mv "$tmp" "$ENV_FILE" +} + +get_env_var_from_file() { + local file="$1" + local key="$2" + grep -E "^[[:space:]]*${key}=" "$file" | head -n 1 | cut -d '=' -f 2- || true +} + +set_env_var_in_file() { + local file="$1" + local key="$2" + local value="$3" + local tmp + + tmp="$(mktemp)" + awk -v key="$key" -v value="$value" ' + BEGIN { updated = 0 } + $0 ~ "^[[:space:]]*" key "=" { + if (!updated) { + print key "=" value + updated = 1 + } + next + } + { print } + END { + if (!updated) { + print key "=" value + } + } + ' "$file" > "$tmp" + mv "$tmp" "$file" +} + +ask_country_currency() { + local country="$1" + local country_env_file="$2" + local current current_country default_currency input + + current="$(get_env_var_from_file "$country_env_file" "COUNTRY_CURRENCY")" + current_country="$(get_env_var_from_file "$country_env_file" "COUNTRY_CODE")" + if [[ "$current_country" == "$country" && -n "$current" ]]; then + default_currency="$current" + else + default_currency="$(default_currency_for_country "$country")" + fi + + while true; do + read -r -p "Moneda para ${country} (ISO 4217, ejemplo: PEN, MXN, COP) [default: ${default_currency}]: " input + input="${input:-$default_currency}" + input="$(echo "$input" | tr '[:lower:]' '[:upper:]' | tr -d ' ')" + + if validate_currency "$input"; then + printf '%s' "$input" + return + fi + + echo "La moneda debe tener exactamente 3 letras (ejemplo: PEN)." >&2 + done +} + +append_country_to_supported() { + local country="$1" + local current + current="$(get_env_var "SUPPORTED_COUNTRIES")" + current="${current:-pe,mx}" + current="$(echo "$current" | tr '[:upper:]' '[:lower:]' | tr -d ' ' | sed -E 's/,+/,/g; s/^,+//; s/,+$//')" + + if echo ",$current," | grep -q ",$country,"; then + log "${country} ya estรก en SUPPORTED_COUNTRIES (${current})." + return + fi + + local updated + if [[ -z "$current" ]]; then + updated="$country" + else + updated="${current},${country}" + fi + + set_env_var "SUPPORTED_COUNTRIES" "$updated" + log "Se actualizรณ SUPPORTED_COUNTRIES=${updated}." +} + +prepare_country_env_file() { + local country="$1" + local currency="$2" + local country_env_file=".env.${country}" + + if [[ ! -f "$country_env_file" ]]; then + if [[ -f "$COUNTRY_TEMPLATE" ]]; then + cp "$COUNTRY_TEMPLATE" "$country_env_file" + else + printf 'COUNTRY_CODE=%s\n' "$country" > "$country_env_file" + fi + log "Se creรณ ${country_env_file}." + fi + + set_env_var_in_file "$country_env_file" "COUNTRY_CODE" "$country" + set_env_var_in_file "$country_env_file" "COUNTRY_CURRENCY" "$currency" + + log "Se configurรณ COUNTRY_CODE=${country} y COUNTRY_CURRENCY=${currency} en ${country_env_file}." +} + +ensure_docker_available() { + if ! command -v docker >/dev/null 2>&1; then + error "docker no estรก disponible en PATH." + exit 1 + fi + + if ! docker compose version >/dev/null 2>&1; then + error "docker compose no estรก disponible." + exit 1 + fi +} + +ensure_topics_for_country() { + local country="$1" + + if [[ ! -f "$CORE_COMPOSE_FILE" ]]; then + error "No existe ${CORE_COMPOSE_FILE}. No puedo inicializar topics." + exit 1 + fi + + log "Inicializando topics Kafka para ${country} (idempotente)..." + + if ! docker ps --format '{{.Names}}' | grep -Fxq "yape-kafka"; then + if ask_yes_no "Kafka no estรก corriendo. ยฟLevantar servicio kafka del core ahora?" "y"; then + docker compose --env-file "$ENV_FILE" --profile core up -d kafka + else + error "No se puede inicializar topics sin Kafka activo." + exit 1 + fi + fi + + SUPPORTED_COUNTRIES="$country" docker compose \ + --env-file "$ENV_FILE" \ + run --rm --no-deps topic-init +} + +start_country_services() { + local country="$1" + local project="yape-${country}" + local country_env_file=".env.${country}" + + if [[ ! -f "$COUNTRY_COMPOSE_FILE" ]]; then + error "No existe ${COUNTRY_COMPOSE_FILE}." + exit 1 + fi + + log "Levantando servicios aislados para ${country} en proyecto ${project}..." + docker compose -p "$project" \ + -f "$COUNTRY_COMPOSE_FILE" \ + --env-file "$ENV_FILE" \ + --env-file "$country_env_file" \ + --profile country \ + up -d --build +} + +main() { + ensure_docker_available + ensure_env_file + + local country_input="${1:-}" + if [[ -z "$country_input" ]]; then + read -r -p "Cรณdigo de paรญs (2 letras, ejemplo: pe, mx, co): " country_input + fi + + local country + country="$(normalize_country "$country_input")" + + if ! validate_country "$country"; then + error "Cรณdigo invรกlido: '${country_input}'. Usa exactamente 2 letras (ej: pe, mx, co)." + exit 1 + fi + + log "Preparando onboarding para paรญs: ${country}." + + append_country_to_supported "$country" + + local country_env_file=".env.${country}" + if [[ ! -f "$country_env_file" && -f "$COUNTRY_TEMPLATE" ]]; then + cp "$COUNTRY_TEMPLATE" "$country_env_file" + elif [[ ! -f "$country_env_file" ]]; then + printf 'COUNTRY_CODE=%s\n' "$country" > "$country_env_file" + fi + + local currency + currency="$(ask_country_currency "$country" "$country_env_file")" + prepare_country_env_file "$country" "$currency" + + if ask_yes_no "ยฟInicializar topics de Kafka para ${country} ahora?" "y"; then + ensure_topics_for_country "$country" + else + log "Se omitiรณ inicializaciรณn de topics." + fi + + if ask_yes_no "ยฟLevantar ahora ledger-consumer y status-saga para ${country}?" "y"; then + start_country_services "$country" + log "Paรญs ${country} agregado sin afectar paรญses existentes." + log "Comando usado: docker compose -p yape-${country} -f docker-compose.country.yml --env-file .env --env-file .env.${country} --profile country up -d --build" + else + log "Listo. Para levantar manualmente ejecuta:" + echo "docker compose -p yape-${country} -f docker-compose.country.yml --env-file .env --env-file .env.${country} --profile country up -d --build" + fi +} + +main "$@" diff --git a/scripts/cleanup-docker.sh b/scripts/cleanup-docker.sh new file mode 100755 index 00000000..dc78c5d9 --- /dev/null +++ b/scripts/cleanup-docker.sh @@ -0,0 +1,291 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +ENV_FILE=".env" +CORE_COMPOSE_FILE="docker-compose.yml" +COUNTRY_COMPOSE_FILE="docker-compose.country.yml" +CORE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-$(basename "$ROOT_DIR")}" + +CORE_SERVICES=( + "yape-postgres" + "yape-kafka" + "yape-kafka-ui" + "yape-topic-init" + "yape-payment-api" + "yape-outbox-relay" + "yape-fraud-consumer" + "yape-visual-demo" +) + +log() { + printf '[cleanup] %s\n' "$*" +} + +warn() { + printf '[cleanup][warn] %s\n' "$*" >&2 +} + +error() { + printf '[cleanup][error] %s\n' "$*" >&2 +} + +require_docker() { + if ! command -v docker >/dev/null 2>&1; then + error "docker no estรก disponible en PATH." + exit 1 + fi + + if ! docker compose version >/dev/null 2>&1; then + error "docker compose no estรก disponible." + exit 1 + fi +} + +project_has_resources() { + local project="$1" + + if docker ps -a --filter "label=com.docker.compose.project=${project}" --format '{{.ID}}' | grep -q '.'; then + return 0 + fi + + if docker volume ls --filter "label=com.docker.compose.project=${project}" --format '{{.Name}}' | grep -q '.'; then + return 0 + fi + + if docker network ls --filter "label=com.docker.compose.project=${project}" --format '{{.Name}}' | grep -q '.'; then + return 0 + fi + + return 1 +} + +normalize_countries() { + local raw="$1" + echo "$raw" | tr '[:upper:]' '[:lower:]' | tr -d ' ' | sed -E 's/,+/,/g; s/^,+//; s/,+$//' +} + +get_supported_countries() { + local countries="" + + if [[ -f "$ENV_FILE" ]]; then + countries="$(grep -E '^[[:space:]]*SUPPORTED_COUNTRIES=' "$ENV_FILE" | head -n 1 | cut -d '=' -f 2- || true)" + fi + + countries="$(normalize_countries "${countries:-}")" + + if [[ -z "$countries" ]]; then + countries="pe,mx" + fi + + printf '%s' "$countries" +} + +collect_country_codes() { + local supported_csv="$1" + local country + declare -A uniq=() + + IFS=',' read -ra from_env <<< "$supported_csv" + for country in "${from_env[@]}"; do + [[ -n "$country" ]] && uniq["$country"]=1 + done + + while IFS= read -r file; do + country="${file##*.env.}" + if [[ "$country" =~ ^[a-z]{2}$ ]]; then + uniq["$country"]=1 + fi + done < <(find "$ROOT_DIR" -maxdepth 1 -type f -name '.env.*' ! -name '.env.example' ! -name '.env.country.example' | sort) + + local out=() + for country in "${!uniq[@]}"; do + out+=("$country") + done + + if ((${#out[@]} > 0)); then + printf '%s\n' "${out[@]}" | sort + fi +} + +list_container_candidates() { + log "Contenedores de este proyecto detectados:" + + for c in "${CORE_SERVICES[@]}"; do + if docker ps -a --format '{{.Names}}' | grep -Fxq "$c"; then + echo " - $c" + fi + done + + local country + while IFS= read -r country; do + [[ -z "$country" ]] && continue + local project="yape-${country}" + + while IFS= read -r name; do + [[ -n "$name" ]] && echo " - $name" + done < <(docker ps -a --filter "label=com.docker.compose.project=${project}" --format '{{.Names}}') + done < <(collect_country_codes "$(get_supported_countries)") +} + +list_resource_candidates() { + log "Recursos potenciales del proyecto:" + + echo " Redes:" + while IFS= read -r network; do + [[ -n "$network" ]] && echo " - $network" + done < <(docker network ls --format '{{.Name}}' | grep -E '^(yape-shared|yape-[a-z]{2}_default|yape-challenge_default)$' || true) + + echo " Volรบmenes (labels compose):" + while IFS= read -r volume; do + [[ -n "$volume" ]] && echo " - $volume" + done < <(docker volume ls --filter 'label=com.docker.compose.project' --format '{{.Name}}' | grep -E '(yape|yape-challenge|yape-[a-z]{2})' || true) + + echo " Imรกgenes locales candidatas (built por compose):" + while IFS= read -r image; do + [[ -n "$image" ]] && echo " - $image" + done < <(docker images --format '{{.Repository}}:{{.Tag}}' | grep -E '(yape|yape-challenge)' || true) +} + +down_country_projects() { + local country + + while IFS= read -r country; do + [[ -z "$country" ]] && continue + local project="yape-${country}" + + if ! project_has_resources "$project"; then + log "Proyecto paรญs ${project} sin recursos activos. Se omite." + continue + fi + + log "Bajando proyecto paรญs ${project}..." + + if [[ -f "$ENV_FILE" ]]; then + COUNTRY_CODE="$country" docker compose -p "$project" \ + -f "$COUNTRY_COMPOSE_FILE" \ + --env-file "$ENV_FILE" \ + down -v --remove-orphans || true + else + COUNTRY_CODE="$country" docker compose -p "$project" \ + -f "$COUNTRY_COMPOSE_FILE" \ + down -v --remove-orphans || true + fi + done < <(collect_country_codes "$(get_supported_countries)") +} + +down_core_project() { + if [[ ! -f "$CORE_COMPOSE_FILE" ]]; then + warn "No se encontrรณ ${CORE_COMPOSE_FILE}; se omite limpieza core por compose." + return + fi + + if ! project_has_resources "$CORE_PROJECT_NAME"; then + log "Proyecto core ${CORE_PROJECT_NAME} sin recursos activos. Se omite." + return + fi + + log "Bajando stack core del proyecto..." + + if [[ -f "$ENV_FILE" ]]; then + docker compose -p "$CORE_PROJECT_NAME" -f "$CORE_COMPOSE_FILE" --env-file "$ENV_FILE" --profile all down -v --remove-orphans --rmi local || true + else + docker compose -p "$CORE_PROJECT_NAME" -f "$CORE_COMPOSE_FILE" --profile all down -v --remove-orphans --rmi local || true + fi +} + +remove_leftover_project_containers() { + log "Eliminando contenedores huรฉrfanos del proyecto (si existen)..." + + local ids="" + + ids="$(docker ps -a --filter "label=com.docker.compose.project=${CORE_PROJECT_NAME}" --format '{{.ID}}')" + if [[ -n "$ids" ]]; then + echo "$ids" | xargs docker rm -f >/dev/null 2>&1 || true + fi + + for c in "${CORE_SERVICES[@]}"; do + if docker ps -a --format '{{.Names}}' | grep -Fxq "$c"; then + docker rm -f "$c" >/dev/null 2>&1 || true + fi + done + + local country + while IFS= read -r country; do + [[ -z "$country" ]] && continue + local project="yape-${country}" + ids="$(docker ps -a --filter "label=com.docker.compose.project=${project}" --format '{{.ID}}')" + if [[ -n "$ids" ]]; then + echo "$ids" | xargs docker rm -f >/dev/null 2>&1 || true + fi + done < <(collect_country_codes "$(get_supported_countries)") +} + +remove_leftover_project_images() { + log "Eliminando imรกgenes locales del proyecto (si existen)..." + + local image + while IFS= read -r image; do + [[ -n "$image" ]] && docker image rm -f "$image" >/dev/null 2>&1 || true + done < <(docker images --format '{{.Repository}}:{{.Tag}}' | grep -E "^${CORE_PROJECT_NAME}-(payment-api|outbox-relay|fraud-consumer|visual-demo):" || true) + + local country + while IFS= read -r country; do + [[ -z "$country" ]] && continue + while IFS= read -r image; do + [[ -n "$image" ]] && docker image rm -f "$image" >/dev/null 2>&1 || true + done < <(docker images --format '{{.Repository}}:{{.Tag}}' | grep -E "^yape-${country}-(ledger-consumer|status-saga):" || true) + done < <(collect_country_codes "$(get_supported_countries)") +} + +remove_shared_network_if_unused() { + if ! docker network inspect yape-shared >/dev/null 2>&1; then + return + fi + + local attached + attached="$(docker network inspect yape-shared --format '{{len .Containers}}' 2>/dev/null || echo "0")" + + if [[ "$attached" == "0" ]]; then + log "Eliminando red yape-shared (sin contenedores conectados)..." + docker network rm yape-shared >/dev/null 2>&1 || true + else + warn "No se elimina yape-shared porque tiene ${attached} contenedor(es) conectado(s)." + fi +} + +main() { + require_docker + + echo "" + echo "Este script estรก a punto de borrar recursos Docker del proyecto local yape-challenge:" + echo "- Contenedores (core y por paรญs)" + echo "- Redes de compose del proyecto" + echo "- Volรบmenes de compose del proyecto" + echo "- Imรกgenes locales construidas por compose del proyecto" + echo "" + + list_container_candidates + list_resource_candidates + + echo "" + echo "Escribe CONFIRM para ejecutar el borrado." + read -r -p "> " confirm + + if [[ "$confirm" != "CONFIRM" ]]; then + log "Operaciรณn cancelada. No se eliminรณ nada." + exit 0 + fi + + down_country_projects + down_core_project + remove_leftover_project_containers + remove_leftover_project_images + remove_shared_network_if_unused + + log "Limpieza completada." +} + +main "$@" diff --git a/scripts/init-local.sh b/scripts/init-local.sh new file mode 100755 index 00000000..54a92ba3 --- /dev/null +++ b/scripts/init-local.sh @@ -0,0 +1,360 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +ENV_FILE=".env" +ENV_EXAMPLE=".env.example" +COUNTRY_TEMPLATE=".env.country.example" + +log() { + printf '[init] %s\n' "$*" >&2 +} + +error() { + printf '[init][error] %s\n' "$*" >&2 +} + +ask_yes_no() { + local question="$1" + local default_answer="$2" + + while true; do + local suffix="[y/N]" + if [[ "$default_answer" == "y" ]]; then + suffix="[Y/n]" + fi + + read -r -p "$question $suffix: " reply + reply="${reply:-$default_answer}" + + case "$reply" in + y|Y|yes|YES) + return 0 + ;; + n|N|no|NO) + return 1 + ;; + *) + echo "Please answer y or n." + ;; + esac + done +} + +normalize_countries() { + local raw="$1" + echo "$raw" | tr '[:upper:]' '[:lower:]' | tr -d ' ' | sed -E 's/,+/,/g; s/^,+//; s/,+$//' +} + +set_env_var() { + local file="$1" + local key="$2" + local value="$3" + local tmp + + tmp="$(mktemp)" + awk -v key="$key" -v value="$value" ' + BEGIN { updated = 0 } + $0 ~ "^[[:space:]]*" key "=" { + if (!updated) { + print key "=" value + updated = 1 + } + next + } + { print } + END { + if (!updated) { + print key "=" value + } + } + ' "$file" > "$tmp" + mv "$tmp" "$file" +} + +get_env_var() { + local file="$1" + local key="$2" + local value + + value="$(grep -E "^[[:space:]]*${key}=" "$file" | head -n 1 | cut -d '=' -f 2- || true)" + printf '%s' "$value" +} + +validate_countries() { + local countries_csv="$1" + [[ "$countries_csv" =~ ^[a-z]{2}(,[a-z]{2})*$ ]] +} + +default_currency_for_country() { + case "$1" in + pe) echo "PEN" ;; + mx) echo "MXN" ;; + co) echo "COP" ;; + cl) echo "CLP" ;; + ar) echo "ARS" ;; + br) echo "BRL" ;; + ec) echo "USD" ;; + *) echo "$(echo "$1" | tr '[:lower:]' '[:upper:]')" ;; + esac +} + +validate_currency() { + local currency="$1" + [[ "$currency" =~ ^[A-Z]{3}$ ]] +} + +ask_country_currency() { + local country="$1" + local file="$2" + local current current_country default_currency input + + current="$(get_env_var "$file" "COUNTRY_CURRENCY")" + current_country="$(get_env_var "$file" "COUNTRY_CODE")" + if [[ "$current_country" == "$country" && -n "$current" ]]; then + default_currency="$current" + else + default_currency="$(default_currency_for_country "$country")" + fi + + while true; do + read -r -p "Currency for ${country} (ISO 4217, example: PEN, MXN, COP) [default: ${default_currency}]: " input + input="${input:-$default_currency}" + input="$(echo "$input" | tr '[:lower:]' '[:upper:]' | tr -d ' ')" + + if validate_currency "$input"; then + printf '%s' "$input" + return + fi + + echo "Currency must be exactly 3 uppercase letters (example: PEN)." >&2 + done +} + +ensure_env_file() { + if [[ -f "$ENV_FILE" ]]; then + log "Found existing ${ENV_FILE}." + return + fi + + if [[ ! -f "$ENV_EXAMPLE" ]]; then + error "${ENV_EXAMPLE} not found. Cannot bootstrap ${ENV_FILE}." + exit 1 + fi + + cp "$ENV_EXAMPLE" "$ENV_FILE" + log "Created ${ENV_FILE} from ${ENV_EXAMPLE}." +} + +configure_db_password() { + local current_password + current_password="$(get_env_var "$ENV_FILE" "DB_PASSWORD")" + current_password="${current_password:-postgres}" + + if ask_yes_no "Use default DB password (${current_password})?" "y"; then + set_env_var "$ENV_FILE" "DB_PASSWORD" "$current_password" + return + fi + + local new_password + while true; do + read -r -s -p "Enter DB password: " new_password + echo + if [[ -n "$new_password" ]]; then + break + fi + echo "Password cannot be empty." + done + + set_env_var "$ENV_FILE" "DB_PASSWORD" "$new_password" + log "Updated DB_PASSWORD in ${ENV_FILE}." +} + +configure_countries() { + local current_countries input countries_csv + current_countries="$(get_env_var "$ENV_FILE" "SUPPORTED_COUNTRIES")" + current_countries="$(normalize_countries "${current_countries:-pe,mx}")" + + read -r -p "Add SUPPORTED_COUNTRIES (example: pe,mx,co) [default: ${current_countries}]: " input + input="${input:-$current_countries}" + countries_csv="$(normalize_countries "$input")" + + if [[ -z "$countries_csv" ]] || ! validate_countries "$countries_csv"; then + error "Invalid format for countries. Use two-letter codes separated by comma (example: pe,mx,co)." + exit 1 + fi + + set_env_var "$ENV_FILE" "SUPPORTED_COUNTRIES" "$countries_csv" + set_env_var "$ENV_FILE" "COUNTRY_NAMESPACE_ENABLED" "true" + set_env_var "$ENV_FILE" "DB_HOST" "yape-postgres" + set_env_var "$ENV_FILE" "KAFKA_BROKERS" "yape-kafka:9092" + + log "Configured SUPPORTED_COUNTRIES=${countries_csv} in ${ENV_FILE}." + printf '%s' "$countries_csv" +} + +create_country_env_files() { + local countries_csv="$1" + local country + IFS=',' read -ra countries <<< "$countries_csv" + + for country in "${countries[@]}"; do + local file=".env.${country}" + + if [[ -f "$file" ]]; then + if ! ask_yes_no "${file} already exists. Update COUNTRY_CODE and COUNTRY_CURRENCY?" "y"; then + log "Keeping existing ${file}." + continue + fi + else + if [[ -f "$COUNTRY_TEMPLATE" ]]; then + cp "$COUNTRY_TEMPLATE" "$file" + else + printf 'COUNTRY_CODE=%s\n' "$country" > "$file" + fi + fi + + local currency + currency="$(ask_country_currency "$country" "$file")" + + set_env_var "$file" "COUNTRY_CODE" "$country" + set_env_var "$file" "COUNTRY_CURRENCY" "$currency" + log "Prepared ${file} with COUNTRY_CODE=${country} and COUNTRY_CURRENCY=${currency}." + done +} + +ensure_docker_available() { + if ! command -v docker >/dev/null 2>&1; then + error "docker not found in PATH. Install Docker first." + exit 1 + fi + + if ! docker compose version >/dev/null 2>&1; then + error "docker compose not available. Please enable Docker Compose plugin." + exit 1 + fi +} + +start_stack() { + local countries_csv="$1" + local with_kafka_ui="$2" + local with_visual_demo="$3" + + log "Starting core stack..." + docker compose --env-file "$ENV_FILE" \ + --profile core \ + --profile init-topics \ + --profile payment-api \ + --profile outbox-relay \ + --profile fraud-consumer \ + up -d --build + + if [[ "$with_kafka_ui" == "yes" ]]; then + log "Starting Kafka UI..." + docker compose --env-file "$ENV_FILE" --profile kafka --profile kafka-ui up -d + fi + + if [[ "$with_visual_demo" == "yes" ]]; then + log "Starting Visual Demo..." + docker compose --env-file "$ENV_FILE" \ + --profile core \ + --profile init-topics \ + --profile payment-api \ + --profile visual-demo \ + up -d --build visual-demo + fi + + local country + IFS=',' read -ra countries <<< "$countries_csv" + for country in "${countries[@]}"; do + local country_env_file=".env.${country}" + log "Starting country workers for ${country}..." + docker compose -p "yape-${country}" \ + -f docker-compose.country.yml \ + --env-file "$ENV_FILE" \ + --env-file "$country_env_file" \ + --profile country \ + up -d --build + done + + log "Bootstrap complete." +} + +print_runtime_info() { + local countries_csv="$1" + local with_kafka_ui="$2" + local with_visual_demo="$3" + local api_port db_port + + api_port="$(get_env_var "$ENV_FILE" "PORT")" + api_port="${api_port:-3000}" + + db_port="$(get_env_var "$ENV_FILE" "DB_PORT")" + db_port="${db_port:-5432}" + + echo "" + echo "=== Runtime info ===" + echo "Payment API : http://localhost:${api_port}" + echo "Kafka broker: localhost:9092" + echo "Postgres : localhost:${db_port}" + + if [[ "$with_kafka_ui" == "yes" ]]; then + echo "Kafka UI : http://localhost:8080" + else + echo "Kafka UI : disabled (run: docker compose --env-file .env --profile kafka --profile kafka-ui up -d)" + fi + + if [[ "$with_visual_demo" == "yes" ]]; then + echo "Visual Demo : http://localhost:4000" + else + echo "Visual Demo : disabled (run: docker compose --env-file .env --profile core --profile init-topics --profile payment-api --profile visual-demo up -d --build visual-demo)" + fi + + echo "Country workers:" + IFS=',' read -ra countries <<< "$countries_csv" + for country in "${countries[@]}"; do + local currency + currency="$(get_env_var ".env.${country}" "COUNTRY_CURRENCY")" + currency="${currency:-$(default_currency_for_country "$country")}" + echo " - ${country}: project yape-${country} (currency ${currency}, ledger-consumer, status-saga)" + done +} + +main() { + log "Interactive bootstrap started." + ensure_docker_available + ensure_env_file + configure_db_password + + local countries_csv + countries_csv="$(configure_countries)" + create_country_env_files "$countries_csv" + + local kafka_ui_choice="no" + if ask_yes_no "Enable Kafka UI (http://localhost:8080)?" "y"; then + kafka_ui_choice="yes" + fi + + local visual_demo_choice="no" + if ask_yes_no "Enable Visual Demo (http://localhost:4000)?" "y"; then + visual_demo_choice="yes" + fi + + if ask_yes_no "Start Docker services now?" "y"; then + start_stack "$countries_csv" "$kafka_ui_choice" "$visual_demo_choice" + print_runtime_info "$countries_csv" "$kafka_ui_choice" "$visual_demo_choice" + else + log "Setup files prepared. You can start manually with:" + echo " docker compose --env-file .env --profile all up -d --build" + IFS=',' read -ra countries <<< "$countries_csv" + for country in "${countries[@]}"; do + echo " docker compose -p yape-${country} -f docker-compose.country.yml --env-file .env --env-file .env.${country} --profile country up -d --build" + done + fi + + log "Done." +} + +main "$@" diff --git a/scripts/init-topics.sh b/scripts/init-topics.sh new file mode 100755 index 00000000..210e7010 --- /dev/null +++ b/scripts/init-topics.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +BROKER="${KAFKA_BROKER:-kafka:9092}" +COUNTRIES_RAW="${SUPPORTED_COUNTRIES:-pe,mx}" +PARTITIONS="${TOPIC_PARTITIONS:-3}" +REPLICATION="${TOPIC_REPLICATION_FACTOR:-1}" + +BASE_TOPICS=( + "payment.created.v1" + "fraud.assessed.v1" + "ledger.posted.v1" + "payment.settled.v1" + "payment.failed.v1" +) + +read -r -a COUNTRIES <<< "$(echo "${COUNTRIES_RAW}" | tr ',' ' ')" + +echo "Initializing topics on broker ${BROKER} for countries: ${COUNTRIES[*]}" + +for country in "${COUNTRIES[@]}"; do + normalized_country="$(echo "${country}" | tr '[:upper:]' '[:lower:]' | xargs)" + + for base in "${BASE_TOPICS[@]}"; do + topic="${normalized_country}.payments.${base}" + dlt_topic="${topic}.dlt" + + /opt/kafka/bin/kafka-topics.sh \ + --bootstrap-server "${BROKER}" \ + --create \ + --if-not-exists \ + --topic "${topic}" \ + --partitions "${PARTITIONS}" \ + --replication-factor "${REPLICATION}" + + /opt/kafka/bin/kafka-topics.sh \ + --bootstrap-server "${BROKER}" \ + --create \ + --if-not-exists \ + --topic "${dlt_topic}" \ + --partitions "${PARTITIONS}" \ + --replication-factor "${REPLICATION}" + + echo "Ensured topics: ${topic} and ${dlt_topic}" + done +done + +echo "Topic initialization complete." diff --git a/scripts/run-kafka-scenario.sh b/scripts/run-kafka-scenario.sh new file mode 100755 index 00000000..019f92e1 --- /dev/null +++ b/scripts/run-kafka-scenario.sh @@ -0,0 +1,467 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +ENV_FILE=".env" +COUNTRY_COMPOSE_FILE="docker-compose.country.yml" + +SCENARIO="success" +COUNTRY="pe" +WALLET_ID="wallet-e2e-001" +AMOUNT="10.50" +CURRENCY="PEN" +API_URL="http://localhost:3000" +TIMEOUT_SECONDS=90 +FAILURE_HOLD_SECONDS=12 +WATCH_TIMEOUT_MS=120000 + +TMP_DIR="" +PAYMENT_ID="" +LEDGER_STOPPED=0 + +if [[ -t 1 ]]; then + C_RESET='\033[0m' + C_INFO='\033[1;34m' + C_OK='\033[1;32m' + C_WARN='\033[1;33m' + C_ERR='\033[1;31m' + C_DIM='\033[2m' +else + C_RESET='' + C_INFO='' + C_OK='' + C_WARN='' + C_ERR='' + C_DIM='' +fi + +log_info() { printf "%b[scenario] %s%b\n" "$C_INFO" "$*" "$C_RESET"; } +log_ok() { printf "%b[scenario] %s%b\n" "$C_OK" "$*" "$C_RESET"; } +log_warn() { printf "%b[scenario] %s%b\n" "$C_WARN" "$*" "$C_RESET"; } +log_err() { printf "%b[scenario] %s%b\n" "$C_ERR" "$*" "$C_RESET" >&2; } + +usage() { + cat < + --country (default: pe) + --wallet-id (default: wallet-e2e-001) + --amount (default: 10.50) + --currency (default: PEN) + --api-url (default: http://localhost:3000) + --timeout (default: 90) + --failure-hold (default: 12) + --watch-timeout-ms (default: 120000) + -h, --help + +Examples: + ./scripts/run-kafka-scenario.sh --scenario success --country pe + ./scripts/run-kafka-scenario.sh --scenario failure-stop-ledger --country pe +USAGE +} + +require_cmd() { + local cmd="$1" + if ! command -v "$cmd" >/dev/null 2>&1; then + log_err "Missing command: ${cmd}" + exit 1 + fi +} + +normalize_country() { + echo "$1" | tr '[:upper:]' '[:lower:]' | tr -d ' ' +} + +country_upper() { + echo "$1" | tr '[:lower:]' '[:upper:]' +} + +country_compose() { + docker compose -p "yape-${COUNTRY}" \ + -f "$COUNTRY_COMPOSE_FILE" \ + --env-file "$ENV_FILE" \ + --env-file ".env.${COUNTRY}" \ + "$@" +} + +json_get_field() { + local field="$1" + node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>{try{const v=JSON.parse(d)?.['${field}']; process.stdout.write(v===undefined||v===null?'':String(v));}catch{process.stdout.write('')}});" +} + +cleanup() { + if [[ "$LEDGER_STOPPED" == "1" ]]; then + log_warn "Restoring ledger-consumer for country '${COUNTRY}'..." + country_compose up -d ledger-consumer >/dev/null 2>&1 || true + fi + + if [[ -n "$TMP_DIR" && -d "$TMP_DIR" ]]; then + rm -rf "$TMP_DIR" + fi +} + +trap cleanup EXIT + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --scenario) + SCENARIO="${2:-}" + shift 2 + ;; + --country) + COUNTRY="${2:-}" + shift 2 + ;; + --wallet-id) + WALLET_ID="${2:-}" + shift 2 + ;; + --amount) + AMOUNT="${2:-}" + shift 2 + ;; + --currency) + CURRENCY="${2:-}" + shift 2 + ;; + --api-url) + API_URL="${2:-}" + shift 2 + ;; + --timeout) + TIMEOUT_SECONDS="${2:-}" + shift 2 + ;; + --failure-hold) + FAILURE_HOLD_SECONDS="${2:-}" + shift 2 + ;; + --watch-timeout-ms) + WATCH_TIMEOUT_MS="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_err "Unknown argument: $1" + usage + exit 1 + ;; + esac + done + + COUNTRY="$(normalize_country "$COUNTRY")" + + if [[ "$SCENARIO" == "failure" ]]; then + SCENARIO="failure-stop-ledger" + fi + + if [[ ! "$COUNTRY" =~ ^[a-z]{2}$ ]]; then + log_err "Invalid country '${COUNTRY}'. Use 2-letter code (pe, mx, co...)." + exit 1 + fi + + if [[ "$SCENARIO" != "success" && "$SCENARIO" != "failure-stop-ledger" ]]; then + log_err "Invalid scenario '${SCENARIO}'. Allowed: success, failure-stop-ledger" + exit 1 + fi + + if [[ ! "$TIMEOUT_SECONDS" =~ ^[0-9]+$ ]] || [[ "$TIMEOUT_SECONDS" -lt 10 ]]; then + log_err "--timeout must be an integer >= 10" + exit 1 + fi + + if [[ ! "$FAILURE_HOLD_SECONDS" =~ ^[0-9]+$ ]] || [[ "$FAILURE_HOLD_SECONDS" -lt 1 ]]; then + log_err "--failure-hold must be an integer >= 1" + exit 1 + fi +} + +ensure_files() { + if [[ ! -f "$ENV_FILE" ]]; then + log_err "Missing ${ENV_FILE}. Run ./scripts/init-local.sh first." + exit 1 + fi + + if [[ ! -f ".env.${COUNTRY}" ]]; then + log_err "Missing .env.${COUNTRY}. Create it (or run ./scripts/add-new-country.sh ${COUNTRY})." + exit 1 + fi + + if [[ ! -f "$COUNTRY_COMPOSE_FILE" ]]; then + log_err "Missing ${COUNTRY_COMPOSE_FILE}." + exit 1 + fi +} + +ensure_stack() { + log_info "Ensuring core services are up (kafka, api, relay, fraud)..." + docker compose --env-file "$ENV_FILE" \ + --profile core \ + --profile init-topics \ + --profile payment-api \ + --profile outbox-relay \ + --profile fraud-consumer \ + up -d --build postgres kafka topic-init payment-api outbox-relay fraud-consumer >/dev/null + + log_info "Ensuring country services are up for ${COUNTRY} (ledger, saga)..." + country_compose --profile country up -d --build ledger-consumer status-saga >/dev/null + + for c in yape-kafka yape-payment-api yape-outbox-relay yape-fraud-consumer; do + if ! docker ps --format '{{.Names}}' | grep -Fxq "$c"; then + log_err "Required container is not running: ${c}" + exit 1 + fi + done + + if ! country_compose ps --status running --services | grep -Fxq 'status-saga'; then + log_err "status-saga is not running for ${COUNTRY}." + exit 1 + fi + + if [[ "$SCENARIO" == "success" ]] && ! country_compose ps --status running --services | grep -Fxq 'ledger-consumer'; then + log_err "ledger-consumer is not running for ${COUNTRY}." + exit 1 + fi + + log_ok "Base stack is ready." +} + +stop_ledger_consumer() { + log_warn "Stopping ledger-consumer for country ${COUNTRY} to simulate failure..." + country_compose stop ledger-consumer >/dev/null + LEDGER_STOPPED=1 + + if country_compose ps --status running --services | grep -Fxq 'ledger-consumer'; then + log_err "ledger-consumer is still running after stop command." + exit 1 + fi + + log_ok "ledger-consumer stopped." +} + +start_ledger_consumer() { + log_info "Starting ledger-consumer again for country ${COUNTRY}..." + country_compose up -d ledger-consumer >/dev/null + LEDGER_STOPPED=0 + log_ok "ledger-consumer restored." +} + +topic_name() { + local suffix="$1" + printf '%s.payments.%s' "$COUNTRY" "$suffix" +} + +start_topic_watchers() { + TMP_DIR="$(mktemp -d)" + + local topics=( + "$(topic_name payment.created.v1)" + "$(topic_name fraud.assessed.v1)" + "$(topic_name ledger.posted.v1)" + "$(topic_name payment.settled.v1)" + "$(topic_name payment.failed.v1)" + "$(topic_name payment.created.v1.dlt)" + "$(topic_name fraud.assessed.v1.dlt)" + "$(topic_name ledger.posted.v1.dlt)" + ) + + log_info "Starting Kafka watchers (timeout ${WATCH_TIMEOUT_MS}ms each topic)..." + + for topic in "${topics[@]}"; do + local safe + safe="${topic//./_}" + + ( + docker exec yape-kafka /opt/kafka/bin/kafka-console-consumer.sh \ + --bootstrap-server localhost:9092 \ + --topic "$topic" \ + --property print.timestamp=true \ + --property print.key=true \ + --timeout-ms "$WATCH_TIMEOUT_MS" \ + >"${TMP_DIR}/${safe}.out" \ + 2>"${TMP_DIR}/${safe}.err" || true + ) & + + echo "$topic" >>"${TMP_DIR}/topics.list" + done + + sleep 1 + log_ok "Watchers ready." +} + +create_payment() { + local cc payload response + cc="$(country_upper "$COUNTRY")" + + payload="{\"walletId\":\"${WALLET_ID}\",\"countryCode\":\"${cc}\",\"amount\":${AMOUNT},\"currency\":\"${CURRENCY}\"}" + + log_info "Creating payment via API (${API_URL}/payments)..." + response="$(curl -sS -X POST "${API_URL}/payments" -H 'Content-Type: application/json' -d "$payload")" + + PAYMENT_ID="$(printf '%s' "$response" | json_get_field paymentId)" + local status + status="$(printf '%s' "$response" | json_get_field status)" + + if [[ -z "$PAYMENT_ID" ]]; then + log_err "Failed to create payment. Raw response: $response" + exit 1 + fi + + log_ok "Payment created: paymentId=${PAYMENT_ID}, status=${status:-unknown}" +} + +read_status() { + curl -sS "${API_URL}/payments/${PAYMENT_ID}/status" +} + +poll_status_until_final() { + local started now elapsed status status_json + started="$(date +%s)" + + while true; do + status_json="$(read_status || true)" + status="$(printf '%s' "$status_json" | json_get_field status)" + + if [[ -n "$status" ]]; then + printf "%b[status] %s%b\n" "$C_DIM" "$status_json" "$C_RESET" + else + log_warn "Status endpoint not ready yet." + fi + + if [[ "$status" == "settled" || "$status" == "failed" ]]; then + log_ok "Final status reached: ${status}" + return 0 + fi + + now="$(date +%s)" + elapsed=$((now - started)) + + if [[ "$elapsed" -ge "$TIMEOUT_SECONDS" ]]; then + log_warn "Timeout waiting final status after ${TIMEOUT_SECONDS}s." + return 1 + fi + + sleep 2 + done +} + +wait_watchers() { + # Wait slightly longer than poll loop to allow delayed consumers + local wait_seconds=$((TIMEOUT_SECONDS + FAILURE_HOLD_SECONDS + 10)) + local i + for ((i=0; i/dev/null 2>&1; then + break + fi + sleep 1 + done +} + +print_topic_results() { + log_info "Kafka events captured for paymentId=${PAYMENT_ID}:" + + while IFS= read -r topic; do + [[ -z "$topic" ]] && continue + local safe out err line + safe="${topic//./_}" + out="${TMP_DIR}/${safe}.out" + err="${TMP_DIR}/${safe}.err" + + if [[ -s "$out" ]]; then + line="$(grep -F "$PAYMENT_ID" "$out" | head -n 1 || true)" + + if [[ -n "$line" ]]; then + printf "%b โœ“ %s%b\n" "$C_OK" "$topic" "$C_RESET" + echo " $line" + else + printf "%b ~ %s%b\n" "$C_WARN" "$topic (messages seen, none matched paymentId)" "$C_RESET" + fi + else + printf "%b - %s%b\n" "$C_DIM" "$topic (no message in window)" "$C_RESET" + fi + + if [[ -s "$err" ]]; then + local filtered + filtered="$(grep -vE 'Processed a total of 0 messages|Reached end of topic|^$' "$err" || true)" + if [[ -n "$filtered" ]]; then + printf "%b note: %s%b\n" "$C_DIM" "$filtered" "$C_RESET" + fi + fi + done <"${TMP_DIR}/topics.list" +} + +run_success() { + start_topic_watchers + create_payment + poll_status_until_final || true + wait_watchers +} + +run_failure_stop_ledger() { + stop_ledger_consumer + start_topic_watchers + create_payment + + log_warn "Holding ledger down for ${FAILURE_HOLD_SECONDS}s..." + sleep "$FAILURE_HOLD_SECONDS" + + local snapshot + snapshot="$(read_status || true)" + if [[ -n "$snapshot" ]]; then + printf "%b[status-mid] %s%b\n" "$C_WARN" "$snapshot" "$C_RESET" + fi + + start_ledger_consumer + poll_status_until_final || true + wait_watchers +} + +print_summary() { + echo "" + echo "=== Scenario summary ===" + echo "Scenario : ${SCENARIO}" + echo "Country : ${COUNTRY}" + echo "Payment ID : ${PAYMENT_ID}" + echo "API : ${API_URL}" + echo "Kafka UI : http://localhost:8080" + echo "" + echo "Useful logs:" + echo " docker compose logs -f outbox-relay fraud-consumer payment-api" + echo " docker compose -p yape-${COUNTRY} -f docker-compose.country.yml --env-file .env --env-file .env.${COUNTRY} logs -f ledger-consumer status-saga" +} + +main() { + parse_args "$@" + + require_cmd docker + require_cmd curl + require_cmd node + + ensure_files + ensure_stack + + log_info "Running scenario '${SCENARIO}' for country '${COUNTRY}'..." + + case "$SCENARIO" in + success) + run_success + ;; + failure-stop-ledger) + run_failure_stop_ledger + ;; + esac + + print_topic_results + print_summary + log_ok "Done." +} + +main "$@" diff --git a/scripts/run-test.sh b/scripts/run-test.sh new file mode 100755 index 00000000..b10b7a22 --- /dev/null +++ b/scripts/run-test.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +DOCKER_IMAGE="${DOCKER_TEST_IMAGE:-node:20-alpine}" + +log() { + printf '[test-docker] %s\n' "$*" +} + +error() { + printf '[test-docker][error] %s\n' "$*" >&2 +} + +require_docker() { + if ! command -v docker >/dev/null 2>&1; then + error "docker no estรก disponible en PATH." + exit 1 + fi +} + +main() { + require_docker + + log "Running tests inside Docker image: ${DOCKER_IMAGE}" + docker run --rm -t \ + -v "${ROOT_DIR}:/app" \ + -w /app \ + "${DOCKER_IMAGE}" \ + sh -lc "npm ci && npm test" +} + +main "$@" diff --git a/src/apps/fraud-consumer/main.ts b/src/apps/fraud-consumer/main.ts new file mode 100644 index 00000000..97f28db4 --- /dev/null +++ b/src/apps/fraud-consumer/main.ts @@ -0,0 +1,36 @@ +import 'reflect-metadata'; +import { Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { NestFactory } from '@nestjs/core'; +import { EventTypes } from 'src/domain/events/event-types'; +import { FraudConsumerService } from 'src/domain/services/fraud-consumer.service'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; +import { KafkaConsumerRunner } from 'src/infrastructure/messaging/kafka-consumer-runner'; +import { FraudConsumerModule } from 'src/modules/consumers/fraud/fraud-consumer.module'; + +async function bootstrap(): Promise { + const logger = new Logger('FraudConsumerProcess'); + const app = await NestFactory.createApplicationContext(FraudConsumerModule); + const configService = app.get(ConfigService); + const runner = app.get(KafkaConsumerRunner); + const service = app.get(FraudConsumerService); + const topicResolver = app.get(TopicResolver); + + const countries = configService.getOrThrow('supportedCountries'); + const topics = Array.from( + new Set(countries.map((country) => topicResolver.resolve(EventTypes.PaymentCreatedV1, country))), + ); + const groupIdPrefix = configService.getOrThrow('kafkaGroupIdPrefix'); + const groupId = `${groupIdPrefix}.fraud-consumer.global`; + + await runner.run({ + consumerName: 'fraud-consumer', + groupId, + topics, + onMessage: (rawEvent) => service.handlePaymentCreated(rawEvent), + }); + + logger.log(`Fraud consumer (global) is running for countries: ${countries.join(', ')}`); +} + +void bootstrap(); diff --git a/src/apps/ledger-consumer/main.ts b/src/apps/ledger-consumer/main.ts new file mode 100644 index 00000000..576f33ac --- /dev/null +++ b/src/apps/ledger-consumer/main.ts @@ -0,0 +1,41 @@ +import 'reflect-metadata'; +import { Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { NestFactory } from '@nestjs/core'; +import { EventTypes } from 'src/domain/events/event-types'; +import { LedgerConsumerService } from 'src/domain/services/ledger-consumer.service'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; +import { KafkaConsumerRunner } from 'src/infrastructure/messaging/kafka-consumer-runner'; +import { LedgerConsumerModule } from 'src/modules/consumers/ledger/ledger-consumer.module'; + +async function bootstrap(): Promise { + const logger = new Logger('LedgerConsumerProcess'); + const app = await NestFactory.createApplicationContext(LedgerConsumerModule); + const configService = app.get(ConfigService); + const runner = app.get(KafkaConsumerRunner); + const service = app.get(LedgerConsumerService); + const topicResolver = app.get(TopicResolver); + const supportedCountries = configService.getOrThrow('supportedCountries'); + + const country = (process.env.CONSUMER_COUNTRY ?? '').toLowerCase(); + if (!country || !supportedCountries.includes(country)) { + throw new Error( + `CONSUMER_COUNTRY must be one of [${supportedCountries.join(', ')}] for ledger-consumer process.`, + ); + } + + const topic = topicResolver.resolve(EventTypes.PaymentCreatedV1, country); + const groupIdPrefix = configService.getOrThrow('kafkaGroupIdPrefix'); + const groupId = `${groupIdPrefix}.ledger-consumer.${country}`; + + await runner.run({ + consumerName: 'ledger-consumer', + groupId, + topics: [topic], + onMessage: (rawEvent) => service.handlePaymentCreated(rawEvent), + }); + + logger.log(`Ledger consumer is running for country ${country} on topic ${topic}`); +} + +void bootstrap(); diff --git a/src/apps/outbox-relay/main.ts b/src/apps/outbox-relay/main.ts new file mode 100644 index 00000000..62bffd28 --- /dev/null +++ b/src/apps/outbox-relay/main.ts @@ -0,0 +1,31 @@ +import 'reflect-metadata'; +import { Logger } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { ConfigService } from '@nestjs/config'; +import { OutboxRelayService } from 'src/domain/services/outbox-relay.service'; +import { OutboxRelayModule } from 'src/modules/outbox/outbox-relay.module'; + +const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); + +async function bootstrap(): Promise { + const logger = new Logger('OutboxRelayProcess'); + const app = await NestFactory.createApplicationContext(OutboxRelayModule); + const config = app.get(ConfigService); + const relay = app.get(OutboxRelayService); + + const pollMs = config.getOrThrow('relayPollMs'); + logger.log(`Outbox relay started with polling interval ${pollMs}ms`); + + while (true) { + try { + await relay.relayBatch(); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + const stack = error instanceof Error ? error.stack : undefined; + logger.error(`Outbox relay batch failed: ${message}`, stack); + } + await sleep(pollMs); + } +} + +void bootstrap(); diff --git a/src/apps/payment-api/main.ts b/src/apps/payment-api/main.ts new file mode 100644 index 00000000..290b3c9f --- /dev/null +++ b/src/apps/payment-api/main.ts @@ -0,0 +1,23 @@ +import 'reflect-metadata'; +import { ValidationPipe } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { ConfigService } from '@nestjs/config'; +import { PaymentApiModule } from 'src/modules/payment/payment-api.module'; + +async function bootstrap(): Promise { + const app = await NestFactory.create(PaymentApiModule); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + forbidUnknownValues: true, + }), + ); + + const config = app.get(ConfigService); + const port = config.getOrThrow('port'); + + await app.listen(port); +} + +void bootstrap(); diff --git a/src/apps/status-saga/main.ts b/src/apps/status-saga/main.ts new file mode 100644 index 00000000..776d34e9 --- /dev/null +++ b/src/apps/status-saga/main.ts @@ -0,0 +1,46 @@ +import 'reflect-metadata'; +import { Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { NestFactory } from '@nestjs/core'; +import { EventTypes } from 'src/domain/events/event-types'; +import { StatusSagaService } from 'src/domain/services/status-saga.service'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; +import { KafkaConsumerRunner } from 'src/infrastructure/messaging/kafka-consumer-runner'; +import { StatusSagaModule } from 'src/modules/status-saga/status-saga.module'; + +async function bootstrap(): Promise { + const logger = new Logger('StatusSagaProcess'); + const app = await NestFactory.createApplicationContext(StatusSagaModule); + const configService = app.get(ConfigService); + const runner = app.get(KafkaConsumerRunner); + const service = app.get(StatusSagaService); + const topicResolver = app.get(TopicResolver); + const supportedCountries = configService.getOrThrow('supportedCountries'); + + const country = (process.env.CONSUMER_COUNTRY ?? '').toLowerCase(); + if (!country || !supportedCountries.includes(country)) { + throw new Error(`CONSUMER_COUNTRY must be one of [${supportedCountries.join(', ')}] for status-saga process.`); + } + + const fraudTopic = topicResolver.resolve(EventTypes.FraudAssessedV1, country); + const ledgerTopic = topicResolver.resolve(EventTypes.LedgerPostedV1, country); + const groupIdPrefix = configService.getOrThrow('kafkaGroupIdPrefix'); + const groupId = `${groupIdPrefix}.status-saga.${country}`; + + await runner.run({ + consumerName: 'status-saga', + groupId, + topics: [fraudTopic, ledgerTopic], + onMessage: (rawEvent, sourceTopic) => { + if (sourceTopic === fraudTopic) { + return service.onFraudAssessed(rawEvent); + } + + return service.onLedgerPosted(rawEvent); + }, + }); + + logger.log(`Status saga is running for country ${country} on topics ${fraudTopic} and ${ledgerTopic}`); +} + +void bootstrap(); diff --git a/src/config/env.ts b/src/config/env.ts new file mode 100644 index 00000000..89b8f435 --- /dev/null +++ b/src/config/env.ts @@ -0,0 +1,41 @@ +export interface AppEnv { + nodeEnv: string; + port: number; + dbHost: string; + dbPort: number; + dbUser: string; + dbPassword: string; + dbName: string; + kafkaBrokers: string[]; + kafkaClientId: string; + kafkaGroupIdPrefix: string; + supportedCountries: string[]; + relayPollMs: number; + maxConsumerRetries: number; + countryNamespaceEnabled: boolean; +} + +const parseCountries = (raw: string): string[] => + raw + .split(',') + .map((country) => country.trim().toLowerCase()) + .filter((country) => country.length > 0); + +export function loadEnv(): AppEnv { + return { + nodeEnv: process.env.NODE_ENV ?? 'development', + port: Number(process.env.PORT ?? 3000), + dbHost: process.env.DB_HOST ?? '127.0.0.1', + dbPort: Number(process.env.DB_PORT ?? 5432), + dbUser: process.env.DB_USER ?? 'postgres', + dbPassword: process.env.DB_PASSWORD ?? 'postgres', + dbName: process.env.DB_NAME ?? 'yape', + kafkaBrokers: (process.env.KAFKA_BROKERS ?? '127.0.0.1:9092').split(','), + kafkaClientId: process.env.KAFKA_CLIENT_ID ?? 'payment-platform', + kafkaGroupIdPrefix: process.env.KAFKA_GROUP_PREFIX ?? 'challenge', + supportedCountries: parseCountries(process.env.SUPPORTED_COUNTRIES ?? 'pe,mx'), + relayPollMs: Number(process.env.RELAY_POLL_MS ?? 1000), + maxConsumerRetries: Number(process.env.MAX_CONSUMER_RETRIES ?? 3), + countryNamespaceEnabled: (process.env.COUNTRY_NAMESPACE_ENABLED ?? 'false') === 'true', + }; +} diff --git a/src/domain/entities/outbox-event.ts b/src/domain/entities/outbox-event.ts new file mode 100644 index 00000000..2b86c37f --- /dev/null +++ b/src/domain/entities/outbox-event.ts @@ -0,0 +1,22 @@ +export enum OutboxStatus { + Pending = 'pending', + Processing = 'processing', + Published = 'published', + Failed = 'failed', +} + +export interface OutboxEvent { + id: string; + aggregateId: string; + eventId: string; + eventType: string; + topic: string; + countryCode: string; + payload: string; + status: OutboxStatus; + attempts: number; + nextAttemptAt: Date; + publishedAt?: Date | null; + createdAt: Date; + updatedAt: Date; +} diff --git a/src/domain/entities/payment-step.ts b/src/domain/entities/payment-step.ts new file mode 100644 index 00000000..59860e98 --- /dev/null +++ b/src/domain/entities/payment-step.ts @@ -0,0 +1,13 @@ +export enum StepStatus { + Pending = 'pending', + Succeeded = 'succeeded', + Failed = 'failed', +} + +export interface PaymentStep { + paymentId: string; + fraudStatus: StepStatus; + ledgerStatus: StepStatus; + failureReason?: string | null; + updatedAt: Date; +} diff --git a/src/domain/entities/payment.ts b/src/domain/entities/payment.ts new file mode 100644 index 00000000..fa925049 --- /dev/null +++ b/src/domain/entities/payment.ts @@ -0,0 +1,16 @@ +export enum PaymentStatus { + Pending = 'pending', + Settled = 'settled', + Failed = 'failed', +} + +export interface Payment { + id: string; + walletId: string; + countryCode: string; + amount: number; + currency: string; + status: PaymentStatus; + createdAt: Date; + updatedAt: Date; +} diff --git a/src/domain/events/event-types.ts b/src/domain/events/event-types.ts new file mode 100644 index 00000000..49ed7bf2 --- /dev/null +++ b/src/domain/events/event-types.ts @@ -0,0 +1,51 @@ +export const EventTypes = { + PaymentCreatedV1: 'payment.created.v1', + FraudAssessedV1: 'fraud.assessed.v1', + LedgerPostedV1: 'ledger.posted.v1', + PaymentSettledV1: 'payment.settled.v1', + PaymentFailedV1: 'payment.failed.v1', +} as const; + +export type EventType = (typeof EventTypes)[keyof typeof EventTypes]; + +export interface DomainEvent { + eventId: string; + type: EventType; + aggregateId: string; + occurredAt: string; + countryCode: string; + payload: TPayload; + schemaVersion: 1; +} + +export interface PaymentCreatedPayload { + paymentId: string; + walletId: string; + amount: number; + currency: string; +} + +export interface FraudAssessedPayload { + paymentId: string; + approved: boolean; + reason?: string; +} + +export interface LedgerPostedPayload { + paymentId: string; + entryId: string; + success: boolean; + reason?: string; +} + +export interface PaymentSettledPayload { + paymentId: string; + settledAt: string; +} + +export interface PaymentFailedPayload { + paymentId: string; + failedAt: string; + reason: string; + sourceEventId: string; +} diff --git a/src/domain/repositories/outbox.repository.ts b/src/domain/repositories/outbox.repository.ts new file mode 100644 index 00000000..7c907bfe --- /dev/null +++ b/src/domain/repositories/outbox.repository.ts @@ -0,0 +1,20 @@ +import { OutboxEvent } from 'src/domain/entities/outbox-event'; + +export const OUTBOX_REPOSITORY = Symbol('OUTBOX_REPOSITORY'); + +export interface EnqueueOutboxEventParams { + id: string; + aggregateId: string; + eventId: string; + eventType: string; + topic: string; + countryCode: string; + payload: string; +} + +export interface OutboxRepository { + enqueue(params: EnqueueOutboxEventParams): Promise; + lockPendingBatch(limit: number, now: Date): Promise; + markPublished(outboxId: string, publishedAt: Date): Promise; + markForRetry(outboxId: string, nextAttemptAt: Date): Promise; +} diff --git a/src/domain/repositories/payment-step.repository.ts b/src/domain/repositories/payment-step.repository.ts new file mode 100644 index 00000000..af98520f --- /dev/null +++ b/src/domain/repositories/payment-step.repository.ts @@ -0,0 +1,10 @@ +import { PaymentStep, StepStatus } from 'src/domain/entities/payment-step'; + +export const PAYMENT_STEP_REPOSITORY = Symbol('PAYMENT_STEP_REPOSITORY'); + +export interface PaymentStepRepository { + init(paymentId: string): Promise; + markFraud(paymentId: string, status: StepStatus, reason?: string): Promise; + markLedger(paymentId: string, status: StepStatus, reason?: string): Promise; + getByPaymentId(paymentId: string): Promise; +} diff --git a/src/domain/repositories/payment.repository.ts b/src/domain/repositories/payment.repository.ts new file mode 100644 index 00000000..c3db24c6 --- /dev/null +++ b/src/domain/repositories/payment.repository.ts @@ -0,0 +1,17 @@ +import { Payment, PaymentStatus } from 'src/domain/entities/payment'; + +export interface CreatePaymentParams { + id: string; + walletId: string; + countryCode: string; + amount: number; + currency: string; +} + +export const PAYMENT_REPOSITORY = Symbol('PAYMENT_REPOSITORY'); + +export interface PaymentRepository { + createPending(params: CreatePaymentParams): Promise; + updateStatus(paymentId: string, status: PaymentStatus): Promise; + getById(paymentId: string): Promise; +} diff --git a/src/domain/repositories/ports.ts b/src/domain/repositories/ports.ts new file mode 100644 index 00000000..19d1b18c --- /dev/null +++ b/src/domain/repositories/ports.ts @@ -0,0 +1,15 @@ +export const EVENT_PUBLISHER = Symbol('EVENT_PUBLISHER'); +export const DLT_PUBLISHER = Symbol('DLT_PUBLISHER'); + +export interface EventPublisher { + publish(topic: string, payload: string, key?: string): Promise; +} + +export interface DeadLetterPublisher { + publishDeadLetter( + originalTopic: string, + payload: string, + reason: string, + sourceEventId: string, + ): Promise; +} diff --git a/src/domain/repositories/processed-event.repository.ts b/src/domain/repositories/processed-event.repository.ts new file mode 100644 index 00000000..9a9625a4 --- /dev/null +++ b/src/domain/repositories/processed-event.repository.ts @@ -0,0 +1,5 @@ +export const PROCESSED_EVENT_REPOSITORY = Symbol('PROCESSED_EVENT_REPOSITORY'); + +export interface ProcessedEventRepository { + tryMarkProcessed(consumerName: string, countryCode: string, eventId: string): Promise; +} diff --git a/src/domain/repositories/unit-of-work.ts b/src/domain/repositories/unit-of-work.ts new file mode 100644 index 00000000..8771342c --- /dev/null +++ b/src/domain/repositories/unit-of-work.ts @@ -0,0 +1,5 @@ +export const UNIT_OF_WORK = Symbol('UNIT_OF_WORK'); + +export interface UnitOfWork { + execute(work: () => Promise): Promise; +} diff --git a/src/domain/services/consumer-executor.service.ts b/src/domain/services/consumer-executor.service.ts new file mode 100644 index 00000000..32228373 --- /dev/null +++ b/src/domain/services/consumer-executor.service.ts @@ -0,0 +1,52 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { DLT_PUBLISHER, DeadLetterPublisher } from 'src/domain/repositories/ports'; + +export interface ConsumerExecutionContext { + consumerName: string; + sourceTopic: string; + eventId: string; + payload: string; +} + +@Injectable() +export class ConsumerExecutorService { + private readonly logger = new Logger(ConsumerExecutorService.name); + + constructor( + @Inject(DLT_PUBLISHER) + private readonly deadLetterPublisher: DeadLetterPublisher, + ) {} + + async executeWithRetry( + context: ConsumerExecutionContext, + maxRetries: number, + operation: () => Promise, + ): Promise { + let attempt = 0; + + while (attempt < maxRetries) { + try { + await operation(); + return; + } catch (error) { + attempt += 1; + + if (attempt >= maxRetries) { + const reason = error instanceof Error ? error.message : 'unknown_error'; + + await this.deadLetterPublisher.publishDeadLetter( + context.sourceTopic, + context.payload, + reason, + context.eventId, + ); + + this.logger.error( + `Consumer ${context.consumerName} exhausted retries for ${context.eventId}. Sent to DLT.`, + ); + return; + } + } + } + } +} diff --git a/src/domain/services/fraud-consumer.service.ts b/src/domain/services/fraud-consumer.service.ts new file mode 100644 index 00000000..fe507188 --- /dev/null +++ b/src/domain/services/fraud-consumer.service.ts @@ -0,0 +1,64 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { EventTypes, FraudAssessedPayload, PaymentCreatedPayload } from 'src/domain/events/event-types'; +import { EVENT_PUBLISHER, EventPublisher } from 'src/domain/repositories/ports'; +import { + PROCESSED_EVENT_REPOSITORY, + ProcessedEventRepository, +} from 'src/domain/repositories/processed-event.repository'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; + +interface PaymentCreatedEvent { + eventId: string; + aggregateId: string; + countryCode: string; + payload: PaymentCreatedPayload; +} + +@Injectable() +export class FraudConsumerService { + private readonly consumerName = 'fraud-consumer'; + + constructor( + @Inject(PROCESSED_EVENT_REPOSITORY) + private readonly processedEventRepository: ProcessedEventRepository, + @Inject(EVENT_PUBLISHER) + private readonly eventPublisher: EventPublisher, + private readonly topicResolver: TopicResolver, + ) {} + + async handlePaymentCreated(rawEvent: string): Promise { + const event = JSON.parse(rawEvent) as PaymentCreatedEvent; + + const firstTime = await this.processedEventRepository.tryMarkProcessed( + this.consumerName, + event.countryCode, + event.eventId, + ); + if (!firstTime) { + return; + } + + const approved = event.payload.amount <= 1000; + const result: FraudAssessedPayload = { + paymentId: event.payload.paymentId, + approved, + reason: approved ? undefined : 'risk_threshold_exceeded', + }; + + const outputTopic = this.topicResolver.resolve(EventTypes.FraudAssessedV1, event.countryCode); + + await this.eventPublisher.publish( + outputTopic, + JSON.stringify({ + eventId: `${event.eventId}.fraud`, + type: EventTypes.FraudAssessedV1, + aggregateId: event.aggregateId, + occurredAt: new Date().toISOString(), + countryCode: event.countryCode, + schemaVersion: 1, + payload: result, + }), + event.aggregateId, + ); + } +} diff --git a/src/domain/services/ledger-consumer.service.ts b/src/domain/services/ledger-consumer.service.ts new file mode 100644 index 00000000..1c1b49b2 --- /dev/null +++ b/src/domain/services/ledger-consumer.service.ts @@ -0,0 +1,64 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { v4 as uuidv4 } from 'uuid'; +import { EventTypes, LedgerPostedPayload, PaymentCreatedPayload } from 'src/domain/events/event-types'; +import { EVENT_PUBLISHER, EventPublisher } from 'src/domain/repositories/ports'; +import { + PROCESSED_EVENT_REPOSITORY, + ProcessedEventRepository, +} from 'src/domain/repositories/processed-event.repository'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; + +interface PaymentCreatedEvent { + eventId: string; + aggregateId: string; + countryCode: string; + payload: PaymentCreatedPayload; +} + +@Injectable() +export class LedgerConsumerService { + private readonly consumerName = 'ledger-consumer'; + + constructor( + @Inject(PROCESSED_EVENT_REPOSITORY) + private readonly processedEventRepository: ProcessedEventRepository, + @Inject(EVENT_PUBLISHER) + private readonly eventPublisher: EventPublisher, + private readonly topicResolver: TopicResolver, + ) {} + + async handlePaymentCreated(rawEvent: string): Promise { + const event = JSON.parse(rawEvent) as PaymentCreatedEvent; + + const firstTime = await this.processedEventRepository.tryMarkProcessed( + this.consumerName, + event.countryCode, + event.eventId, + ); + if (!firstTime) { + return; + } + + const result: LedgerPostedPayload = { + paymentId: event.payload.paymentId, + entryId: uuidv4(), + success: true, + }; + + const outputTopic = this.topicResolver.resolve(EventTypes.LedgerPostedV1, event.countryCode); + + await this.eventPublisher.publish( + outputTopic, + JSON.stringify({ + eventId: `${event.eventId}.ledger`, + type: EventTypes.LedgerPostedV1, + aggregateId: event.aggregateId, + occurredAt: new Date().toISOString(), + countryCode: event.countryCode, + schemaVersion: 1, + payload: result, + }), + event.aggregateId, + ); + } +} diff --git a/src/domain/services/outbox-relay.service.ts b/src/domain/services/outbox-relay.service.ts new file mode 100644 index 00000000..3c603939 --- /dev/null +++ b/src/domain/services/outbox-relay.service.ts @@ -0,0 +1,34 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { OUTBOX_REPOSITORY, OutboxRepository } from 'src/domain/repositories/outbox.repository'; +import { EVENT_PUBLISHER, EventPublisher } from 'src/domain/repositories/ports'; + +@Injectable() +export class OutboxRelayService { + private readonly logger = new Logger(OutboxRelayService.name); + + constructor( + @Inject(OUTBOX_REPOSITORY) + private readonly outboxRepository: OutboxRepository, + @Inject(EVENT_PUBLISHER) + private readonly eventPublisher: EventPublisher, + ) {} + + async relayBatch(batchSize = 50): Promise { + const pending = await this.outboxRepository.lockPendingBatch(batchSize, new Date()); + + for (const event of pending) { + try { + await this.eventPublisher.publish(event.topic, event.payload, event.aggregateId); + await this.outboxRepository.markPublished(event.id, new Date()); + } catch (error) { + const retryAt = new Date(Date.now() + Math.min((event.attempts + 1) * 1000, 30000)); + await this.outboxRepository.markForRetry(event.id, retryAt); + this.logger.warn( + `Failed to publish outbox event ${event.id}. It will be retried at ${retryAt.toISOString()}.`, + ); + } + } + + return pending.length; + } +} diff --git a/src/domain/services/payment.service.ts b/src/domain/services/payment.service.ts new file mode 100644 index 00000000..90904664 --- /dev/null +++ b/src/domain/services/payment.service.ts @@ -0,0 +1,85 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { v4 as uuidv4 } from 'uuid'; +import { PaymentStatus } from 'src/domain/entities/payment'; +import { EventTypes } from 'src/domain/events/event-types'; +import { OUTBOX_REPOSITORY, OutboxRepository } from 'src/domain/repositories/outbox.repository'; +import { PAYMENT_REPOSITORY, PaymentRepository } from 'src/domain/repositories/payment.repository'; +import { PAYMENT_STEP_REPOSITORY, PaymentStepRepository } from 'src/domain/repositories/payment-step.repository'; +import { UNIT_OF_WORK, UnitOfWork } from 'src/domain/repositories/unit-of-work'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; + +export interface CreatePaymentCommand { + walletId: string; + countryCode: string; + amount: number; + currency: string; +} + +export interface PaymentCreationResult { + paymentId: string; + status: PaymentStatus; +} + +@Injectable() +export class PaymentService { + constructor( + @Inject(UNIT_OF_WORK) + private readonly unitOfWork: UnitOfWork, + @Inject(PAYMENT_REPOSITORY) + private readonly paymentRepository: PaymentRepository, + @Inject(PAYMENT_STEP_REPOSITORY) + private readonly paymentStepRepository: PaymentStepRepository, + @Inject(OUTBOX_REPOSITORY) + private readonly outboxRepository: OutboxRepository, + private readonly topicResolver: TopicResolver, + ) {} + + async createPayment(command: CreatePaymentCommand): Promise { + const paymentId = uuidv4(); + const eventId = uuidv4(); + const outboxId = uuidv4(); + const occurredAt = new Date().toISOString(); + + await this.unitOfWork.execute(async () => { + await this.paymentRepository.createPending({ + id: paymentId, + walletId: command.walletId, + countryCode: command.countryCode, + amount: command.amount, + currency: command.currency, + }); + + await this.paymentStepRepository.init(paymentId); + + const topic = this.topicResolver.resolve(EventTypes.PaymentCreatedV1, command.countryCode); + + await this.outboxRepository.enqueue({ + id: outboxId, + aggregateId: paymentId, + eventId, + eventType: EventTypes.PaymentCreatedV1, + topic, + countryCode: command.countryCode, + payload: JSON.stringify({ + eventId, + type: EventTypes.PaymentCreatedV1, + aggregateId: paymentId, + occurredAt, + countryCode: command.countryCode, + schemaVersion: 1, + payload: { + paymentId, + walletId: command.walletId, + amount: command.amount, + currency: command.currency, + }, + }), + }); + }); + + return { + paymentId, + status: PaymentStatus.Pending, + }; + } +} diff --git a/src/domain/services/status-saga.service.ts b/src/domain/services/status-saga.service.ts new file mode 100644 index 00000000..1acd4d19 --- /dev/null +++ b/src/domain/services/status-saga.service.ts @@ -0,0 +1,140 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { PaymentStatus } from 'src/domain/entities/payment'; +import { StepStatus } from 'src/domain/entities/payment-step'; +import { + EventTypes, + FraudAssessedPayload, + LedgerPostedPayload, + PaymentFailedPayload, + PaymentSettledPayload, +} from 'src/domain/events/event-types'; +import { PAYMENT_REPOSITORY, PaymentRepository } from 'src/domain/repositories/payment.repository'; +import { PAYMENT_STEP_REPOSITORY, PaymentStepRepository } from 'src/domain/repositories/payment-step.repository'; +import { EVENT_PUBLISHER, EventPublisher } from 'src/domain/repositories/ports'; +import { + PROCESSED_EVENT_REPOSITORY, + ProcessedEventRepository, +} from 'src/domain/repositories/processed-event.repository'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; + +interface IntegrationEvent { + eventId: string; + aggregateId: string; + countryCode: string; + payload: TPayload; +} + +@Injectable() +export class StatusSagaService { + private readonly consumerName = 'status-saga'; + + constructor( + @Inject(PROCESSED_EVENT_REPOSITORY) + private readonly processedEventRepository: ProcessedEventRepository, + @Inject(PAYMENT_REPOSITORY) + private readonly paymentRepository: PaymentRepository, + @Inject(PAYMENT_STEP_REPOSITORY) + private readonly paymentStepRepository: PaymentStepRepository, + @Inject(EVENT_PUBLISHER) + private readonly eventPublisher: EventPublisher, + private readonly topicResolver: TopicResolver, + ) {} + + async onFraudAssessed(rawEvent: string): Promise { + const event = JSON.parse(rawEvent) as IntegrationEvent; + + const firstTime = await this.processedEventRepository.tryMarkProcessed( + this.consumerName, + event.countryCode, + event.eventId, + ); + if (!firstTime) { + return; + } + + await this.paymentStepRepository.markFraud( + event.payload.paymentId, + event.payload.approved ? StepStatus.Succeeded : StepStatus.Failed, + event.payload.reason, + ); + + await this.reconcile(event.aggregateId, event.countryCode, event.eventId); + } + + async onLedgerPosted(rawEvent: string): Promise { + const event = JSON.parse(rawEvent) as IntegrationEvent; + + const firstTime = await this.processedEventRepository.tryMarkProcessed( + this.consumerName, + event.countryCode, + event.eventId, + ); + if (!firstTime) { + return; + } + + await this.paymentStepRepository.markLedger( + event.payload.paymentId, + event.payload.success ? StepStatus.Succeeded : StepStatus.Failed, + event.payload.reason, + ); + + await this.reconcile(event.aggregateId, event.countryCode, event.eventId); + } + + private async reconcile(paymentId: string, countryCode: string, sourceEventId: string): Promise { + const step = await this.paymentStepRepository.getByPaymentId(paymentId); + if (!step) { + return; + } + + if (step.fraudStatus === StepStatus.Failed || step.ledgerStatus === StepStatus.Failed) { + await this.paymentRepository.updateStatus(paymentId, PaymentStatus.Failed); + + const payload: PaymentFailedPayload = { + paymentId, + failedAt: new Date().toISOString(), + reason: step.failureReason ?? 'consumer_failure', + sourceEventId, + }; + + await this.eventPublisher.publish( + this.topicResolver.resolve(EventTypes.PaymentFailedV1, countryCode), + JSON.stringify({ + eventId: `${sourceEventId}.payment_failed`, + type: EventTypes.PaymentFailedV1, + aggregateId: paymentId, + occurredAt: new Date().toISOString(), + countryCode, + schemaVersion: 1, + payload, + }), + paymentId, + ); + return; + } + + if (step.fraudStatus === StepStatus.Succeeded && step.ledgerStatus === StepStatus.Succeeded) { + await this.paymentRepository.updateStatus(paymentId, PaymentStatus.Settled); + + const payload: PaymentSettledPayload = { + paymentId, + settledAt: new Date().toISOString(), + }; + + await this.eventPublisher.publish( + this.topicResolver.resolve(EventTypes.PaymentSettledV1, countryCode), + JSON.stringify({ + eventId: `${sourceEventId}.payment_settled`, + type: EventTypes.PaymentSettledV1, + aggregateId: paymentId, + occurredAt: new Date().toISOString(), + countryCode, + schemaVersion: 1, + payload, + }), + paymentId, + ); + } + } +} diff --git a/src/domain/services/topic-resolver.ts b/src/domain/services/topic-resolver.ts new file mode 100644 index 00000000..b9e72ada --- /dev/null +++ b/src/domain/services/topic-resolver.ts @@ -0,0 +1,11 @@ +export class TopicResolver { + constructor(private readonly perCountryNamespaceEnabled = false) {} + + resolve(baseTopic: string, countryCode: string): string { + if (!this.perCountryNamespaceEnabled) { + return baseTopic; + } + + return `${countryCode.toLowerCase()}.payments.${baseTopic}`; + } +} diff --git a/src/infrastructure/messaging/kafka-consumer-runner.ts b/src/infrastructure/messaging/kafka-consumer-runner.ts new file mode 100644 index 00000000..898f0ed8 --- /dev/null +++ b/src/infrastructure/messaging/kafka-consumer-runner.ts @@ -0,0 +1,66 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Consumer, Kafka } from 'kafkajs'; +import { ConsumerExecutorService } from 'src/domain/services/consumer-executor.service'; + +@Injectable() +export class KafkaConsumerRunner { + private readonly logger = new Logger(KafkaConsumerRunner.name); + private readonly kafka: Kafka; + + constructor( + private readonly configService: ConfigService, + private readonly consumerExecutorService: ConsumerExecutorService, + ) { + this.kafka = new Kafka({ + clientId: this.configService.getOrThrow('kafkaClientId'), + brokers: this.configService.getOrThrow('kafkaBrokers'), + }); + } + + async run(params: { + consumerName: string; + topics: string[]; + groupId: string; + onMessage: (rawEvent: string, sourceTopic: string) => Promise; + }): Promise { + const consumer = this.kafka.consumer({ groupId: params.groupId }); + await consumer.connect(); + for (const topic of params.topics) { + await consumer.subscribe({ topic, fromBeginning: true }); + } + + await consumer.run({ + eachMessage: async ({ topic, message }) => { + const rawEvent = (message.value ?? Buffer.from('')).toString('utf-8'); + if (!rawEvent) { + return; + } + + let eventId = 'unknown'; + try { + const parsed = JSON.parse(rawEvent) as { eventId?: string }; + eventId = parsed.eventId ?? eventId; + } catch { + eventId = 'malformed_event'; + } + + await this.consumerExecutorService.executeWithRetry( + { + consumerName: params.consumerName, + sourceTopic: topic, + eventId, + payload: rawEvent, + }, + this.configService.getOrThrow('maxConsumerRetries'), + () => params.onMessage(rawEvent, topic), + ); + }, + }); + + this.logger.log( + `Consumer ${params.consumerName} (${params.groupId}) subscribed to topics: ${params.topics.join(', ')}`, + ); + return consumer; + } +} diff --git a/src/infrastructure/messaging/kafka-publisher.ts b/src/infrastructure/messaging/kafka-publisher.ts new file mode 100644 index 00000000..2049ce57 --- /dev/null +++ b/src/infrastructure/messaging/kafka-publisher.ts @@ -0,0 +1,75 @@ +import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Kafka, Producer } from 'kafkajs'; +import { DeadLetterPublisher, EventPublisher } from 'src/domain/repositories/ports'; + +@Injectable() +export class KafkaPublisher implements EventPublisher, DeadLetterPublisher, OnModuleInit, OnModuleDestroy { + private readonly logger = new Logger(KafkaPublisher.name); + private producer!: Producer; + private kafka!: Kafka; + + constructor(private readonly configService: ConfigService) {} + + async onModuleInit(): Promise { + this.kafka = new Kafka({ + clientId: this.configService.getOrThrow('kafkaClientId'), + brokers: this.configService.getOrThrow('kafkaBrokers'), + }); + + this.producer = this.kafka.producer(); + await this.producer.connect(); + } + + async onModuleDestroy(): Promise { + if (this.producer) { + await this.producer.disconnect(); + } + } + + async publish(topic: string, payload: string, key?: string): Promise { + await this.producer.send({ + topic, + messages: [ + { + key, + value: payload, + }, + ], + }); + } + + async publishDeadLetter( + originalTopic: string, + payload: string, + reason: string, + sourceEventId: string, + ): Promise { + const dltTopic = `${originalTopic}.dlt`; + let parsedPayload: unknown; + + try { + parsedPayload = JSON.parse(payload); + } catch { + parsedPayload = { rawPayload: payload }; + } + + await this.producer.send({ + topic: dltTopic, + messages: [ + { + key: sourceEventId, + value: JSON.stringify({ + sourceTopic: originalTopic, + sourceEventId, + reason, + payload: parsedPayload, + occurredAt: new Date().toISOString(), + }), + }, + ], + }); + + this.logger.warn(`Message ${sourceEventId} moved to DLT ${dltTopic} because: ${reason}`); + } +} diff --git a/src/infrastructure/persistence/entities/outbox-event.orm-entity.ts b/src/infrastructure/persistence/entities/outbox-event.orm-entity.ts new file mode 100644 index 00000000..3d6514f5 --- /dev/null +++ b/src/infrastructure/persistence/entities/outbox-event.orm-entity.ts @@ -0,0 +1,44 @@ +import { Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { OutboxStatus } from 'src/domain/entities/outbox-event'; + +@Entity({ name: 'outbox_events' }) +export class OutboxEventOrmEntity { + @PrimaryColumn({ type: 'uuid' }) + id!: string; + + @Column({ type: 'uuid' }) + aggregateId!: string; + + @Column({ type: 'uuid', unique: true }) + eventId!: string; + + @Column({ type: 'varchar', length: 128 }) + eventType!: string; + + @Column({ type: 'varchar', length: 255 }) + topic!: string; + + @Column({ type: 'char', length: 2 }) + countryCode!: string; + + @Column({ type: 'text' }) + payload!: string; + + @Column({ type: 'varchar', length: 16 }) + status!: OutboxStatus; + + @Column({ type: 'int', default: 0 }) + attempts!: number; + + @Column({ type: 'timestamptz' }) + nextAttemptAt!: Date; + + @Column({ type: 'timestamptz', nullable: true }) + publishedAt!: Date | null; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt!: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt!: Date; +} diff --git a/src/infrastructure/persistence/entities/payment-step.orm-entity.ts b/src/infrastructure/persistence/entities/payment-step.orm-entity.ts new file mode 100644 index 00000000..363ae940 --- /dev/null +++ b/src/infrastructure/persistence/entities/payment-step.orm-entity.ts @@ -0,0 +1,24 @@ +import { Column, Entity, OneToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { StepStatus } from 'src/domain/entities/payment-step'; +import { PaymentOrmEntity } from 'src/infrastructure/persistence/entities/payment.orm-entity'; + +@Entity({ name: 'payment_steps' }) +export class PaymentStepOrmEntity { + @PrimaryColumn({ type: 'uuid' }) + paymentId!: string; + + @OneToOne(() => PaymentOrmEntity) + payment?: PaymentOrmEntity; + + @Column({ type: 'varchar', length: 16 }) + fraudStatus!: StepStatus; + + @Column({ type: 'varchar', length: 16 }) + ledgerStatus!: StepStatus; + + @Column({ type: 'varchar', length: 255, nullable: true }) + failureReason!: string | null; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt!: Date; +} diff --git a/src/infrastructure/persistence/entities/payment.orm-entity.ts b/src/infrastructure/persistence/entities/payment.orm-entity.ts new file mode 100644 index 00000000..e1218b81 --- /dev/null +++ b/src/infrastructure/persistence/entities/payment.orm-entity.ts @@ -0,0 +1,29 @@ +import { Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'; +import { PaymentStatus } from 'src/domain/entities/payment'; + +@Entity({ name: 'payments' }) +export class PaymentOrmEntity { + @PrimaryColumn({ type: 'uuid' }) + id!: string; + + @Column({ type: 'varchar', length: 64 }) + walletId!: string; + + @Column({ type: 'char', length: 2 }) + countryCode!: string; + + @Column({ type: 'numeric', precision: 14, scale: 2 }) + amount!: string; + + @Column({ type: 'varchar', length: 3 }) + currency!: string; + + @Column({ type: 'varchar', length: 16 }) + status!: PaymentStatus; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt!: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt!: Date; +} diff --git a/src/infrastructure/persistence/entities/processed-event.orm-entity.ts b/src/infrastructure/persistence/entities/processed-event.orm-entity.ts new file mode 100644 index 00000000..79db8753 --- /dev/null +++ b/src/infrastructure/persistence/entities/processed-event.orm-entity.ts @@ -0,0 +1,20 @@ +import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity({ name: 'processed_events' }) +@Index('uniq_consumer_country_event', ['consumerName', 'countryCode', 'eventId'], { unique: true }) +export class ProcessedEventOrmEntity { + @PrimaryGeneratedColumn('uuid') + id!: string; + + @Column({ type: 'varchar', length: 128 }) + consumerName!: string; + + @Column({ type: 'char', length: 2 }) + countryCode!: string; + + @Column({ type: 'varchar', length: 128 }) + eventId!: string; + + @CreateDateColumn({ type: 'timestamptz' }) + processedAt!: Date; +} diff --git a/src/infrastructure/persistence/repositories/outbox.typeorm-repository.ts b/src/infrastructure/persistence/repositories/outbox.typeorm-repository.ts new file mode 100644 index 00000000..f9267fc9 --- /dev/null +++ b/src/infrastructure/persistence/repositories/outbox.typeorm-repository.ts @@ -0,0 +1,112 @@ +import { Injectable } from '@nestjs/common'; +import { Brackets, DataSource } from 'typeorm'; +import { OutboxStatus } from 'src/domain/entities/outbox-event'; +import { EnqueueOutboxEventParams, OutboxRepository } from 'src/domain/repositories/outbox.repository'; +import { OutboxEventOrmEntity } from 'src/infrastructure/persistence/entities/outbox-event.orm-entity'; +import { TransactionContext } from 'src/infrastructure/persistence/transaction-context'; + +@Injectable() +export class TypeOrmOutboxRepository implements OutboxRepository { + private static readonly CLAIM_TIMEOUT_MS = 60_000; + + constructor( + private readonly dataSource: DataSource, + private readonly transactionContext: TransactionContext, + ) {} + + async enqueue(params: EnqueueOutboxEventParams): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + const repository = manager.getRepository(OutboxEventOrmEntity); + await repository.save({ + id: params.id, + aggregateId: params.aggregateId, + eventId: params.eventId, + eventType: params.eventType, + topic: params.topic, + countryCode: params.countryCode, + payload: params.payload, + status: OutboxStatus.Pending, + attempts: 0, + nextAttemptAt: new Date(), + publishedAt: null, + }); + } + + async lockPendingBatch(limit: number, now: Date): Promise { + return this.dataSource.transaction(async (manager) => { + const repository = manager.getRepository(OutboxEventOrmEntity); + const staleProcessingBefore = new Date(now.getTime() - TypeOrmOutboxRepository.CLAIM_TIMEOUT_MS); + + const claimable = await repository + .createQueryBuilder('outbox') + .where( + new Brackets((qb) => { + qb.where('outbox.status = :pendingStatus AND outbox.nextAttemptAt <= :now', { + pendingStatus: OutboxStatus.Pending, + now, + }).orWhere('outbox.status = :processingStatus AND outbox.updatedAt <= :staleProcessingBefore', { + processingStatus: OutboxStatus.Processing, + staleProcessingBefore, + }); + }), + ) + .orderBy('outbox.createdAt', 'ASC') + .limit(limit) + .setLock('pessimistic_write') + .setOnLocked('skip_locked') + .getMany(); + + if (claimable.length === 0) { + return []; + } + + const claimableIds = claimable.map((event) => event.id); + + await repository + .createQueryBuilder() + .update(OutboxEventOrmEntity) + .set({ + status: OutboxStatus.Processing, + nextAttemptAt: now, + }) + .whereInIds(claimableIds) + .execute(); + + return claimable.map((event) => ({ + ...event, + status: OutboxStatus.Processing, + nextAttemptAt: now, + })); + }); + } + + async markPublished(outboxId: string, publishedAt: Date): Promise { + await this.dataSource + .getRepository(OutboxEventOrmEntity) + .createQueryBuilder() + .update(OutboxEventOrmEntity) + .set({ + status: OutboxStatus.Published, + attempts: () => 'attempts + 1', + publishedAt, + }) + .where('id = :outboxId', { outboxId }) + .andWhere('status = :processingStatus', { processingStatus: OutboxStatus.Processing }) + .execute(); + } + + async markForRetry(outboxId: string, nextAttemptAt: Date): Promise { + await this.dataSource + .getRepository(OutboxEventOrmEntity) + .createQueryBuilder() + .update(OutboxEventOrmEntity) + .set({ + status: OutboxStatus.Pending, + attempts: () => 'attempts + 1', + nextAttemptAt, + }) + .where('id = :outboxId', { outboxId }) + .andWhere('status = :processingStatus', { processingStatus: OutboxStatus.Processing }) + .execute(); + } +} diff --git a/src/infrastructure/persistence/repositories/payment-step.typeorm-repository.ts b/src/infrastructure/persistence/repositories/payment-step.typeorm-repository.ts new file mode 100644 index 00000000..ffdcbd95 --- /dev/null +++ b/src/infrastructure/persistence/repositories/payment-step.typeorm-repository.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import { PaymentStep, StepStatus } from 'src/domain/entities/payment-step'; +import { PaymentStepRepository } from 'src/domain/repositories/payment-step.repository'; +import { PaymentStepOrmEntity } from 'src/infrastructure/persistence/entities/payment-step.orm-entity'; +import { TransactionContext } from 'src/infrastructure/persistence/transaction-context'; + +@Injectable() +export class TypeOrmPaymentStepRepository implements PaymentStepRepository { + constructor( + private readonly dataSource: DataSource, + private readonly transactionContext: TransactionContext, + ) {} + + async init(paymentId: string): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + await manager.getRepository(PaymentStepOrmEntity).save({ + paymentId, + fraudStatus: StepStatus.Pending, + ledgerStatus: StepStatus.Pending, + failureReason: null, + }); + } + + async markFraud(paymentId: string, status: StepStatus, reason?: string): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + await manager.getRepository(PaymentStepOrmEntity).update( + { paymentId }, + { + fraudStatus: status, + failureReason: status === StepStatus.Failed ? reason ?? 'fraud_rejected' : null, + }, + ); + } + + async markLedger(paymentId: string, status: StepStatus, reason?: string): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + await manager.getRepository(PaymentStepOrmEntity).update( + { paymentId }, + { + ledgerStatus: status, + failureReason: status === StepStatus.Failed ? reason ?? 'ledger_failed' : null, + }, + ); + } + + async getByPaymentId(paymentId: string): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + const row = await manager.getRepository(PaymentStepOrmEntity).findOneBy({ paymentId }); + if (!row) { + return null; + } + + return { + paymentId: row.paymentId, + fraudStatus: row.fraudStatus, + ledgerStatus: row.ledgerStatus, + failureReason: row.failureReason, + updatedAt: row.updatedAt, + }; + } +} diff --git a/src/infrastructure/persistence/repositories/payment.typeorm-repository.ts b/src/infrastructure/persistence/repositories/payment.typeorm-repository.ts new file mode 100644 index 00000000..35770c16 --- /dev/null +++ b/src/infrastructure/persistence/repositories/payment.typeorm-repository.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import { Payment, PaymentStatus } from 'src/domain/entities/payment'; +import { CreatePaymentParams, PaymentRepository } from 'src/domain/repositories/payment.repository'; +import { PaymentOrmEntity } from 'src/infrastructure/persistence/entities/payment.orm-entity'; +import { TransactionContext } from 'src/infrastructure/persistence/transaction-context'; + +@Injectable() +export class TypeOrmPaymentRepository implements PaymentRepository { + constructor( + private readonly dataSource: DataSource, + private readonly transactionContext: TransactionContext, + ) {} + + async createPending(params: CreatePaymentParams): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + const repository = manager.getRepository(PaymentOrmEntity); + + const entity = repository.create({ + id: params.id, + walletId: params.walletId, + countryCode: params.countryCode, + amount: params.amount.toFixed(2), + currency: params.currency, + status: PaymentStatus.Pending, + }); + + const saved = await repository.save(entity); + return this.toDomain(saved); + } + + async updateStatus(paymentId: string, status: PaymentStatus): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + await manager.getRepository(PaymentOrmEntity).update({ id: paymentId }, { status }); + } + + async getById(paymentId: string): Promise { + const manager = this.transactionContext.getEntityManager() ?? this.dataSource.manager; + const row = await manager.getRepository(PaymentOrmEntity).findOneBy({ id: paymentId }); + if (!row) { + return null; + } + + return this.toDomain(row); + } + + private toDomain(row: PaymentOrmEntity): Payment { + return { + id: row.id, + walletId: row.walletId, + countryCode: row.countryCode, + amount: Number(row.amount), + currency: row.currency, + status: row.status, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + }; + } +} diff --git a/src/infrastructure/persistence/repositories/processed-event.typeorm-repository.ts b/src/infrastructure/persistence/repositories/processed-event.typeorm-repository.ts new file mode 100644 index 00000000..ea8c4901 --- /dev/null +++ b/src/infrastructure/persistence/repositories/processed-event.typeorm-repository.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import { ProcessedEventRepository } from 'src/domain/repositories/processed-event.repository'; +import { ProcessedEventOrmEntity } from 'src/infrastructure/persistence/entities/processed-event.orm-entity'; + +@Injectable() +export class TypeOrmProcessedEventRepository implements ProcessedEventRepository { + constructor(private readonly dataSource: DataSource) {} + + async tryMarkProcessed(consumerName: string, countryCode: string, eventId: string): Promise { + try { + await this.dataSource.getRepository(ProcessedEventOrmEntity).insert({ + consumerName, + countryCode, + eventId, + }); + return true; + } catch { + return false; + } + } +} diff --git a/src/infrastructure/persistence/transaction-context.ts b/src/infrastructure/persistence/transaction-context.ts new file mode 100644 index 00000000..3458d4ae --- /dev/null +++ b/src/infrastructure/persistence/transaction-context.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@nestjs/common'; +import { AsyncLocalStorage } from 'node:async_hooks'; +import { EntityManager } from 'typeorm'; + +@Injectable() +export class TransactionContext { + private readonly storage = new AsyncLocalStorage(); + + runInTransaction(manager: EntityManager, work: () => Promise): Promise { + return this.storage.run(manager, work); + } + + getEntityManager(): EntityManager | undefined { + return this.storage.getStore(); + } +} diff --git a/src/infrastructure/persistence/typeorm-unit-of-work.ts b/src/infrastructure/persistence/typeorm-unit-of-work.ts new file mode 100644 index 00000000..318d1910 --- /dev/null +++ b/src/infrastructure/persistence/typeorm-unit-of-work.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import { UnitOfWork } from 'src/domain/repositories/unit-of-work'; +import { TransactionContext } from 'src/infrastructure/persistence/transaction-context'; + +@Injectable() +export class TypeOrmUnitOfWork implements UnitOfWork { + constructor( + private readonly dataSource: DataSource, + private readonly transactionContext: TransactionContext, + ) {} + + async execute(work: () => Promise): Promise { + return this.dataSource.transaction((manager) => { + return this.transactionContext.runInTransaction(manager, work); + }); + } +} diff --git a/src/modules/common/core.module.ts b/src/modules/common/core.module.ts new file mode 100644 index 00000000..91106c2d --- /dev/null +++ b/src/modules/common/core.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { loadEnv } from 'src/config/env'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [loadEnv], + }), + ], + providers: [ + { + provide: TopicResolver, + inject: [ConfigService], + useFactory: (configService: ConfigService) => + new TopicResolver(configService.get('countryNamespaceEnabled', false)), + }, + ], + exports: [ConfigModule, TopicResolver], +}) +export class CoreModule {} diff --git a/src/modules/common/messaging.module.ts b/src/modules/common/messaging.module.ts new file mode 100644 index 00000000..4eea95c9 --- /dev/null +++ b/src/modules/common/messaging.module.ts @@ -0,0 +1,29 @@ +import { Module } from '@nestjs/common'; +import { EVENT_PUBLISHER, DLT_PUBLISHER } from 'src/domain/repositories/ports'; +import { ConsumerExecutorService } from 'src/domain/services/consumer-executor.service'; +import { KafkaConsumerRunner } from 'src/infrastructure/messaging/kafka-consumer-runner'; +import { KafkaPublisher } from 'src/infrastructure/messaging/kafka-publisher'; + +@Module({ + providers: [ + ConsumerExecutorService, + KafkaPublisher, + KafkaConsumerRunner, + { + provide: EVENT_PUBLISHER, + useExisting: KafkaPublisher, + }, + { + provide: DLT_PUBLISHER, + useExisting: KafkaPublisher, + }, + ], + exports: [ + ConsumerExecutorService, + KafkaPublisher, + KafkaConsumerRunner, + EVENT_PUBLISHER, + DLT_PUBLISHER, + ], +}) +export class MessagingModule {} diff --git a/src/modules/common/persistence.module.ts b/src/modules/common/persistence.module.ts new file mode 100644 index 00000000..0f20b9d7 --- /dev/null +++ b/src/modules/common/persistence.module.ts @@ -0,0 +1,76 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { OUTBOX_REPOSITORY } from 'src/domain/repositories/outbox.repository'; +import { PAYMENT_REPOSITORY } from 'src/domain/repositories/payment.repository'; +import { PAYMENT_STEP_REPOSITORY } from 'src/domain/repositories/payment-step.repository'; +import { PROCESSED_EVENT_REPOSITORY } from 'src/domain/repositories/processed-event.repository'; +import { UNIT_OF_WORK } from 'src/domain/repositories/unit-of-work'; +import { OutboxEventOrmEntity } from 'src/infrastructure/persistence/entities/outbox-event.orm-entity'; +import { PaymentOrmEntity } from 'src/infrastructure/persistence/entities/payment.orm-entity'; +import { PaymentStepOrmEntity } from 'src/infrastructure/persistence/entities/payment-step.orm-entity'; +import { ProcessedEventOrmEntity } from 'src/infrastructure/persistence/entities/processed-event.orm-entity'; +import { TypeOrmOutboxRepository } from 'src/infrastructure/persistence/repositories/outbox.typeorm-repository'; +import { TypeOrmPaymentRepository } from 'src/infrastructure/persistence/repositories/payment.typeorm-repository'; +import { TypeOrmPaymentStepRepository } from 'src/infrastructure/persistence/repositories/payment-step.typeorm-repository'; +import { TypeOrmProcessedEventRepository } from 'src/infrastructure/persistence/repositories/processed-event.typeorm-repository'; +import { TransactionContext } from 'src/infrastructure/persistence/transaction-context'; +import { TypeOrmUnitOfWork } from 'src/infrastructure/persistence/typeorm-unit-of-work'; + +@Module({ + imports: [ + ConfigModule, + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => ({ + type: 'postgres', + host: config.getOrThrow('dbHost'), + port: config.getOrThrow('dbPort'), + username: config.getOrThrow('dbUser'), + password: config.getOrThrow('dbPassword'), + database: config.getOrThrow('dbName'), + synchronize: true, + logging: false, + entities: [PaymentOrmEntity, PaymentStepOrmEntity, OutboxEventOrmEntity, ProcessedEventOrmEntity], + }), + }), + ], + providers: [ + TransactionContext, + TypeOrmUnitOfWork, + TypeOrmPaymentRepository, + TypeOrmPaymentStepRepository, + TypeOrmOutboxRepository, + TypeOrmProcessedEventRepository, + { + provide: UNIT_OF_WORK, + useExisting: TypeOrmUnitOfWork, + }, + { + provide: PAYMENT_REPOSITORY, + useExisting: TypeOrmPaymentRepository, + }, + { + provide: PAYMENT_STEP_REPOSITORY, + useExisting: TypeOrmPaymentStepRepository, + }, + { + provide: OUTBOX_REPOSITORY, + useExisting: TypeOrmOutboxRepository, + }, + { + provide: PROCESSED_EVENT_REPOSITORY, + useExisting: TypeOrmProcessedEventRepository, + }, + ], + exports: [ + UNIT_OF_WORK, + PAYMENT_REPOSITORY, + PAYMENT_STEP_REPOSITORY, + OUTBOX_REPOSITORY, + PROCESSED_EVENT_REPOSITORY, + TransactionContext, + ], +}) +export class PersistenceModule {} diff --git a/src/modules/consumers/fraud/fraud-consumer.module.ts b/src/modules/consumers/fraud/fraud-consumer.module.ts new file mode 100644 index 00000000..cf78fda1 --- /dev/null +++ b/src/modules/consumers/fraud/fraud-consumer.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { FraudConsumerService } from 'src/domain/services/fraud-consumer.service'; +import { CoreModule } from 'src/modules/common/core.module'; +import { MessagingModule } from 'src/modules/common/messaging.module'; +import { PersistenceModule } from 'src/modules/common/persistence.module'; + +@Module({ + imports: [CoreModule, PersistenceModule, MessagingModule], + providers: [FraudConsumerService], +}) +export class FraudConsumerModule {} diff --git a/src/modules/consumers/ledger/ledger-consumer.module.ts b/src/modules/consumers/ledger/ledger-consumer.module.ts new file mode 100644 index 00000000..081b7217 --- /dev/null +++ b/src/modules/consumers/ledger/ledger-consumer.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { LedgerConsumerService } from 'src/domain/services/ledger-consumer.service'; +import { CoreModule } from 'src/modules/common/core.module'; +import { MessagingModule } from 'src/modules/common/messaging.module'; +import { PersistenceModule } from 'src/modules/common/persistence.module'; + +@Module({ + imports: [CoreModule, PersistenceModule, MessagingModule], + providers: [LedgerConsumerService], +}) +export class LedgerConsumerModule {} diff --git a/src/modules/outbox/outbox-relay.module.ts b/src/modules/outbox/outbox-relay.module.ts new file mode 100644 index 00000000..dc865817 --- /dev/null +++ b/src/modules/outbox/outbox-relay.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { OutboxRelayService } from 'src/domain/services/outbox-relay.service'; +import { CoreModule } from 'src/modules/common/core.module'; +import { MessagingModule } from 'src/modules/common/messaging.module'; +import { PersistenceModule } from 'src/modules/common/persistence.module'; + +@Module({ + imports: [CoreModule, PersistenceModule, MessagingModule], + providers: [OutboxRelayService], + exports: [OutboxRelayService], +}) +export class OutboxRelayModule {} diff --git a/src/modules/payment/dto/create-payment.dto.ts b/src/modules/payment/dto/create-payment.dto.ts new file mode 100644 index 00000000..13eeeb90 --- /dev/null +++ b/src/modules/payment/dto/create-payment.dto.ts @@ -0,0 +1,18 @@ +import { IsISO31661Alpha2, IsNumber, IsPositive, IsString, Length } from 'class-validator'; + +export class CreatePaymentDto { + @IsString() + @Length(3, 64) + walletId!: string; + + @IsISO31661Alpha2() + countryCode!: string; + + @IsNumber({ maxDecimalPlaces: 2 }) + @IsPositive() + amount!: number; + + @IsString() + @Length(3, 3) + currency!: string; +} diff --git a/src/modules/payment/payment-api.module.ts b/src/modules/payment/payment-api.module.ts new file mode 100644 index 00000000..6f05e005 --- /dev/null +++ b/src/modules/payment/payment-api.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { PaymentService } from 'src/domain/services/payment.service'; +import { CoreModule } from 'src/modules/common/core.module'; +import { PersistenceModule } from 'src/modules/common/persistence.module'; +import { PaymentController } from 'src/modules/payment/payment.controller'; +import { PaymentStatusQuery } from 'src/modules/payment/payment-status.query'; + +@Module({ + imports: [CoreModule, PersistenceModule], + controllers: [PaymentController], + providers: [PaymentService, PaymentStatusQuery], +}) +export class PaymentApiModule {} diff --git a/src/modules/payment/payment-status.query.ts b/src/modules/payment/payment-status.query.ts new file mode 100644 index 00000000..2d655488 --- /dev/null +++ b/src/modules/payment/payment-status.query.ts @@ -0,0 +1,44 @@ +import { Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { PaymentRepository, PAYMENT_REPOSITORY } from 'src/domain/repositories/payment.repository'; +import { + PaymentStepRepository, + PAYMENT_STEP_REPOSITORY, +} from 'src/domain/repositories/payment-step.repository'; + +@Injectable() +export class PaymentStatusQuery { + constructor( + @Inject(PAYMENT_REPOSITORY) + private readonly paymentRepository: PaymentRepository, + @Inject(PAYMENT_STEP_REPOSITORY) + private readonly paymentStepRepository: PaymentStepRepository, + ) {} + + async getStatus(paymentId: string): Promise<{ + paymentId: string; + status: string; + fraudStep: string; + ledgerStep: string; + consistency: 'eventual'; + note: string; + lastUpdatedAt: string; + }> { + const payment = await this.paymentRepository.getById(paymentId); + if (!payment) { + throw new NotFoundException(`payment ${paymentId} not found`); + } + + const step = await this.paymentStepRepository.getByPaymentId(paymentId); + + return { + paymentId, + status: payment.status, + fraudStep: step?.fraudStatus ?? 'pending', + ledgerStep: step?.ledgerStatus ?? 'pending', + consistency: 'eventual', + note: + 'This endpoint is eventually consistent. A recently created payment can remain pending until fraud and ledger consumers acknowledge.', + lastUpdatedAt: payment.updatedAt.toISOString(), + }; + } +} diff --git a/src/modules/payment/payment.controller.ts b/src/modules/payment/payment.controller.ts new file mode 100644 index 00000000..e6c42629 --- /dev/null +++ b/src/modules/payment/payment.controller.ts @@ -0,0 +1,37 @@ +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { CreatePaymentCommand, PaymentService } from 'src/domain/services/payment.service'; +import { CreatePaymentDto } from 'src/modules/payment/dto/create-payment.dto'; +import { PaymentStatusQuery } from 'src/modules/payment/payment-status.query'; + +@Controller('payments') +export class PaymentController { + constructor( + private readonly paymentService: PaymentService, + private readonly paymentStatusQuery: PaymentStatusQuery, + ) {} + + @Post() + async createPayment(@Body() dto: CreatePaymentDto): Promise<{ paymentId: string; status: string }> { + const command: CreatePaymentCommand = { + walletId: dto.walletId, + countryCode: dto.countryCode, + amount: dto.amount, + currency: dto.currency, + }; + + return this.paymentService.createPayment(command); + } + + @Get(':paymentId/status') + async getStatus(@Param('paymentId') paymentId: string): Promise<{ + paymentId: string; + status: string; + fraudStep: string; + ledgerStep: string; + consistency: 'eventual'; + note: string; + lastUpdatedAt: string; + }> { + return this.paymentStatusQuery.getStatus(paymentId); + } +} diff --git a/src/modules/status-saga/status-saga.module.ts b/src/modules/status-saga/status-saga.module.ts new file mode 100644 index 00000000..91d82e5d --- /dev/null +++ b/src/modules/status-saga/status-saga.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { StatusSagaService } from 'src/domain/services/status-saga.service'; +import { CoreModule } from 'src/modules/common/core.module'; +import { MessagingModule } from 'src/modules/common/messaging.module'; +import { PersistenceModule } from 'src/modules/common/persistence.module'; + +@Module({ + imports: [CoreModule, PersistenceModule, MessagingModule], + providers: [StatusSagaService], +}) +export class StatusSagaModule {} diff --git a/test-visual/public/app.js b/test-visual/public/app.js new file mode 100644 index 00000000..29230b90 --- /dev/null +++ b/test-visual/public/app.js @@ -0,0 +1,478 @@ +const state = { + running: false, + resultAction: 'reset', + countries: {}, +}; + +const $ = (selector) => document.querySelector(selector); + +const els = { + amount: $('#amount'), + country: $('#country'), + currency: $('#currency'), + scenario: $('#scenario'), + recipient: $('#recipient'), + message: $('#message'), + yapearButton: $('#yapearButton'), + resetButton: $('#resetButton'), + consoleOutput: $('#consoleOutput'), + eventInspector: $('#eventInspector'), + healthGrid: $('#healthGrid'), + timeline: $('#timeline'), + serviceMap: $('#serviceMap'), + narratorText: $('#narratorText'), + dbEvidence: $('#dbEvidence'), + countryAvailability: $('#countryAvailability'), + paymentForm: $('#paymentForm'), + phoneResult: $('#phoneResult'), + phoneResultIcon: $('#phoneResultIcon'), + phoneResultKicker: $('#phoneResultKicker'), + phoneResultTitle: $('#phoneResultTitle'), + phoneResultAmount: $('#phoneResultAmount'), + phoneResultMeta: $('#phoneResultMeta'), + phoneResultDetail: $('#phoneResultDetail'), + phoneResultButton: $('#phoneResultButton'), +}; + +const currencySymbols = { + PEN: 'S/', + MXN: '$', + USD: 'US$', + COP: '$', + CLP: '$', + ARS: '$', + BRL: 'R$', +}; + +const services = { + api: { title: 'Payment API', subtitle: 'Recibe el yape', state: 'waiting', detail: 'Esperando request' }, + outbox: { title: 'Outbox DB', subtitle: 'Commit local', state: 'waiting', detail: 'payment + outbox en una transaccion' }, + relay: { title: 'Outbox Relay', subtitle: 'Publicador separado', state: 'waiting', detail: 'Kafka se llama despues del commit' }, + kafka: { title: 'Kafka Topic', subtitle: 'Eventos por pais', state: 'waiting', detail: 'pe.payments.* / mx.payments.*' }, + fraud: { title: 'Fraud Consumer', subtitle: 'Riesgo independiente', state: 'waiting', detail: 'Idempotente por eventId' }, + ledger: { title: 'Ledger Consumer', subtitle: 'Doble entrada', state: 'waiting', detail: 'Aislado por pais' }, + saga: { title: 'Status Saga', subtitle: 'Consistencia eventual', state: 'waiting', detail: 'Cierra settled o failed' }, + dlt: { title: 'DLT', subtitle: 'Fallos irreparables', state: 'waiting', detail: 'No se pierden mensajes agotados' }, +}; + +function appendConsole(line, level = 'info') { + const prefix = { + info: 'INFO ', + ok: 'OK ', + warn: 'WARN ', + error: 'ERR ', + }[level] || 'INFO '; + + els.consoleOutput.textContent += `\n${new Date().toLocaleTimeString()} ${prefix}${line}`; + els.consoleOutput.scrollTop = els.consoleOutput.scrollHeight; +} + +function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function prettyJson(value) { + if (value === null || value === undefined) { + return 'null'; + } + + return escapeHtml(JSON.stringify(value, null, 2)); +} + +function moneyLabel() { + const currency = els.currency.value; + const amount = Number(els.amount.value || 0).toFixed(2); + return `${currencySymbols[currency] || currency} ${amount}`; +} + +function showPaymentForm() { + els.paymentForm.classList.remove('hidden'); + els.phoneResult.classList.add('hidden'); +} + +function showPhoneState({ status, title, detail, action }) { + const labels = { + processing: 'Procesando', + success: 'Yape exitoso', + failed: 'No completado', + pending: 'En proceso', + }; + const icons = { + processing: '...', + success: 'OK', + failed: '!', + pending: '...', + }; + + state.resultAction = action || 'reset'; + els.paymentForm.classList.add('hidden'); + els.phoneResult.className = `phone-result ${status || 'processing'}`; + els.phoneResultIcon.className = `result-icon ${status || 'processing'}`; + els.phoneResultIcon.textContent = icons[status] || '...'; + els.phoneResultKicker.textContent = labels[status] || 'Procesando'; + els.phoneResultTitle.textContent = title || 'Procesando tu yape'; + els.phoneResultAmount.textContent = moneyLabel(); + els.phoneResultMeta.textContent = `${els.country.value.toUpperCase()} ยท ${els.currency.value}`; + els.phoneResultDetail.textContent = detail || `Para ${els.recipient.value}`; + els.phoneResultButton.textContent = action === 'retry' ? 'Reintentar' : status === 'processing' ? 'Procesando...' : 'Volver a yapear'; + els.phoneResultButton.disabled = status === 'processing'; +} + +function resetTimeline() { + els.timeline.querySelectorAll('[data-step]').forEach((node) => { + node.className = ''; + }); +} + +function renderServices() { + els.serviceMap.innerHTML = Object.entries(services) + .map(([key, service]) => ` +

+ `) + .join(''); +} + +function setService(service, state, detail) { + if (!services[service]) return; + services[service].state = state || services[service].state; + services[service].detail = detail || services[service].detail; + renderServices(); +} + +function setStep(step, status) { + const node = els.timeline.querySelector(`[data-step="${step}"]`); + if (!node) return; + node.className = status; +} + +function resetDemo() { + els.consoleOutput.textContent = 'Listo para yapear...'; + els.eventInspector.textContent = '{}'; + els.dbEvidence.innerHTML = '
Ejecuta un escenario para ver payments, outbox_events, payment_steps y processed_events.
'; + els.narratorText.textContent = 'Elige un escenario y presiona Yapear.'; + state.resultAction = 'reset'; + els.phoneResultButton.disabled = false; + showPaymentForm(); + resetTimeline(); + Object.keys(services).forEach((key) => { + services[key].state = 'waiting'; + }); + renderServices(); +} + +function renderDbList(title, rows, emptyMessage) { + const content = rows && rows.length + ? rows.map((row) => `
${prettyJson(row)}
`).join('') + : `

${escapeHtml(emptyMessage)}

`; + + return ` +
+ ${escapeHtml(title)} + ${content} +
+ `; +} + +function renderDbObject(title, value, emptyMessage) { + return renderDbList(title, value ? [value] : [], emptyMessage); +} + +function renderDbSnapshot(snapshot) { + const summary = (snapshot.summary || []) + .map((item) => `${escapeHtml(item)}`) + .join(''); + + els.dbEvidence.innerHTML = ` +
+ ${escapeHtml(snapshot.stage || 'DB snapshot')} +

${escapeHtml(snapshot.note || 'Snapshot de evidencia persistida.')}

+
${summary}
+
+
+ ${renderDbObject('payments', snapshot.payment, 'Sin fila payment para este escenario.')} + ${renderDbObject('payment_steps', snapshot.paymentStep, 'Todavia no hay ACK persistido de fraud/ledger.')} + ${renderDbList('outbox_events', snapshot.outboxEvents, 'Sin outbox asociado.')} + ${renderDbList('processed_events', snapshot.processedEvents, 'Aun no hay marcas idempotentes de consumers.')} +
+ `; +} + +function statusClass(status) { + return status === 'running' ? 'running' : status === 'missing' || status === 'exited' ? status : ''; +} + +function healthItem(name, status) { + return `
${name}${status}
`; +} + +function availabilityClass(status) { + return status === 'available' ? 'available' : status === 'missing' ? 'missing' : 'degraded'; +} + +function renderCountryAvailability() { + const countries = Object.entries(state.countries); + if (!countries.length) { + els.countryAvailability.innerHTML = ''; + return; + } + + els.countryAvailability.innerHTML = countries + .map(([country, info]) => ` +
+ ${country.toUpperCase()} + ${escapeHtml(info.availability || 'unknown')} +

${escapeHtml(info.detail || `currency=${info.currency || 'N/A'}, ledger=${info.ledgerConsumer || 'unknown'}, saga=${info.statusSaga || 'unknown'}`)}

+
+ `) + .join(''); +} + +function syncCountryOptions(countries) { + const previous = els.country.value; + const entries = Object.entries(countries); + if (!entries.length) return; + + els.country.innerHTML = entries + .map(([country, info]) => ``) + .join(''); + + els.country.value = countries[previous] ? previous : entries[0][0]; + syncCurrency(); +} + +function setCountryAvailability(country, availability, detail) { + const normalized = String(country || '').toLowerCase(); + if (!normalized) return; + + state.countries[normalized] = { + ...(state.countries[normalized] || {}), + availability, + detail, + }; + renderCountryAvailability(); +} + +async function loadHealth() { + try { + const res = await fetch('/api/health'); + const health = await res.json(); + state.countries = health.countries || {}; + syncCountryOptions(state.countries); + renderCountryAvailability(); + + const countryHealth = Object.entries(state.countries).flatMap(([country, info]) => [ + healthItem(`${country.toUpperCase()} Ledger`, info.ledgerConsumer), + healthItem(`${country.toUpperCase()} Saga`, info.statusSaga), + ]); + + els.healthGrid.innerHTML = [ + healthItem('Payment API', health.paymentApi), + healthItem('Kafka', health.kafka), + healthItem('Outbox Relay', health.outboxRelay), + healthItem('Fraud', health.fraudConsumer), + ...countryHealth, + ].join(''); + } catch (error) { + els.healthGrid.innerHTML = healthItem('Health', 'unavailable'); + } +} + +function syncCurrency() { + const country = els.country.value; + const currency = state.countries[country]?.currency || (country === 'mx' ? 'MXN' : 'PEN'); + + if (!Array.from(els.currency.options).some((option) => option.value === currency)) { + const option = document.createElement('option'); + option.value = currency; + option.textContent = currencySymbols[currency] || currency; + els.currency.appendChild(option); + } + + els.currency.value = currency; +} + +function applyScenarioDefaults() { + const scenario = els.scenario.value; + if (scenario === 'fraud-rejected') { + els.amount.value = '1500'; + } + if (scenario === 'mx-outage-pe-available') { + els.country.value = 'pe'; + syncCurrency(); + } + if ((scenario === 'success' || scenario === 'idempotency-replay' || scenario === 'country-isolation' || scenario === 'mx-outage-pe-available') && Number(els.amount.value) > 500) { + els.amount.value = '25'; + } +} + +async function runScenario() { + if (state.running) return; + + state.running = true; + els.yapearButton.disabled = true; + resetDemo(); + syncCurrency(); + applyScenarioDefaults(); + showPhoneState({ + status: 'processing', + title: 'Procesando tu yape', + detail: 'Estamos creando el pago y esperando confirmacion del pipeline.', + action: 'none', + }); + + appendConsole(`Iniciando escenario ${els.scenario.value} para ${els.country.value.toUpperCase()}`, 'info'); + + const payload = { + scenario: els.scenario.value, + country: els.country.value, + currency: els.currency.value, + amount: Number(els.amount.value), + walletId: `visual-${els.country.value}-${Date.now()}`, + recipient: els.recipient.value, + message: els.message.value, + }; + + try { + const response = await fetch('/api/scenario', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (!response.body) { + throw new Error('El navegador no pudo abrir el stream de eventos.'); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() || ''; + + for (const chunk of chunks) { + const line = chunk.split('\n').find((part) => part.startsWith('data: ')); + if (!line) continue; + + const event = JSON.parse(line.slice(6)); + handleEvent(event); + } + } + } catch (error) { + appendConsole(error.message || String(error), 'error'); + showPhoneState({ + status: 'failed', + title: 'No pudimos completar tu yape', + detail: error.message || String(error), + action: 'retry', + }); + } finally { + state.running = false; + els.yapearButton.disabled = false; + await loadHealth(); + } +} + +function handleEvent(event) { + if (event.type === 'step') { + setStep(event.step, event.status); + return; + } + + if (event.type === 'service') { + setService(event.service, event.status, event.detail); + return; + } + + if (event.type === 'narrative') { + els.narratorText.textContent = event.message; + appendConsole(event.message, event.level || 'info'); + return; + } + + if (event.type === 'phone-state') { + showPhoneState(event); + return; + } + + if (event.type === 'country-status') { + setCountryAvailability(event.country, event.availability, event.detail); + appendConsole(`${String(event.country || '').toUpperCase()} availability=${event.availability}: ${event.detail}`, event.availability === 'available' ? 'ok' : 'warn'); + return; + } + + if (event.type === 'outbox') { + setService('outbox', event.status === 'published' ? 'ok' : 'running', `status=${event.status}, attempts=${event.attempts}, topic=${event.topic}`); + appendConsole(`Outbox ${event.status} attempts=${event.attempts} topic=${event.topic}`, event.status === 'published' ? 'ok' : 'info'); + return; + } + + if (event.type === 'idempotency') { + appendConsole(event.message, event.level || 'ok'); + setService('fraud', 'ok', event.fraud || 'duplicate ignored'); + setService('ledger', 'ok', event.ledger || 'duplicate ignored'); + els.eventInspector.textContent = JSON.stringify(event.rows || [], null, 2); + return; + } + + if (event.type === 'db-snapshot') { + renderDbSnapshot(event.snapshot); + appendConsole(`DB snapshot: ${event.snapshot.stage}`, 'ok'); + return; + } + + if (event.type === 'kafka-event') { + appendConsole(`${event.topic}`, event.level); + els.eventInspector.textContent = JSON.stringify(event.event, null, 2); + return; + } + + if (event.type === 'status') { + appendConsole(`${event.message} ${JSON.stringify(event.status)}`, event.level); + return; + } + + if (event.type === 'summary') { + appendConsole(event.message, event.level); + return; + } + + if (event.type === 'done') { + appendConsole('Escenario finalizado.', 'ok'); + return; + } + + appendConsole(event.message || JSON.stringify(event), event.level || 'info'); +} + +els.yapearButton.addEventListener('click', runScenario); +els.resetButton.addEventListener('click', resetDemo); +els.phoneResultButton.addEventListener('click', () => { + if (state.resultAction === 'retry') { + resetDemo(); + runScenario(); + return; + } + + resetDemo(); +}); +els.country.addEventListener('change', syncCurrency); +els.scenario.addEventListener('change', applyScenarioDefaults); + +loadHealth(); +renderServices(); +setInterval(loadHealth, 8000); diff --git a/test-visual/public/index.html b/test-visual/public/index.html new file mode 100644 index 00000000..11adcd78 --- /dev/null +++ b/test-visual/public/index.html @@ -0,0 +1,141 @@ + + + + + + Yape Visual Demo + + + +
+
+
+ 12:30 + LTE +
+ +
+ + Yapear a + +
+ +
+ + + +
+ + +
+ +

Limite por yapeo S/500, limite total por dia S/2,000

+ +
+ + + +
+ + + +
+ + +
+ + +
+ + +
+ +
+ + +
+
+ +
+ Payment API + Outbox + Kafka + Fraud + Ledger + Saga + DLT +
+ +
+ +
+
Modo seguimiento
+

Elige un escenario y presiona Yapear.

+
+ +
+
+
CMD en vivo
+
Listo para yapear...
+
+ +
+
Inspector de evento
+
{}
+
+
+ +
+
Database Evidence
+

Aqui se muestran las filas reales que prueban outbox, idempotencia y consistencia eventual.

+
+
Ejecuta un escenario para ver payments, outbox_events, payment_steps y processed_events.
+
+
+
+
+ + + + diff --git a/test-visual/public/styles.css b/test-visual/public/styles.css new file mode 100644 index 00000000..cf289b5a --- /dev/null +++ b/test-visual/public/styles.css @@ -0,0 +1,713 @@ +:root { + --ink: #252131; + --muted: #6d687c; + --line: #e6e1ed; + --brand: #74218a; + --brand-soft: #f3e7f7; + --money: #09bda5; + --ok: #13a56f; + --warn: #d88900; + --error: #c83f5a; + --paper: #fbf9fd; + --cmd: #101418; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100vh; + background: + linear-gradient(135deg, rgba(9, 189, 165, 0.12), transparent 34%), + linear-gradient(315deg, rgba(116, 33, 138, 0.16), transparent 38%), + #f5f2f8; + color: var(--ink); + font-family: Avenir Next, Trebuchet MS, Verdana, sans-serif; +} + +button, +input, +select { + font: inherit; +} + +.shell { + display: grid; + grid-template-columns: minmax(320px, 430px) minmax(360px, 1fr); + gap: 28px; + width: min(1280px, calc(100vw - 32px)); + margin: 20px auto; + align-items: start; +} + +.phone { + min-height: 860px; + border: 8px solid #d8d3df; + border-radius: 48px; + background: white; + overflow: hidden; + box-shadow: 0 22px 48px rgba(37, 33, 49, 0.18); +} + +.hidden { + display: none !important; +} + +.status-bar { + height: 58px; + padding: 18px 32px 0; + color: white; + background: var(--brand); + display: flex; + justify-content: space-between; + font-weight: 700; +} + +.topbar { + height: 76px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 24px; + font-size: 24px; +} + +.ghost-button { + width: 42px; + height: 42px; + border: 0; + background: transparent; + color: var(--ink); + font-size: 46px; + line-height: 1; + cursor: pointer; +} + +.recipient-label { + display: block; + margin-top: 34px; + color: var(--muted); + text-align: center; + font-size: 13px; + text-transform: uppercase; +} + +.recipient-input { + display: block; + width: calc(100% - 72px); + margin: 8px auto 54px; + border: 0; + color: var(--brand); + text-align: center; + font-size: 30px; + font-weight: 800; + outline: 0; +} + +.amount-line { + display: flex; + justify-content: center; + align-items: baseline; + gap: 8px; +} + +.currency-select { + width: 92px; + border: 0; + color: #c98bd7; + background: transparent; + font-size: 40px; + font-weight: 800; + outline: 0; +} + +.amount-input { + width: 180px; + border: 0; + color: var(--brand); + font-size: 96px; + font-weight: 700; + outline: 0; +} + +.limit-copy { + width: max-content; + max-width: calc(100% - 64px); + margin: 20px auto 72px; + padding: 8px 18px; + border-radius: 8px; + background: #f3f0f7; + color: var(--muted); + font-weight: 700; + text-align: center; +} + +.form-row { + display: grid; + grid-template-columns: 92px 1fr; + gap: 12px; + padding: 0 28px; +} + +.form-row label { + color: var(--muted); + font-size: 13px; + font-weight: 800; + text-transform: uppercase; +} + +.form-row select, +.message-input { + width: 100%; + margin-top: 6px; + border: 1px solid var(--line); + border-radius: 8px; + padding: 12px; + background: white; + color: var(--ink); +} + +.message-input { + display: block; + width: calc(100% - 56px); + margin: 18px auto 18px; + text-align: center; +} + +.actions { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; + padding: 0 28px 22px; +} + +.outline-button, +.primary-button { + min-height: 54px; + border-radius: 8px; + font-weight: 900; + cursor: pointer; +} + +.outline-button { + border: 2px solid var(--money); + color: var(--money); + background: white; +} + +.primary-button { + border: 0; + color: white; + background: var(--money); +} + +.primary-button:disabled { + opacity: 0.6; + cursor: wait; +} + +.keypad { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + padding: 10px 24px 28px; + background: #d7d9df; +} + +.keypad span { + min-height: 58px; + border-radius: 6px; + background: white; + display: grid; + place-items: center; + font-size: 28px; + box-shadow: 0 2px 0 rgba(0, 0, 0, 0.14); +} + +.keypad small { + font-size: 11px; + font-weight: 900; +} + +.keypad .zero { + grid-column: 2; +} + +.phone-result { + min-height: 700px; + padding: 72px 34px 34px; + text-align: center; + display: grid; + align-content: start; + justify-items: center; +} + +.result-icon { + width: 92px; + height: 92px; + border-radius: 999px; + display: grid; + place-items: center; + color: white; + font-size: 30px; + font-weight: 1000; + letter-spacing: -0.04em; + box-shadow: 0 16px 32px rgba(37, 33, 49, 0.18); +} + +.result-icon.processing, +.result-icon.pending { + background: var(--warn); +} + +.result-icon.success { + background: var(--money); +} + +.result-icon.failed { + background: var(--error); +} + +.result-kicker { + margin: 24px 0 0; + color: var(--muted); + font-size: 13px; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.phone-result h2 { + margin: 10px 0 0; + color: var(--brand); + font-size: 30px; +} + +.result-amount { + margin: 28px 0 0; + color: var(--ink); + font-size: 52px; + font-weight: 900; +} + +.result-meta { + margin: 6px 0 0; + padding: 6px 12px; + border-radius: 999px; + background: var(--brand-soft); + color: var(--brand); + font-size: 12px; + font-weight: 900; + letter-spacing: 0.08em; +} + +.result-detail { + width: min(320px, 100%); + margin: 18px auto 34px; + color: var(--muted); + font-size: 16px; + line-height: 1.45; +} + +.phone-result .primary-button { + width: 100%; +} + +.ops { + min-height: 860px; + display: grid; + gap: 18px; +} + +.ops-header { + display: flex; + justify-content: space-between; + gap: 18px; + align-items: center; +} + +.eyebrow { + margin: 0 0 6px; + color: var(--money); + font-weight: 900; + text-transform: uppercase; +} + +h1 { + margin: 0; + font-size: 36px; +} + +.kafka-link { + border-radius: 8px; + padding: 12px 16px; + background: var(--ink); + color: white; + text-decoration: none; + font-weight: 900; +} + +.health-grid { + display: grid; + grid-template-columns: repeat(4, minmax(120px, 1fr)); + gap: 10px; +} + +.health-item { + border-left: 4px solid var(--line); + padding: 10px 12px; + background: rgba(255, 255, 255, 0.72); + border-radius: 8px; +} + +.health-item strong { + display: block; + font-size: 13px; +} + +.health-item span { + color: var(--muted); + font-size: 12px; +} + +.health-item.running { + border-color: var(--ok); +} + +.health-item.exited, +.health-item.missing { + border-color: var(--error); +} + +.country-availability { + display: grid; + grid-template-columns: repeat(2, minmax(180px, 1fr)); + gap: 10px; +} + +.country-card { + border: 1px solid var(--line); + border-left: 6px solid var(--line); + border-radius: 8px; + padding: 12px; + background: white; +} + +.country-card strong { + display: inline-block; + margin-right: 8px; + font-size: 20px; +} + +.country-card span { + display: inline-block; + padding: 4px 9px; + border-radius: 999px; + color: white; + font-size: 11px; + font-weight: 900; + text-transform: uppercase; +} + +.country-card p { + margin: 8px 0 0; + color: var(--muted); + font-size: 12px; + line-height: 1.4; +} + +.country-card.available { + border-left-color: var(--ok); +} + +.country-card.available span { + background: var(--ok); +} + +.country-card.degraded, +.country-card.missing { + border-left-color: var(--error); + background: #fff6f7; +} + +.country-card.degraded span, +.country-card.missing span { + background: var(--error); +} + +.timeline { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 8px; +} + +.timeline span { + min-height: 54px; + border: 1px solid var(--line); + border-radius: 8px; + background: white; + display: grid; + place-items: center; + text-align: center; + color: var(--muted); + font-size: 13px; + font-weight: 900; +} + +.timeline span.running { + border-color: var(--warn); + background: #fff7e5; + color: var(--warn); +} + +.timeline span.ok { + border-color: var(--ok); + background: #e8f8f1; + color: var(--ok); +} + +.timeline span.failed { + border-color: var(--error); + background: #fdecef; + color: var(--error); +} + +.timeline span.retrying { + border-color: var(--warn); + background: #fff7e5; + color: var(--warn); +} + +.service-map { + display: grid; + grid-template-columns: repeat(4, minmax(140px, 1fr)); + gap: 10px; +} + +.service-card { + min-height: 118px; + border: 1px solid var(--line); + border-top: 5px solid var(--line); + border-radius: 8px; + padding: 12px; + background: white; +} + +.service-card strong, +.service-card span { + display: block; +} + +.service-card strong { + font-size: 14px; +} + +.service-card span { + margin-top: 4px; + color: var(--muted); + font-size: 12px; + font-weight: 800; +} + +.service-card p { + margin: 10px 0 0; + color: var(--ink); + font-size: 12px; + line-height: 1.35; +} + +.service-card.running, +.service-card.retrying { + border-top-color: var(--warn); +} + +.service-card.ok { + border-top-color: var(--ok); +} + +.service-card.failed { + border-top-color: var(--error); +} + +.narrator-panel { + border: 1px solid var(--line); + border-radius: 8px; + overflow: hidden; + background: white; +} + +.narrator-panel p { + margin: 0; + padding: 16px; + color: var(--ink); + font-size: 15px; + line-height: 1.45; +} + +.panels { + display: grid; + grid-template-columns: minmax(320px, 1.35fr) minmax(280px, 0.85fr); + gap: 14px; + min-height: 560px; +} + +.console-panel, +.inspector-panel { + border-radius: 8px; + overflow: hidden; + background: white; + border: 1px solid var(--line); +} + +.panel-title { + padding: 12px 14px; + background: var(--ink); + color: white; + font-weight: 900; +} + +.console-output, +.event-inspector { + height: 520px; + margin: 0; + padding: 16px; + overflow: auto; + background: var(--cmd); + color: #d9ffe8; + font-family: Menlo, Monaco, Consolas, monospace; + font-size: 12px; + line-height: 1.55; + white-space: pre-wrap; +} + +.event-inspector { + color: #dce9ff; +} + +.db-panel { + border: 1px solid var(--line); + border-radius: 8px; + overflow: hidden; + background: white; +} + +.db-copy { + margin: 0; + padding: 14px 16px 0; + color: var(--muted); + font-size: 13px; + line-height: 1.45; +} + +.db-evidence { + padding: 14px; +} + +.db-empty { + border: 1px dashed var(--line); + border-radius: 8px; + padding: 16px; + color: var(--muted); + background: #faf8f4; +} + +.db-stage { + border: 1px solid var(--line); + border-left: 5px solid var(--accent); + border-radius: 8px; + padding: 14px; + margin-bottom: 12px; + background: #fffdf6; +} + +.db-stage strong { + display: block; + font-size: 15px; +} + +.db-stage p { + margin: 6px 0 0; + color: var(--ink); + line-height: 1.45; +} + +.db-summary { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; +} + +.db-summary span { + padding: 7px 9px; + border-radius: 999px; + background: var(--ink); + color: white; + font-size: 12px; + font-weight: 900; +} + +.db-grid { + display: grid; + grid-template-columns: repeat(2, minmax(260px, 1fr)); + gap: 12px; +} + +.db-card { + border: 1px solid var(--line); + border-radius: 8px; + background: #fbfbfb; + overflow: hidden; +} + +.db-card strong { + display: block; + padding: 10px 12px; + background: #ede7d7; + color: var(--ink); + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.db-card pre, +.muted-row { + margin: 0; + padding: 12px; +} + +.db-card pre { + overflow: auto; + background: #101820; + color: #c7ffdf; + font-family: Menlo, Monaco, Consolas, monospace; + font-size: 11px; + line-height: 1.5; + border-top: 1px solid rgba(255, 255, 255, 0.08); +} + +.muted-row { + color: var(--muted); + font-size: 13px; +} + +@media (max-width: 920px) { + .shell, + .panels { + grid-template-columns: 1fr; + } + + .phone { + min-height: auto; + } + + .health-grid, + .country-availability, + .timeline, + .service-map, + .db-grid { + grid-template-columns: repeat(2, 1fr); + } +} diff --git a/test-visual/server.js b/test-visual/server.js new file mode 100644 index 00000000..bfbb962e --- /dev/null +++ b/test-visual/server.js @@ -0,0 +1,937 @@ +const http = require('node:http'); +const fs = require('node:fs'); +const path = require('node:path'); +const { spawn } = require('node:child_process'); + +const PORT = Number(process.env.PORT || 4000); +const PAYMENT_API_URL = process.env.PAYMENT_API_URL || 'http://payment-api:3000'; +const KAFKA_CONTAINER = process.env.KAFKA_CONTAINER || 'yape-kafka'; +const POSTGRES_CONTAINER = process.env.POSTGRES_CONTAINER || 'yape-postgres'; +const DB_USER = process.env.DB_USER || 'postgres'; +const DB_PASSWORD = process.env.DB_PASSWORD || 'postgres'; +const DB_NAME = process.env.DB_NAME || 'yape'; +const PUBLIC_DIR = path.join(__dirname, 'public'); + +const CONTENT_TYPES = { + '.html': 'text/html; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.js': 'text/javascript; charset=utf-8', + '.json': 'application/json; charset=utf-8', +}; + +const TOPIC_SUFFIXES = [ + 'payment.created.v1', + 'fraud.assessed.v1', + 'ledger.posted.v1', + 'payment.settled.v1', + 'payment.failed.v1', + 'payment.created.v1.dlt', + 'fraud.assessed.v1.dlt', + 'ledger.posted.v1.dlt', +]; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function sendJson(res, statusCode, payload) { + res.writeHead(statusCode, { 'Content-Type': 'application/json; charset=utf-8' }); + res.end(JSON.stringify(payload)); +} + +function parseBody(req) { + return new Promise((resolve, reject) => { + let body = ''; + req.on('data', (chunk) => { + body += chunk; + }); + req.on('end', () => { + try { + resolve(body ? JSON.parse(body) : {}); + } catch (error) { + reject(error); + } + }); + }); +} + +function run(command, args, options = {}) { + return new Promise((resolve) => { + const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'], ...options }); + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + stdout += chunk.toString(); + }); + child.stderr.on('data', (chunk) => { + stderr += chunk.toString(); + }); + child.on('error', (error) => { + resolve({ code: 1, stdout, stderr: error.message }); + }); + child.on('close', (code) => { + resolve({ code: code ?? 0, stdout, stderr }); + }); + }); +} + +async function containerStatus(name) { + const result = await run('docker', ['inspect', '-f', '{{.State.Status}}', name]); + if (result.code !== 0) { + return 'missing'; + } + + return result.stdout.trim() || 'unknown'; +} + +async function countryContainerId(country, service) { + const result = await run('docker', [ + 'ps', + '-a', + '--filter', + `label=com.docker.compose.project=yape-${country}`, + '--filter', + `label=com.docker.compose.service=${service}`, + '--format', + '{{.ID}}', + ]); + + return result.stdout.trim().split('\n').filter(Boolean)[0] || ''; +} + +async function countryServiceStatus(country, service) { + const id = await countryContainerId(country, service); + if (!id) { + return 'missing'; + } + + const result = await run('docker', ['inspect', '-f', '{{.State.Status}}', id]); + return result.code === 0 ? result.stdout.trim() : 'unknown'; +} + +async function countryServiceEnv(country, service, key) { + const id = await countryContainerId(country, service); + if (!id) { + return ''; + } + + const result = await run('docker', [ + 'inspect', + '-f', + `{{range .Config.Env}}{{println .}}{{end}}`, + id, + ]); + + if (result.code !== 0) { + return ''; + } + + const prefix = `${key}=`; + const line = result.stdout + .split('\n') + .find((entry) => entry.startsWith(prefix)); + + return line ? line.slice(prefix.length).trim() : ''; +} + +async function countryCurrency(country) { + const fromLedger = await countryServiceEnv(country, 'ledger-consumer', 'COUNTRY_CURRENCY'); + const fromSaga = fromLedger || (await countryServiceEnv(country, 'status-saga', 'COUNTRY_CURRENCY')); + return fromSaga || defaultCurrencyForCountry(country); +} + +function availabilityFrom(ledgerStatus, sagaStatus) { + if (ledgerStatus === 'running' && sagaStatus === 'running') { + return 'available'; + } + if (ledgerStatus === 'missing' && sagaStatus === 'missing') { + return 'missing'; + } + return 'degraded'; +} + +async function stopCountryService(country, service) { + const id = await countryContainerId(country, service); + if (!id) { + throw new Error(`${service} container for ${country} was not found`); + } + + await run('docker', ['stop', id]); +} + +async function startCountryService(country, service) { + const id = await countryContainerId(country, service); + if (!id) { + throw new Error(`${service} container for ${country} was not found`); + } + + await run('docker', ['start', id]); +} + +async function health() { + const [api, kafka, relay, fraud] = await Promise.all([ + containerStatus('yape-payment-api'), + containerStatus(KAFKA_CONTAINER), + containerStatus('yape-outbox-relay'), + containerStatus('yape-fraud-consumer'), + ]); + + const countries = {}; + for (const country of supportedCountries()) { + const [ledgerConsumer, statusSaga, currency] = await Promise.all([ + countryServiceStatus(country, 'ledger-consumer'), + countryServiceStatus(country, 'status-saga'), + countryCurrency(country), + ]); + + countries[country] = { + currency, + ledgerConsumer, + statusSaga, + availability: availabilityFrom(ledgerConsumer, statusSaga), + }; + } + + return { + paymentApi: api, + kafka, + outboxRelay: relay, + fraudConsumer: fraud, + countries, + }; +} + +function topic(country, suffix) { + return `${country}.payments.${suffix}`; +} + +function paymentIdFromEvent(event) { + return event?.payload?.paymentId || event?.aggregateId || ''; +} + +function normalizeCountry(country) { + return String(country || 'pe').trim().toLowerCase(); +} + +function supportedCountries() { + return String(process.env.SUPPORTED_COUNTRIES || 'pe,mx') + .split(',') + .map((country) => normalizeCountry(country)) + .filter((country) => /^[a-z]{2}$/.test(country)); +} + +function defaultCurrencyForCountry(country) { + return { + pe: 'PEN', + mx: 'MXN', + co: 'COP', + cl: 'CLP', + ar: 'ARS', + br: 'BRL', + ec: 'USD', + }[country] || country.toUpperCase(); +} + +function writeEvent(res, event) { + res.write(`data: ${JSON.stringify({ at: new Date().toISOString(), ...event })}\n\n`); +} + +function serviceEvent(service, status, detail) { + return { type: 'service', service, status, detail }; +} + +function narrative(message, level = 'info') { + return { type: 'narrative', level, message }; +} + +function phoneState(status, title, detail, action = 'reset') { + return { type: 'phone-state', status, title, detail, action }; +} + +async function psql(query) { + const result = await run('docker', [ + 'exec', + '-e', + `PGPASSWORD=${DB_PASSWORD}`, + POSTGRES_CONTAINER, + 'psql', + '-U', + DB_USER, + '-d', + DB_NAME, + '-t', + '-A', + '-F', + '|', + '-c', + query, + ]); + + if (result.code !== 0) { + throw new Error(`psql failed: ${result.stderr || result.stdout}`); + } + + return result.stdout.trim(); +} + +function sqlLiteral(value) { + return `'${String(value).replace(/'/g, "''")}'`; +} + +async function psqlJson(query, fallback) { + const raw = await psql(query); + if (!raw) { + return fallback; + } + + return JSON.parse(raw); +} + +async function readOutbox(paymentId) { + const rows = await psql( + `select status, attempts, topic, "eventId" from outbox_events where "aggregateId"=${sqlLiteral(paymentId)} order by "createdAt" desc limit 1;`, + ); + const [row] = rows.split('\n').filter(Boolean); + if (!row) { + return null; + } + + const [status, attempts, outboxTopic, eventId] = row.split('|'); + return { status, attempts: Number(attempts), topic: outboxTopic, eventId }; +} + +async function processedRows(country, eventIds) { + const ids = eventIds.map((id) => sqlLiteral(id)).join(','); + if (!ids) { + return []; + } + + const rows = await psql( + `select "consumerName", "countryCode", "eventId" from processed_events where lower("countryCode")=lower(${sqlLiteral(country)}) and "eventId" in (${ids}) order by "consumerName", "eventId";`, + ); + + return rows + .split('\n') + .filter(Boolean) + .map((row) => { + const [consumerName, countryCode, eventId] = row.split('|'); + return { consumerName, countryCode, eventId }; + }); +} + +async function dbSnapshot(paymentId, stage, note) { + if (!paymentId) { + return { + stage, + note, + payment: null, + paymentStep: null, + outboxEvents: [], + processedEvents: [], + summary: ['No hay paymentId: este escenario inyecta un mensaje directo en Kafka, por eso la evidencia principal vive en el DLT.'], + }; + } + + const paymentIdSql = sqlLiteral(paymentId); + const payment = await psqlJson( + `select coalesce((select row_to_json(t)::text from ( + select id, "walletId", "countryCode", amount::text as amount, currency, status, "createdAt", "updatedAt" + from payments + where id=${paymentIdSql} + ) t), 'null');`, + null, + ); + const paymentStep = await psqlJson( + `select coalesce((select row_to_json(t)::text from ( + select "paymentId", "fraudStatus", "ledgerStatus", "failureReason", "updatedAt" + from payment_steps + where "paymentId"=${paymentIdSql} + ) t), 'null');`, + null, + ); + const outboxEvents = await psqlJson( + `select coalesce((select json_agg(t)::text from ( + select id, "eventId", "eventType", topic, "countryCode", status, attempts, "publishedAt", "createdAt", "updatedAt" + from outbox_events + where "aggregateId"=${paymentIdSql} + order by "createdAt" + ) t), '[]');`, + [], + ); + const processedEvents = await psqlJson( + `with base as ( + select "eventId"::varchar as id + from outbox_events + where "aggregateId"=${paymentIdSql} + ) + select coalesce((select json_agg(t)::text from ( + select "consumerName", "countryCode", "eventId", "processedAt" + from processed_events pe + where exists ( + select 1 + from base + where pe."eventId" = base.id + or pe."eventId" like base.id || '.%' + ) + order by "consumerName", "processedAt" + ) t), '[]');`, + [], + ); + + const latestOutbox = outboxEvents[outboxEvents.length - 1]; + const summary = [ + payment ? `payments.status=${payment.status}` : 'payments: todavia no hay fila visible', + latestOutbox ? `outbox.status=${latestOutbox.status}, attempts=${latestOutbox.attempts}` : 'outbox: sin fila', + paymentStep ? `fraud=${paymentStep.fraudStatus}, ledger=${paymentStep.ledgerStatus}` : 'payment_steps: pendiente de consumidores', + `processed_events=${processedEvents.length}`, + ]; + + return { + stage, + note, + payment, + paymentStep, + outboxEvents, + processedEvents, + summary, + }; +} + +async function emitDbSnapshot({ paymentId, stage, note, emit }) { + try { + const snapshot = await dbSnapshot(paymentId, stage, note); + emit({ type: 'db-snapshot', snapshot }); + } catch (error) { + emit({ + type: 'log', + source: 'db', + level: 'warn', + message: `Could not read DB evidence: ${error instanceof Error ? error.message : String(error)}`, + }); + } +} + +async function pollOutbox({ paymentId, emit, timeoutMs = 12000 }) { + const start = Date.now(); + let last = null; + + while (Date.now() - start < timeoutMs) { + const row = await readOutbox(paymentId); + if (row && (!last || row.status !== last.status || row.attempts !== last.attempts)) { + emit({ type: 'outbox', ...row }); + last = row; + } + + if (row?.status === 'published') { + return row; + } + + await sleep(900); + } + + return last; +} + +function startWatcher({ res, country, paymentIdRef, acceptDlt, onKafkaEvent }) { + const children = []; + const seen = new Set(); + + for (const suffix of TOPIC_SUFFIXES) { + const topicName = topic(country, suffix); + const child = spawn('docker', [ + 'exec', + KAFKA_CONTAINER, + '/opt/kafka/bin/kafka-console-consumer.sh', + '--bootstrap-server', + 'localhost:9092', + '--topic', + topicName, + '--timeout-ms', + '90000', + ]); + + children.push(child); + let buffer = ''; + + child.stdout.on('data', (chunk) => { + buffer += chunk.toString(); + const lines = buffer.split(/\r?\n/); + buffer = lines.pop() || ''; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed) { + continue; + } + + let parsed = null; + try { + parsed = JSON.parse(trimmed); + } catch { + parsed = { raw: trimmed }; + } + + const eventPaymentId = paymentIdFromEvent(parsed); + const isRelevantPayment = paymentIdRef.value && eventPaymentId === paymentIdRef.value; + const isRelevantDlt = acceptDlt && topicName.endsWith('.dlt'); + + if (!isRelevantPayment && !isRelevantDlt) { + continue; + } + + const key = `${topicName}:${trimmed}`; + if (seen.has(key)) { + continue; + } + seen.add(key); + + writeEvent(res, { + type: 'kafka-event', + topic: topicName, + level: topicName.endsWith('.dlt') ? 'error' : 'ok', + message: `Kafka event on ${topicName}`, + event: parsed, + }); + onKafkaEvent?.({ topic: topicName, suffix, event: parsed, raw: trimmed }); + + const step = suffix.includes('dlt') + ? 'dlt' + : suffix === 'payment.created.v1' + ? 'created' + : suffix === 'fraud.assessed.v1' + ? 'fraud' + : suffix === 'ledger.posted.v1' + ? 'ledger' + : suffix === 'payment.settled.v1' || suffix === 'payment.failed.v1' + ? 'saga' + : ''; + + if (step) { + writeEvent(res, { type: 'step', step, status: topicName.endsWith('.dlt') ? 'failed' : 'ok' }); + } + } + }); + } + + return () => { + for (const child of children) { + if (!child.killed) { + child.kill('SIGTERM'); + } + } + }; +} + +async function createPayment({ country, walletId, amount, currency, emit }) { + const payload = { + walletId, + countryCode: country.toUpperCase(), + amount: Number(amount), + currency, + }; + + emit({ type: 'log', source: 'api', level: 'info', message: `POST /payments ${JSON.stringify(payload)}` }); + + const response = await fetch(`${PAYMENT_API_URL}/payments`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + const body = await response.json(); + if (!response.ok || !body.paymentId) { + throw new Error(`Payment API rejected request: ${JSON.stringify(body)}`); + } + + emit({ type: 'log', source: 'api', level: 'ok', message: `Payment created: ${body.paymentId}` }); + emit({ type: 'step', step: 'api', status: 'ok' }); + return body.paymentId; +} + +async function readStatus(paymentId) { + const response = await fetch(`${PAYMENT_API_URL}/payments/${paymentId}/status`); + return response.json(); +} + +async function pollStatus({ paymentId, timeoutMs, emit, expectedFinal = true }) { + const start = Date.now(); + let lastStatus = ''; + + while (Date.now() - start < timeoutMs) { + const status = await readStatus(paymentId); + lastStatus = status.status; + emit({ type: 'status', level: 'info', message: `Payment status: ${status.status}`, status }); + + if (expectedFinal && (status.status === 'settled' || status.status === 'failed')) { + emit({ + type: 'step', + step: 'saga', + status: status.status === 'settled' ? 'ok' : 'failed', + }); + return status; + } + + await new Promise((resolve) => setTimeout(resolve, 1500)); + } + + return { status: lastStatus || 'timeout' }; +} + +async function publishInvalidPayload(country, emit) { + const topicName = topic(country, 'payment.created.v1'); + const child = spawn('docker', [ + 'exec', + '-i', + KAFKA_CONTAINER, + '/opt/kafka/bin/kafka-console-producer.sh', + '--bootstrap-server', + 'localhost:9092', + '--topic', + topicName, + ]); + + const malformed = `not-json-${Date.now()}`; + emit({ type: 'log', source: 'kafka', level: 'warn', message: `Publishing malformed payload to ${topicName}` }); + child.stdin.write(`${malformed}\n`); + child.stdin.end(); + + await new Promise((resolve) => child.on('close', resolve)); + emit({ type: 'step', step: 'created', status: 'failed' }); +} + +async function publishPayload(topicName, payload, emit, label = 'payload') { + const child = spawn('docker', [ + 'exec', + '-i', + KAFKA_CONTAINER, + '/opt/kafka/bin/kafka-console-producer.sh', + '--bootstrap-server', + 'localhost:9092', + '--topic', + topicName, + ]); + + emit({ type: 'log', source: 'kafka', level: 'warn', message: `Publishing ${label} to ${topicName}` }); + child.stdin.write(`${payload}\n`); + child.stdin.end(); + + await new Promise((resolve) => child.on('close', resolve)); +} + +async function runScenario(req, res) { + const body = await parseBody(req); + const scenario = body.scenario || 'success'; + let country = normalizeCountry(body.country); + if (scenario === 'mx-outage-pe-available') { + country = 'pe'; + } + const paymentIdRef = { value: '' }; + const amount = scenario === 'fraud-rejected' ? 1500 : Number(body.amount || 25); + const currency = body.currency || (country === 'mx' ? 'MXN' : 'PEN'); + const walletId = body.walletId || `visual-${country}-${Date.now()}`; + const restore = []; + const emit = (event) => writeEvent(res, event); + const captured = { + paymentCreatedRaw: '', + paymentCreatedEventId: '', + fraudEventId: '', + ledgerEventId: '', + }; + + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + }); + + emit({ type: 'hello', message: 'Visual demo scenario started' }); + emit(phoneState('processing', 'Procesando tu yape', 'Estamos creando el pago y esperando confirmacion de los servicios.', 'none')); + emit(narrative('Esta demo sigue un pago real: primero API y outbox, luego Kafka, fraude, ledger y saga.')); + const stopWatchers = startWatcher({ + res, + country, + paymentIdRef, + acceptDlt: scenario === 'dlt-invalid-payload', + onKafkaEvent: ({ suffix, event, raw }) => { + if (suffix === 'payment.created.v1') { + captured.paymentCreatedRaw = raw; + captured.paymentCreatedEventId = event.eventId || ''; + emit(serviceEvent('kafka', 'ok', `published ${event.type || suffix}`)); + emit(serviceEvent('relay', 'ok', 'relay published the outbox event to Kafka')); + } + if (suffix === 'fraud.assessed.v1') { + captured.fraudEventId = event.eventId || ''; + const approved = event?.payload?.approved; + emit(serviceEvent('fraud', approved ? 'ok' : 'failed', approved ? 'approved by risk rules' : `rejected: ${event?.payload?.reason || 'risk rule'}`)); + emit(narrative(approved ? 'FraudConsumer respondio OK. Es independiente del ledger y puede reprocesar sin doble efecto.' : 'FraudConsumer rechazo el pago. La saga debe cerrar failed sin esperar un falso settled.', approved ? 'ok' : 'warn')); + } + if (suffix === 'ledger.posted.v1') { + captured.ledgerEventId = event.eventId || ''; + emit(serviceEvent('ledger', 'ok', `entryId=${event?.payload?.entryId || 'created'}`)); + emit(narrative('LedgerConsumer confirmo la escritura. En produccion aqui viviria la doble entrada contable.', 'ok')); + } + if (suffix === 'payment.settled.v1') { + emit(serviceEvent('saga', 'ok', 'fraud + ledger succeeded')); + emit(narrative('StatusSaga vio ambos ACKs y cerro el pago como settled.', 'ok')); + } + if (suffix === 'payment.failed.v1') { + emit(serviceEvent('saga', 'failed', event?.payload?.reason || 'consumer failure')); + emit(narrative('StatusSaga cerro failed porque uno de los pasos criticos fallo.', 'warn')); + } + if (suffix.includes('dlt')) { + emit(serviceEvent('dlt', 'failed', event?.reason || 'retry budget exhausted')); + emit(narrative('El mensaje agoto retries y fue a DLT. No se borro silenciosamente.', 'warn')); + } + }, + }); + await sleep(1000); + + try { + emit({ type: 'log', source: 'scenario', level: 'info', message: `Scenario: ${scenario} / country: ${country}` }); + emit(serviceEvent('api', 'running', 'creating payment request')); + emit(serviceEvent('outbox', 'running', 'waiting for local transaction')); + + if (scenario === 'country-isolation') { + const blockedCountry = country === 'pe' ? 'mx' : 'pe'; + emit(narrative(`Aislaremos ${blockedCountry.toUpperCase()} apagando su ledger. El pago se hara en ${country.toUpperCase()} y debe seguir normal.`, 'warn')); + await stopCountryService(blockedCountry, 'ledger-consumer'); + restore.push(() => startCountryService(blockedCountry, 'ledger-consumer')); + emit({ + type: 'country-status', + country: blockedCountry, + availability: 'degraded', + detail: 'ledger-consumer stopped for isolation demo', + }); + emit({ type: 'log', source: 'country', level: 'warn', message: `${blockedCountry} ledger stopped; ${country} should keep processing.` }); + } + + if (scenario === 'mx-outage-pe-available') { + emit(narrative('Simularemos caida critica de MX: ledger y status-saga de MX se apagan. PE debe seguir yapeando.', 'warn')); + await stopCountryService('mx', 'ledger-consumer'); + restore.push(() => startCountryService('mx', 'ledger-consumer')); + await stopCountryService('mx', 'status-saga'); + restore.push(() => startCountryService('mx', 'status-saga')); + emit({ + type: 'country-status', + country: 'mx', + availability: 'degraded', + detail: 'MX ledger-consumer and status-saga stopped', + }); + emit({ + type: 'country-status', + country: 'pe', + availability: 'available', + detail: 'PE critical workers remain available', + }); + emit({ type: 'log', source: 'country', level: 'warn', message: 'MX critical workers stopped; PE payment should still settle.' }); + } + + if (scenario === 'ledger-down-recovery' || scenario === 'timeout-pending') { + emit({ type: 'log', source: 'ledger', level: 'warn', message: `Stopping ledger-consumer for ${country}` }); + await stopCountryService(country, 'ledger-consumer'); + restore.push(() => startCountryService(country, 'ledger-consumer')); + emit({ type: 'step', step: 'ledger', status: 'retrying' }); + emit(serviceEvent('ledger', 'retrying', 'consumer stopped; Kafka keeps the message')); + emit(narrative('Ledger esta caido. El pago no se pierde: Kafka conserva el evento y el status debe seguir pending.', 'warn')); + } + + if (scenario === 'dlt-invalid-payload') { + await publishInvalidPayload(country, emit); + await emitDbSnapshot({ + paymentId: '', + stage: 'DLT direct Kafka injection', + note: 'No se creo payment ni outbox porque este caso salta la API para probar DLT con payload invalido.', + emit, + }); + await sleep(8000); + emit(phoneState('failed', 'Error tecnico controlado', 'El mensaje agoto retries y fue enviado al DLT para investigacion.', 'reset')); + emit({ type: 'summary', level: 'error', message: 'Malformed event should appear in payment.created.v1.dlt.' }); + return; + } + + paymentIdRef.value = await createPayment({ country, walletId, amount, currency, emit }); + emit(serviceEvent('api', 'ok', `paymentId=${paymentIdRef.value}`)); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: 'After local DB transaction', + note: 'PaymentService ya hizo commit de payment + outbox. Kafka todavia no fue llamado dentro de esa transaccion.', + emit, + }); + emit(serviceEvent('outbox', 'running', 'claiming outbox row')); + await pollOutbox({ paymentId: paymentIdRef.value, emit }); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: 'After outbox relay', + note: 'El relay, en otro proceso, publico el evento y marco el outbox como published.', + emit, + }); + + if (scenario === 'ledger-down-recovery') { + await sleep(8000); + const pending = await readStatus(paymentIdRef.value); + emit({ type: 'status', level: 'warn', message: 'Ledger is down, status should still be pending.', status: pending }); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: 'While ledger is down', + note: 'Fraud puede estar listo, pero ledger sigue pendiente; por eso el payment no debe mentir con settled.', + emit, + }); + emit(phoneState('pending', 'Tu yape esta en proceso', 'Ledger esta temporalmente caido; el pago queda pendiente sin perderse.', 'reset')); + emit({ type: 'log', source: 'ledger', level: 'info', message: `Restarting ledger-consumer for ${country}` }); + await startCountryService(country, 'ledger-consumer'); + restore.pop(); + emit({ type: 'step', step: 'ledger', status: 'running' }); + emit(serviceEvent('ledger', 'running', 'consumer restored; waiting for redelivery')); + const finalStatus = await pollStatus({ paymentId: paymentIdRef.value, timeoutMs: 60000, emit }); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: 'After ledger recovery', + note: 'Al volver ledger, Kafka entrega el pendiente y la saga puede cerrar el pago.', + emit, + }); + emit(phoneState( + finalStatus.status === 'settled' ? 'success' : 'failed', + finalStatus.status === 'settled' ? 'Yape exitoso' : 'No pudimos completar tu yape', + finalStatus.status === 'settled' ? 'El pago pendiente se recupero y fue liquidado correctamente.' : 'El pago no pudo cerrarse luego de la recuperacion.', + 'reset', + )); + emit({ type: 'summary', level: finalStatus.status === 'settled' ? 'ok' : 'error', message: `Final status: ${finalStatus.status}` }); + return; + } + + if (scenario === 'timeout-pending') { + const pending = await pollStatus({ paymentId: paymentIdRef.value, timeoutMs: 16000, emit, expectedFinal: false }); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: 'Timeout window ended', + note: 'La consulta de estado es honestamente eventual: muestra pending mientras falta un ACK critico.', + emit, + }); + emit(phoneState('pending', 'Tu yape esta en proceso', 'Aun esperamos confirmacion de un servicio critico. El estado sigue pending honestamente.', 'reset')); + emit({ + type: 'summary', + level: 'warn', + message: `Timeout window ended with status: ${pending.status}. This demonstrates honest eventual consistency.`, + }); + return; + } + + const finalStatus = await pollStatus({ paymentId: paymentIdRef.value, timeoutMs: 60000, emit }); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: `Final DB state: ${finalStatus.status}`, + note: 'Estado persistido final despues de que la saga reconciliara los ACKs de fraud y ledger.', + emit, + }); + emit(phoneState( + finalStatus.status === 'settled' ? 'success' : 'failed', + finalStatus.status === 'settled' ? 'Yape exitoso' : 'No pudimos completar tu yape', + finalStatus.status === 'settled' ? 'Tu pago fue procesado y liquidado correctamente.' : 'El pago fue rechazado o fallo durante la liquidacion.', + 'reset', + )); + + if (scenario === 'idempotency-replay') { + emit(narrative('Ahora republicamos el mismo payment.created.v1 con el mismo eventId. Fraud y ledger deben ignorarlo.', 'warn')); + await publishPayload(topic(country, 'payment.created.v1'), captured.paymentCreatedRaw, emit, 'duplicate payment.created.v1'); + await sleep(5000); + const rows = await processedRows(country, [ + captured.paymentCreatedEventId, + captured.fraudEventId, + captured.ledgerEventId, + ].filter(Boolean)); + + emit({ + type: 'idempotency', + level: 'ok', + message: `Replay enviado. processed_events mantiene una fila por consumer/eventId (${rows.length} marcas encontradas).`, + fraud: 'duplicate payment.created ignored by fraud-consumer', + ledger: 'duplicate payment.created ignored by ledger-consumer', + rows, + }); + await emitDbSnapshot({ + paymentId: paymentIdRef.value, + stage: 'After duplicate replay', + note: 'processed_events mantiene una marca por consumer/eventId; no se crea otro efecto observable.', + emit, + }); + emit(narrative('Este es el punto clave de redelivery: repetir el evento no genera otro scoring ni otro asiento ledger.', 'ok')); + } + + emit({ type: 'summary', level: finalStatus.status === 'settled' ? 'ok' : 'error', message: `Final status: ${finalStatus.status}` }); + } catch (error) { + emit(phoneState('failed', 'No pudimos completar tu yape', error instanceof Error ? error.message : String(error), 'retry')); + emit({ + type: 'summary', + level: 'error', + message: error instanceof Error ? error.message : String(error), + }); + } finally { + for (const action of restore.reverse()) { + try { + await action(); + emit({ type: 'log', source: 'cleanup', level: 'ok', message: 'Service restored for the next demo.' }); + } catch (error) { + emit({ type: 'log', source: 'cleanup', level: 'error', message: error instanceof Error ? error.message : String(error) }); + } + } + + setTimeout(() => { + stopWatchers(); + emit({ type: 'done', message: 'Scenario finished' }); + res.end(); + }, 1200); + } +} + +function serveStatic(req, res) { + const url = new URL(req.url, `http://${req.headers.host}`); + const requestedPath = url.pathname === '/' ? '/index.html' : url.pathname; + const filePath = path.normalize(path.join(PUBLIC_DIR, requestedPath)); + + if (!filePath.startsWith(PUBLIC_DIR)) { + res.writeHead(403); + res.end('Forbidden'); + return; + } + + fs.readFile(filePath, (error, content) => { + if (error) { + res.writeHead(404); + res.end('Not found'); + return; + } + + res.writeHead(200, { 'Content-Type': CONTENT_TYPES[path.extname(filePath)] || 'text/plain; charset=utf-8' }); + res.end(content); + }); +} + +const server = http.createServer(async (req, res) => { + const url = new URL(req.url, `http://${req.headers.host}`); + + try { + if (req.method === 'GET' && url.pathname === '/api/health') { + sendJson(res, 200, await health()); + return; + } + + if (req.method === 'POST' && url.pathname === '/api/scenario') { + await runScenario(req, res); + return; + } + + if (req.method === 'GET') { + serveStatic(req, res); + return; + } + + sendJson(res, 405, { error: 'method_not_allowed' }); + } catch (error) { + sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) }); + } +}); + +server.listen(PORT, () => { + console.log(`Visual demo listening on http://localhost:${PORT}`); +}); diff --git a/test/dlt-handler.spec.ts b/test/dlt-handler.spec.ts new file mode 100644 index 00000000..70b7ab6c --- /dev/null +++ b/test/dlt-handler.spec.ts @@ -0,0 +1,28 @@ +import { ConsumerExecutorService } from 'src/domain/services/consumer-executor.service'; +import { FakeDeadLetterPublisher } from 'test/helpers/fakes'; + +describe('DLT handler', () => { + it('sends event to DLT after retry budget is exhausted', async () => { + const dltPublisher = new FakeDeadLetterPublisher(); + const service = new ConsumerExecutorService(dltPublisher); + + let attempts = 0; + await service.executeWithRetry( + { + consumerName: 'fraud-consumer', + sourceTopic: 'payment.created.v1', + eventId: 'evt-1', + payload: JSON.stringify({ eventId: 'evt-1' }), + }, + 3, + async () => { + attempts += 1; + throw new Error('simulated_failure'); + }, + ); + + expect(attempts).toBe(3); + expect(dltPublisher.dlt).toHaveLength(1); + expect(dltPublisher.dlt[0].originalTopic).toBe('payment.created.v1'); + }); +}); diff --git a/test/helpers/fakes.ts b/test/helpers/fakes.ts new file mode 100644 index 00000000..2c2f7661 --- /dev/null +++ b/test/helpers/fakes.ts @@ -0,0 +1,221 @@ +import { Payment, PaymentStatus } from 'src/domain/entities/payment'; +import { OutboxEvent, OutboxStatus } from 'src/domain/entities/outbox-event'; +import { PaymentStep, StepStatus } from 'src/domain/entities/payment-step'; +import { EnqueueOutboxEventParams, OutboxRepository } from 'src/domain/repositories/outbox.repository'; +import { CreatePaymentParams, PaymentRepository } from 'src/domain/repositories/payment.repository'; +import { PaymentStepRepository } from 'src/domain/repositories/payment-step.repository'; +import { ProcessedEventRepository } from 'src/domain/repositories/processed-event.repository'; +import { DeadLetterPublisher, EventPublisher } from 'src/domain/repositories/ports'; +import { UnitOfWork } from 'src/domain/repositories/unit-of-work'; + +export class FakePaymentRepository implements PaymentRepository { + public readonly items = new Map(); + + async createPending(params: CreatePaymentParams): Promise { + const now = new Date(); + const payment: Payment = { + id: params.id, + walletId: params.walletId, + countryCode: params.countryCode, + amount: params.amount, + currency: params.currency, + status: PaymentStatus.Pending, + createdAt: now, + updatedAt: now, + }; + + this.items.set(params.id, payment); + return payment; + } + + async updateStatus(paymentId: string, status: PaymentStatus): Promise { + const payment = this.items.get(paymentId); + if (!payment) { + return; + } + + payment.status = status; + payment.updatedAt = new Date(); + this.items.set(paymentId, payment); + } + + async getById(paymentId: string): Promise { + return this.items.get(paymentId) ?? null; + } +} + +export class FakePaymentStepRepository implements PaymentStepRepository { + public readonly items = new Map(); + + async init(paymentId: string): Promise { + this.items.set(paymentId, { + paymentId, + fraudStatus: StepStatus.Pending, + ledgerStatus: StepStatus.Pending, + failureReason: null, + updatedAt: new Date(), + }); + } + + async markFraud(paymentId: string, status: StepStatus, reason?: string): Promise { + const step = this.items.get(paymentId); + if (!step) { + return; + } + + step.fraudStatus = status; + step.failureReason = status === StepStatus.Failed ? reason ?? 'fraud_failed' : null; + step.updatedAt = new Date(); + this.items.set(paymentId, step); + } + + async markLedger(paymentId: string, status: StepStatus, reason?: string): Promise { + const step = this.items.get(paymentId); + if (!step) { + return; + } + + step.ledgerStatus = status; + step.failureReason = status === StepStatus.Failed ? reason ?? 'ledger_failed' : step.failureReason; + step.updatedAt = new Date(); + this.items.set(paymentId, step); + } + + async getByPaymentId(paymentId: string): Promise { + return this.items.get(paymentId) ?? null; + } +} + +export class FakeOutboxRepository implements OutboxRepository { + public readonly items = new Map(); + public failEnqueue = false; + + async enqueue(params: EnqueueOutboxEventParams): Promise { + if (this.failEnqueue) { + throw new Error('outbox_enqueue_failed'); + } + + const now = new Date(); + this.items.set(params.id, { + id: params.id, + aggregateId: params.aggregateId, + eventId: params.eventId, + eventType: params.eventType, + topic: params.topic, + countryCode: params.countryCode, + payload: params.payload, + status: OutboxStatus.Pending, + attempts: 0, + nextAttemptAt: now, + publishedAt: null, + createdAt: now, + updatedAt: now, + }); + } + + async lockPendingBatch(limit: number, now: Date): Promise { + return Array.from(this.items.values()) + .filter((item) => item.status === OutboxStatus.Pending && item.nextAttemptAt <= now) + .slice(0, limit); + } + + async markPublished(outboxId: string, publishedAt: Date): Promise { + const event = this.items.get(outboxId); + if (!event) { + return; + } + + event.status = OutboxStatus.Published; + event.publishedAt = publishedAt; + event.attempts += 1; + this.items.set(outboxId, event); + } + + async markForRetry(outboxId: string, nextAttemptAt: Date): Promise { + const event = this.items.get(outboxId); + if (!event) { + return; + } + + event.status = OutboxStatus.Pending; + event.nextAttemptAt = nextAttemptAt; + event.attempts += 1; + this.items.set(outboxId, event); + } +} + +export class FakeProcessedEventRepository implements ProcessedEventRepository { + private readonly keys = new Set(); + + async tryMarkProcessed(consumerName: string, countryCode: string, eventId: string): Promise { + const key = `${consumerName}:${countryCode.toLowerCase()}:${eventId}`; + if (this.keys.has(key)) { + return false; + } + + this.keys.add(key); + return true; + } +} + +export class FakeUnitOfWork implements UnitOfWork { + constructor( + private readonly paymentRepository: FakePaymentRepository, + private readonly paymentStepRepository: FakePaymentStepRepository, + private readonly outboxRepository: FakeOutboxRepository, + ) {} + + async execute(work: () => Promise): Promise { + const paymentSnapshot = new Map(this.paymentRepository.items); + const stepSnapshot = new Map(this.paymentStepRepository.items); + const outboxSnapshot = new Map(this.outboxRepository.items); + + try { + return await work(); + } catch (error) { + this.paymentRepository.items.clear(); + this.paymentStepRepository.items.clear(); + this.outboxRepository.items.clear(); + + for (const [key, value] of paymentSnapshot) { + this.paymentRepository.items.set(key, value); + } + + for (const [key, value] of stepSnapshot) { + this.paymentStepRepository.items.set(key, value); + } + + for (const [key, value] of outboxSnapshot) { + this.outboxRepository.items.set(key, value); + } + + throw error; + } + } +} + +export class FakeEventPublisher implements EventPublisher { + public readonly published: Array<{ topic: string; payload: string; key?: string }> = []; + + async publish(topic: string, payload: string, key?: string): Promise { + this.published.push({ topic, payload, key }); + } +} + +export class FakeDeadLetterPublisher implements DeadLetterPublisher { + public readonly dlt: Array<{ + originalTopic: string; + payload: string; + reason: string; + sourceEventId: string; + }> = []; + + async publishDeadLetter( + originalTopic: string, + payload: string, + reason: string, + sourceEventId: string, + ): Promise { + this.dlt.push({ originalTopic, payload, reason, sourceEventId }); + } +} diff --git a/test/idempotency-consumers.spec.ts b/test/idempotency-consumers.spec.ts new file mode 100644 index 00000000..f7d974bf --- /dev/null +++ b/test/idempotency-consumers.spec.ts @@ -0,0 +1,72 @@ +import { EventTypes } from 'src/domain/events/event-types'; +import { FraudConsumerService } from 'src/domain/services/fraud-consumer.service'; +import { LedgerConsumerService } from 'src/domain/services/ledger-consumer.service'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; +import { FakeEventPublisher, FakeProcessedEventRepository } from 'test/helpers/fakes'; + +const paymentCreatedEvent = JSON.stringify({ + eventId: 'evt-100', + type: EventTypes.PaymentCreatedV1, + aggregateId: 'payment-1', + occurredAt: new Date().toISOString(), + countryCode: 'PE', + schemaVersion: 1, + payload: { + paymentId: 'payment-1', + walletId: 'wallet-1', + amount: 100, + currency: 'PEN', + }, +}); + +const paymentCreatedEventMxSameId = JSON.stringify({ + eventId: 'evt-100', + type: EventTypes.PaymentCreatedV1, + aggregateId: 'payment-2', + occurredAt: new Date().toISOString(), + countryCode: 'MX', + schemaVersion: 1, + payload: { + paymentId: 'payment-2', + walletId: 'wallet-2', + amount: 150, + currency: 'MXN', + }, +}); + +describe('Idempotent consumers', () => { + it('fraud consumer should not publish duplicate side effects', async () => { + const processed = new FakeProcessedEventRepository(); + const publisher = new FakeEventPublisher(); + const service = new FraudConsumerService(processed, publisher, new TopicResolver(false)); + + await service.handlePaymentCreated(paymentCreatedEvent); + await service.handlePaymentCreated(paymentCreatedEvent); + + expect(publisher.published).toHaveLength(1); + }); + + it('ledger consumer should not publish duplicate side effects', async () => { + const processed = new FakeProcessedEventRepository(); + const publisher = new FakeEventPublisher(); + const service = new LedgerConsumerService(processed, publisher, new TopicResolver(false)); + + await service.handlePaymentCreated(paymentCreatedEvent); + await service.handlePaymentCreated(paymentCreatedEvent); + + expect(publisher.published).toHaveLength(1); + }); + + it('fraud consumer should process same eventId for different countries independently', async () => { + const processed = new FakeProcessedEventRepository(); + const publisher = new FakeEventPublisher(); + const service = new FraudConsumerService(processed, publisher, new TopicResolver(true)); + + await service.handlePaymentCreated(paymentCreatedEvent); + await service.handlePaymentCreated(paymentCreatedEventMxSameId); + + expect(publisher.published).toHaveLength(2); + expect(publisher.published[0].topic).toBe('pe.payments.fraud.assessed.v1'); + expect(publisher.published[1].topic).toBe('mx.payments.fraud.assessed.v1'); + }); +}); diff --git a/test/payment-service.spec.ts b/test/payment-service.spec.ts new file mode 100644 index 00000000..c8c759c6 --- /dev/null +++ b/test/payment-service.spec.ts @@ -0,0 +1,55 @@ +import { PaymentStatus } from 'src/domain/entities/payment'; +import { PaymentService } from 'src/domain/services/payment.service'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; +import { + FakeOutboxRepository, + FakePaymentRepository, + FakePaymentStepRepository, + FakeUnitOfWork, +} from 'test/helpers/fakes'; + +describe('PaymentService', () => { + it('stores payment and outbox in one local transaction', async () => { + const payments = new FakePaymentRepository(); + const steps = new FakePaymentStepRepository(); + const outbox = new FakeOutboxRepository(); + const uow = new FakeUnitOfWork(payments, steps, outbox); + + const service = new PaymentService(uow, payments, steps, outbox, new TopicResolver(false)); + + const result = await service.createPayment({ + walletId: 'wallet-01', + countryCode: 'PE', + amount: 250, + currency: 'PEN', + }); + + expect(result.status).toBe(PaymentStatus.Pending); + expect(payments.items.size).toBe(1); + expect(steps.items.size).toBe(1); + expect(outbox.items.size).toBe(1); + }); + + it('rolls back payment write when outbox enqueue fails', async () => { + const payments = new FakePaymentRepository(); + const steps = new FakePaymentStepRepository(); + const outbox = new FakeOutboxRepository(); + outbox.failEnqueue = true; + + const uow = new FakeUnitOfWork(payments, steps, outbox); + const service = new PaymentService(uow, payments, steps, outbox, new TopicResolver(false)); + + await expect( + service.createPayment({ + walletId: 'wallet-01', + countryCode: 'PE', + amount: 250, + currency: 'PEN', + }), + ).rejects.toThrow('outbox_enqueue_failed'); + + expect(payments.items.size).toBe(0); + expect(steps.items.size).toBe(0); + expect(outbox.items.size).toBe(0); + }); +}); diff --git a/test/status-saga.spec.ts b/test/status-saga.spec.ts new file mode 100644 index 00000000..4963177f --- /dev/null +++ b/test/status-saga.spec.ts @@ -0,0 +1,97 @@ +import { PaymentStatus } from 'src/domain/entities/payment'; +import { EventTypes } from 'src/domain/events/event-types'; +import { StatusSagaService } from 'src/domain/services/status-saga.service'; +import { TopicResolver } from 'src/domain/services/topic-resolver'; +import { + FakeEventPublisher, + FakePaymentRepository, + FakePaymentStepRepository, + FakeProcessedEventRepository, +} from 'test/helpers/fakes'; + +describe('StatusSagaService', () => { + it('marks payment as settled when both downstream consumers succeed', async () => { + const processed = new FakeProcessedEventRepository(); + const payments = new FakePaymentRepository(); + const steps = new FakePaymentStepRepository(); + const publisher = new FakeEventPublisher(); + + await payments.createPending({ + id: 'payment-1', + walletId: 'wallet-1', + countryCode: 'PE', + amount: 20, + currency: 'PEN', + }); + await steps.init('payment-1'); + + const service = new StatusSagaService(processed, payments, steps, publisher, new TopicResolver(false)); + + await service.onFraudAssessed( + JSON.stringify({ + eventId: 'evt-fraud', + type: EventTypes.FraudAssessedV1, + aggregateId: 'payment-1', + countryCode: 'PE', + payload: { + paymentId: 'payment-1', + approved: true, + }, + }), + ); + + await service.onLedgerPosted( + JSON.stringify({ + eventId: 'evt-ledger', + type: EventTypes.LedgerPostedV1, + aggregateId: 'payment-1', + countryCode: 'PE', + payload: { + paymentId: 'payment-1', + success: true, + entryId: 'entry-1', + }, + }), + ); + + const payment = await payments.getById('payment-1'); + expect(payment?.status).toBe(PaymentStatus.Settled); + expect(publisher.published.some((event) => event.topic === EventTypes.PaymentSettledV1)).toBe(true); + }); + + it('marks payment as failed when one step fails', async () => { + const processed = new FakeProcessedEventRepository(); + const payments = new FakePaymentRepository(); + const steps = new FakePaymentStepRepository(); + const publisher = new FakeEventPublisher(); + + await payments.createPending({ + id: 'payment-2', + walletId: 'wallet-2', + countryCode: 'PE', + amount: 1200, + currency: 'PEN', + }); + await steps.init('payment-2'); + + const service = new StatusSagaService(processed, payments, steps, publisher, new TopicResolver(false)); + + await service.onFraudAssessed( + JSON.stringify({ + eventId: 'evt-fraud-2', + type: EventTypes.FraudAssessedV1, + aggregateId: 'payment-2', + countryCode: 'PE', + payload: { + paymentId: 'payment-2', + approved: false, + reason: 'risk_threshold_exceeded', + }, + }), + ); + + const payment = await payments.getById('payment-2'); + expect(payment?.status).toBe(PaymentStatus.Failed); + expect(publisher.published.some((event) => event.topic === EventTypes.PaymentFailedV1)).toBe(true); + }); +}); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..c0972e5f --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true + }, + "exclude": ["test", "**/*.spec.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..3f125fc2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2021", + "moduleResolution": "node", + "strict": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true, + "outDir": "dist", + "baseUrl": ".", + "paths": { + "src/*": ["src/*"] + } + }, + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": ["node_modules", "dist"] +}