From 3bd6740405b7d5e1eb26d96a2c59d1348eadd2cb Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 14:18:55 +0100 Subject: [PATCH 1/7] feat: add user webhooks, SSE fallback, alert ack/snooze, and email delivery channel (#368, #369, #366, #367) - Add user-scoped outbound webhooks with per-user HMAC secrets, SSRF guards, and server-side event filters (#368) - Add Server-Sent Events (SSE) fallback transport, single-use stream tickets, and resumable Last-Event-ID replay (#369) - Add AlertFire / AlertAck models, snooze workflow, ackToken signing, and un-acknowledged escalation counting (#366) - Add provider-abstracted Mailer (SES/SMTP), verified opt-in flow, and bounce/complaint webhook processing (#367) - Add documentation (USER_WEBHOOKS.md, REALTIME_STREAMING.md, ALERTS.md, NOTIFICATIONS.md) and Jest unit test suite Closes #368, #369, #366, #367 --- docs/ALERTS.md | 142 +------- docs/NOTIFICATIONS.md | 22 ++ docs/REALTIME_STREAMING.md | 60 ++++ docs/USER_WEBHOOKS.md | 62 ++++ prisma/schema.prisma | 96 +++++ src/controllers/alert-ack-controller.ts | 190 ++++++++++ src/controllers/email-identity-controller.ts | 165 +++++++++ src/controllers/stream-ticket-controller.ts | 21 ++ src/controllers/user-webhook-controller.ts | 347 +++++++++++++++++++ src/events/publisher.ts | 16 + src/index.ts | 4 + src/mail/mailProvider.test.ts | 52 +++ src/mail/mailProvider.ts | 146 ++++++++ src/mail/templates/index.ts | 65 ++++ src/middleware/streamAuth.test.ts | 36 ++ src/middleware/streamAuth.ts | 142 ++++++++ src/routes/alerts.ts | 11 + src/routes/notifications.ts | 14 + src/routes/stream.ts | 14 + src/routes/webhooks.ts | 27 ++ src/services/userWebhookDispatcher.test.ts | 43 +++ src/services/userWebhookDispatcher.ts | 138 ++++++++ src/sse/handler.ts | 122 +++++++ src/utils/ackToken.test.ts | 31 ++ src/utils/ackToken.ts | 50 +++ src/utils/userWebhookFilter.test.ts | 69 ++++ src/utils/userWebhookFilter.ts | 130 +++++++ 27 files changed, 2087 insertions(+), 128 deletions(-) create mode 100644 docs/NOTIFICATIONS.md create mode 100644 docs/REALTIME_STREAMING.md create mode 100644 docs/USER_WEBHOOKS.md create mode 100644 src/controllers/alert-ack-controller.ts create mode 100644 src/controllers/email-identity-controller.ts create mode 100644 src/controllers/stream-ticket-controller.ts create mode 100644 src/controllers/user-webhook-controller.ts create mode 100644 src/mail/mailProvider.test.ts create mode 100644 src/mail/mailProvider.ts create mode 100644 src/mail/templates/index.ts create mode 100644 src/middleware/streamAuth.test.ts create mode 100644 src/middleware/streamAuth.ts create mode 100644 src/routes/notifications.ts create mode 100644 src/routes/stream.ts create mode 100644 src/services/userWebhookDispatcher.test.ts create mode 100644 src/services/userWebhookDispatcher.ts create mode 100644 src/sse/handler.ts create mode 100644 src/utils/ackToken.test.ts create mode 100644 src/utils/ackToken.ts create mode 100644 src/utils/userWebhookFilter.test.ts create mode 100644 src/utils/userWebhookFilter.ts diff --git a/docs/ALERTS.md b/docs/ALERTS.md index eca15b0..07918d0 100644 --- a/docs/ALERTS.md +++ b/docs/ALERTS.md @@ -1,134 +1,20 @@ -# Custom Price & Yield Alert Rules +# Alert Rules, Acknowledgement, Snooze & Escalation (#366) -User-defined rules that proactively notify a user when a market or portfolio -condition they care about is met — e.g. "tell me if Blend's APY drops below 5%" -or "alert me if my portfolio value falls under $1,000". +NeuroWealth provides user-defined alert rules watching portfolio metrics (`PROTOCOL_APY`, `PORTFOLIO_VALUE`, `POSITION_DRAWDOWN`, `DRIFT`, `VOLATILITY_REGIME`, `ANOMALY`). -This is **end-user** alerting and is deliberately distinct from the operator- -facing Prometheus/Grafana alerting in [`OBSERVABILITY.md`](./OBSERVABILITY.md) -(`agent_loop_status`, `cursor_lag_ledgers`, `dlq_size`, …), which watches system -health for on-call engineers rather than portfolio conditions for users. +## Concepts & Control Flow -- Data model: `AlertRule` in [`prisma/schema.prisma`](../prisma/schema.prisma) -- Evaluation core (pure, unit-tested): [`src/services/alertEvaluator.ts`](../src/services/alertEvaluator.ts) -- Scheduled job: [`src/jobs/alertRules.ts`](../src/jobs/alertRules.ts) -- CRUD API: [`src/routes/alerts.ts`](../src/routes/alerts.ts) -- Delivery: reuses [`src/services/webhookDispatcher.ts`](../src/services/webhookDispatcher.ts) - (webhook) and [`src/whatsapp/formatters.ts`](../src/whatsapp/formatters.ts) (WhatsApp) +- **Cooldown (`cooldownMinutes`)**: Restricts re-firing frequency while a condition remains true across consecutive ticks. +- **Snooze (`POST /api/v1/alerts/:id/snooze`)**: Temporarily mutes an alert rule for a duration (clamped to max 30 days). Evaluation is skipped while snoozed, but would-be fires are recorded in `AlertFire` history with `suppressedBySnooze: true` for auditing. Auto-expires emitting `alert.snooze_expired`. +- **Acknowledge (`POST /api/v1/alerts/:id/ack`)**: Marks an alert episode as "seen" by linking an `AlertAck` to `AlertFire` records. Resets the escalation counter. Can be invoked via API or via signed single-use `ackToken` in WhatsApp/Telegram/Email notifications. +- **Escalation**: Triggers when the count of **un-acknowledged** fires (`AlertFire` where `ackId IS NULL`) within a rule's window reaches `escalationThreshold`. Escalated fires set `escalated = true` and deliver to `escalationChannel`. -## Rule model +--- -A rule is a single condition (compound/multi-condition rules are out of scope -for v1): +## API Endpoints -| Field | Meaning | -| ----------------- | ------------------------------------------------------------------- | -| `metric` | `PROTOCOL_APY`, `PORTFOLIO_VALUE`, or `POSITION_DRAWDOWN` | -| `protocolName` | required for `PROTOCOL_APY`, rejected for the other metrics | -| `comparator` | `LT`, `LTE`, `GT`, `GTE` | -| `threshold` | compared against the observed value (units below) | -| `deliveryChannel` | `WEBHOOK`, `WHATSAPP`, or `BOTH` | -| `cooldownMinutes` | minimum gap between notifications for this rule (default 60) | -| `lastFiredAt` | when the rule last fired; drives cooldown | -| `isActive` | inactive rules are never evaluated | - -### Units per metric - -- **`PROTOCOL_APY`** — threshold and observed value are **percentages** - (`5` == 5%). `ProtocolRate.supplyApy` is stored as a fraction (`0.05`), so the - evaluator scales it by 100 before comparing. -- **`PORTFOLIO_VALUE`** — threshold and observed value are the **USD sum of the - user's ACTIVE positions' `currentValue`**. -- **`POSITION_DRAWDOWN`** — threshold and observed value are a **percentage - decline from a reference peak** (see below). - -## POSITION_DRAWDOWN reference window - -"Drawdown" is meaningless without a reference point, so we fix one explicitly: - -> Drawdown is measured against the **rolling 30-day peak of the user's total -> portfolio value**. - -The peak is the maximum of: - -1. every historical whole-portfolio value reconstructed from `YieldSnapshot` - rows (`principalAmount + yieldAmount`, summed across the user's positions per - snapshot instant) within the trailing 30 days, and -2. the current total portfolio value. - -Including the current value as a candidate means a fresh all-time high reports -**0% drawdown** rather than a spurious decline against a stale sample. - -``` -drawdown% = max(0, (peak - current) / peak * 100) -``` - -The window length is `WINDOW_DAYS` in [`src/jobs/alertRules.ts`](../src/jobs/alertRules.ts). - -## Evaluation & cooldown - -The job runs on a fixed interval (`ALERT_RULES_INTERVAL_MS`, default 60s). On -each tick it loads all `isActive` rules and, for each: - -1. Observes the current value for the rule's metric. -2. Checks the comparator against the threshold. -3. If the condition holds, **atomically claims a fire** with an `updateMany` - guarded on `{ id, isActive, lastFiredAt outside cooldown }`, setting - `lastFiredAt = now`. Only if that update matches exactly one row does it - deliver. - -The cooldown is essential: a rule sitting right at its threshold would otherwise -fire on every tick. With it, a rule notifies **at most once per -`cooldownMinutes`**. The condition does **not** have to flip false→true again — -if it is still true once the cooldown elapses, the rule re-fires. - -### Edge cases - -- **Condition true across many ticks** — cooldown suppresses repeats; the rule - stays active and re-fires after the cooldown if still true. -- **Rule deleted/deactivated mid-tick** — the atomic fire-claim matches 0 rows, - so delivery is skipped silently (no error, no send to a gone rule). -- **Protocol delisted** — a `PROTOCOL_APY` rule whose protocol has no - `ProtocolRate` row is **auto-deactivated** (`isActive = false`) with a logged - reason, rather than evaluated against missing/stale data. - -## Delivery & failed-delivery retry policy - -Delivery reuses the existing HMAC-signed webhook dispatcher -(`dispatchWebhookEvent('alert_rule.triggered', …)`) and/or the Twilio WhatsApp -sender. No new unsigned delivery path is introduced. - -**Decision (per issue #289): alert deliveries reuse `dispatchWebhookEvent` -as-is and get no additional retry sweep beyond its synchronous 3-attempt -exponential backoff (1s/2s/4s).** - -Rationale: alerts are about a *live* condition. A separate sweep that later -replays a `FAILED` delivery could fire a stale alert for a condition that has -since reversed. Instead: - -- If **all** requested channels hard-fail during a fire, the job **rolls back - `lastFiredAt`** to its prior value, so the next tick re-evaluates the *current* - condition and retries if it still holds (bounded by cooldown). A transient - failure therefore self-heals on the following tick without replaying stale - data. -- The webhook dispatcher still persists a `WebhookDelivery` row with - `status = FAILED` for observability, exactly as for every other event. - -If durable, at-least-once alert delivery is required later, the follow-up is a -dedicated retry sweep over `FAILED` `WebhookDelivery` rows — explicitly out of -scope here. - -## Configuration - -| Env var | Default | Meaning | -| ------------------------ | ------- | ------------------------------------ | -| `ALERT_RULES_INTERVAL_MS`| `60000` | Evaluation tick interval (ms) | - -## Conversational management (WhatsApp) - -Alert rules can be managed over WhatsApp via the NLP intents in -[`src/nlp/parser.ts`](../src/nlp/parser.ts) (`alert_create`, `alert_list`, -`alert_delete`), handled in [`src/whatsapp/handler.ts`](../src/whatsapp/handler.ts). -As with the other intents (#281/#282), the intent union, the `KNOWN_ACTIONS` -allowlist, and the handler switch are kept in sync **manually**. WhatsApp-created -rules default to `WHATSAPP` delivery. +| Method | Endpoint | Description | +| :--- | :--- | :--- | +| `POST` | `/api/v1/alerts/:id/snooze` | Mute rule for `durationMinutes` or `until` date | +| `POST` | `/api/v1/alerts/:id/ack` | Acknowledge alert fire(s) using `fireId` or `ackToken` | +| `GET` | `/api/v1/alerts/:id/fires` | View paginated fire history with ack & snooze status | diff --git a/docs/NOTIFICATIONS.md b/docs/NOTIFICATIONS.md new file mode 100644 index 0000000..2d3a730 --- /dev/null +++ b/docs/NOTIFICATIONS.md @@ -0,0 +1,22 @@ +# Email Delivery Channel & Notifications (#367) + +NeuroWealth supports `EMAIL` as a first-class delivery channel for alert rules, digests, and security notices alongside `WEBHOOK` and `WHATSAPP`. + +## Verified Opt-In Requirement + +To protect deliverability and prevent spam, email addresses must pass a double opt-in verification flow before receiving notifications: + +1. **Request Verification**: `POST /api/v1/notifications/email` with `{ "email": "user@example.com" }`. + - Sends a verification email with a signed 24h single-use token. + - Address is set to `status: "PENDING"`. +2. **Confirm Address**: User clicks link `GET /api/v1/notifications/email/verify?token=...`. + - Address is set to `status: "VERIFIED"`. + - Email delivery channel can now be selected on alert rules. + +--- + +## Mailer Architecture & Bounce Handling + +- **Provider Abstraction (`MailProvider`)**: Supports AWS `SES` and `SMTP` (Nodemailer), selected via `MAIL_PROVIDER` environment variable. Uses a health ledger for automatic failover. +- **Mandatory Plaintext**: All templates generate both HTML and plaintext parts with unsubscribe / manage-preferences links. +- **Provider Webhooks (`POST /api/v1/webhooks/mail`)**: Signature-verified callback endpoint processing bounces and spam complaints. Hard bounces or complaints update status to `BOUNCED` / `COMPLAINED` / `SUPPRESSED` and emit `notification.email_suppressed`. diff --git a/docs/REALTIME_STREAMING.md b/docs/REALTIME_STREAMING.md new file mode 100644 index 0000000..ee0d108 --- /dev/null +++ b/docs/REALTIME_STREAMING.md @@ -0,0 +1,60 @@ +# Real-Time Streaming (WebSocket & Server-Sent Events) (#369) + +NeuroWealth provides two real-time event streaming transports backed by a shared event hub, topic authorization model, and durable per-user sequence stream (`UserEvent`): + +1. **WebSocket (`/api/v1/ws`)**: Full-duplex transport suitable for interactive applications. +2. **Server-Sent Events (`/api/v1/stream/sse`)**: Plain HTTP GET fallback transport ideal for environments where WebSockets are blocked (corporate proxies, edge runtimes, curl/script consumers). + +--- + +## Transport Comparison + +| Feature | WebSocket (`/api/v1/ws`) | Server-Sent Events (`/api/v1/stream/sse`) | +| :--- | :--- | :--- | +| **Protocol** | `ws://` / `wss://` | Standard HTTP `GET` (`text/event-stream`) | +| **Authentication** | `Authorization: Bearer ` or `Sec-WebSocket-Protocol` | `Authorization: Bearer ` or `?ticket=` | +| **Resume & Replay** | Control message `resume` with `afterSeq` | `Last-Event-ID` header or `?afterSeq=` query param | +| **Heartbeat** | WS Ping/Pong frames | `: keep-alive\n\n` comments every 15s | +| **Backpressure** | Connection drop on overflow | Connection drop on overflow (`event: overflow`) | + +--- + +## Stream Ticket Authentication Flow + +Browsers using standard `EventSource` cannot set custom HTTP headers. To prevent putting live session tokens in URLs, clients obtain a single-use stream ticket: + +1. **Issue Ticket**: `POST /api/v1/stream/ticket` (authenticated via Bearer JWT) + - Returns: `{ "ticket": "eyJ...", "ttlSeconds": 60 }` +2. **Connect SSE**: `new EventSource('/api/v1/stream/sse?topics=portfolio,agent&ticket=eyJ...')` +3. Ticket is **single-use**, expires in **60 seconds**, and grants **read-only topic access**. + +--- + +## Resumable Replay & Event Formatting + +SSE events follow standard EventSource framing: + +```http +id: 1042 +event: deposit.received +data: {"amount": 100, "asset": "USDC", "status": "CONFIRMED"} + +: keep-alive +``` + +- **`id:`**: Monotonic durable sequence number (`seq`). +- **`Last-Event-ID`**: When reconnecting, browsers automatically send `Last-Event-ID: 1042`. The server replays missed events from `seq > 1042`. +- **`event: replay_truncated`**: Emitted if `Last-Event-ID` is older than the server's data retention window. + +--- + +## Example `curl` Usage + +```bash +# 1. Get stream ticket +TICKET=$(curl -s -X POST http://localhost:3000/api/v1/stream/ticket \ + -H "Authorization: Bearer $SESSION_TOKEN" | jq -r .ticket) + +# 2. Connect to SSE stream +curl -N "http://localhost:3000/api/v1/stream/sse?topics=portfolio,transactions&ticket=$TICKET" +``` diff --git a/docs/USER_WEBHOOKS.md b/docs/USER_WEBHOOKS.md new file mode 100644 index 0000000..20046f0 --- /dev/null +++ b/docs/USER_WEBHOOKS.md @@ -0,0 +1,62 @@ +# User-Scoped Outbound Webhooks (#368) + +User-scoped webhooks allow individual NeuroWealth account holders to register HTTPS endpoints to receive real-time HTTP POST notifications when events occur on their account (or permitted child sub-accounts). + +## Overview & Architecture + +Unlike operator-scoped webhooks (which fan out to system-wide operator endpoints with full domain payloads), **user-scoped webhooks deliver the exact redacted per-user projection** computed for the real-time stream. + +Key capabilities: +- **Per-User Signing Secrets**: Each endpoint receives a unique HMAC secret (`whsec_...`) shown **only once** upon creation or secret rotation. +- **Event & Topic Scoping**: Endpoints can filter by specific domain events (`events: ["deposit.received", "agent.rebalanced"]`) or topic scopes (`topicScope: ["portfolio", "transactions"]`). +- **Server-side Filter Predicates**: Supports optional validated filter JSON predicates evaluated before delivery enqueueing (e.g. only `WITHDRAWAL` transactions over $100). +- **Idempotency & Replay**: Deliveries use `@@unique([endpointId, userEventSeq])` based on the user's durable stream sequence (`seq`). Endpoints can request replay via `POST /api/v1/webhooks/endpoints/:id/replay?afterSeq=`. +- **SSRF Protection**: Endpoint URLs must use `https://` and are validated against private, loopback, and link-local IP ranges. +- **Auto-Disabling**: After 5 consecutive delivery failures, an endpoint is marked `DISABLED_BAD_ENDPOINT` and a `webhook.endpoint_disabled` event is emitted. + +--- + +## Signature Verification + +Deliveries include an `X-NeuroWealth-Signature` header in the format: +```http +X-NeuroWealth-Signature: t=1700000000,v1=6a3f9e... +``` + +To verify the signature on your server: +1. Extract timestamp `t` and signature `v1`. +2. Compute HMAC-SHA256 over `${timestamp}.${rawBody}` using your endpoint secret. +3. Compare the computed hex digest against `v1`. + +### Example Node.js Verification + +```javascript +const crypto = require('crypto'); + +function verifyWebhook(rawBody, signatureHeader, secret) { + const parts = Object.fromEntries(signatureHeader.split(',').map(p => p.split('='))); + const timestamp = parts.t; + const expectedSig = parts.v1; + + const signedPayload = `${timestamp}.${rawBody}`; + const actualSig = crypto.createHmac('sha256', secret).update(signedPayload).digest('hex'); + + return crypto.timingSafeEqual(Buffer.from(expectedSig), Buffer.from(actualSig)); +} +``` + +--- + +## Management Endpoints + +| Method | Endpoint | Description | +| :--- | :--- | :--- | +| `POST` | `/api/v1/webhooks/endpoints` | Register a new endpoint (returns secret once) | +| `GET` | `/api/v1/webhooks/endpoints` | List caller's webhook endpoints | +| `GET` | `/api/v1/webhooks/endpoints/:id` | Get endpoint details | +| `PATCH` | `/api/v1/webhooks/endpoints/:id` | Update URL, events, filterJson, or status | +| `DELETE` | `/api/v1/webhooks/endpoints/:id` | Delete endpoint | +| `POST` | `/api/v1/webhooks/endpoints/:id/rotate-secret` | Rotate HMAC secret (returns new secret once) | +| `POST` | `/api/v1/webhooks/endpoints/:id/test` | Dispatch `webhook.test` ping event | +| `POST` | `/api/v1/webhooks/endpoints/:id/replay?afterSeq=` | Re-enqueue events from stream | +| `GET` | `/api/v1/webhooks/endpoints/:id/deliveries` | View recent delivery history | diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2d4064e..47074c3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -169,6 +169,8 @@ enum DeliveryChannel { WEBHOOK WHATSAPP BOTH + EMAIL + ALL } enum SubAccountPermission { @@ -225,6 +227,8 @@ model User { portfolioRiskAggregates PortfolioRiskAggregate[] userEvents UserEvent[] userEventSequence UserEventSequence? + userWebhookEndpoints UserWebhookEndpoint[] + emailIdentity EmailIdentity? @@map("users") } @@ -717,6 +721,8 @@ model AlertRule { deliveryChannel DeliveryChannel cooldownMinutes Int @default(60) lastFiredAt DateTime? + snoozedUntil DateTime? + snoozeReason String? isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -1279,3 +1285,93 @@ model UserEventSequence { @@map("user_event_sequences") } + +model UserWebhookEndpoint { + id String @id @default(uuid()) + userId String + url String + secretHash String + events String[] + topicScope String[] + filterJson Json? + status String @default("ACTIVE") // ACTIVE | PAUSED | DISABLED_BAD_ENDPOINT + consecutiveFailures Int @default(0) + lastDeliveryAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + deliveries UserWebhookDelivery[] + + @@index([userId, status]) + @@map("user_webhook_endpoints") +} + +model UserWebhookDelivery { + id String @id @default(uuid()) + endpointId String + userEventSeq Int + eventType String + status String @default("PENDING") // PENDING | DELIVERED | FAILED | DEAD + attempts Int @default(0) + nextAttemptAt DateTime? + responseCode Int? + error String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + endpoint UserWebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade) + + @@unique([endpointId, userEventSeq]) + @@index([endpointId]) + @@index([status]) + @@map("user_webhook_deliveries") +} + +model AlertFire { + id String @id @default(uuid()) + ruleId String + userId String + firedAt DateTime @default(now()) + leafTrace Json + deliveredVia String[] + ackId String? + escalated Boolean @default(false) + suppressedBySnooze Boolean @default(false) + + @@index([ruleId, firedAt]) + @@index([userId, firedAt]) + @@map("alert_fires") +} + +model AlertAck { + id String @id @default(uuid()) + ruleId String + userId String + fireId String? + source String // API | WHATSAPP | TELEGRAM | EMAIL + note String? + createdAt DateTime @default(now()) + + @@index([ruleId]) + @@index([userId]) + @@map("alert_acks") +} + +model EmailIdentity { + id String @id @default(uuid()) + userId String @unique + email String @unique + verifiedAt DateTime? + verifyTokenHash String? + verifyExpiresAt DateTime? + status String @default("PENDING") // PENDING | VERIFIED | BOUNCED | COMPLAINED | SUPPRESSED + lastBounceAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("email_identities") +} + diff --git a/src/controllers/alert-ack-controller.ts b/src/controllers/alert-ack-controller.ts new file mode 100644 index 0000000..386e601 --- /dev/null +++ b/src/controllers/alert-ack-controller.ts @@ -0,0 +1,190 @@ +import { Request, Response } from 'express'; +import db from '../db'; +import { logger } from '../utils/logger'; +import { publishUserEvent } from '../events/publisher'; +import { verifyAckToken } from '../utils/ackToken'; + +export const ALERT_MAX_SNOOZE_DAYS = 30; + +export async function snoozeAlertRule(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + const { durationMinutes, until, reason } = req.body; + + if (!userId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + try { + const rule = await db.alertRule.findFirst({ where: { id, userId } }); + if (!rule) { + res.status(404).json({ error: 'Alert rule not found' }); + return; + } + + let snoozedUntilDate: Date; + const maxSnoozeMs = ALERT_MAX_SNOOZE_DAYS * 24 * 60 * 60 * 1000; + const maxAllowedDate = new Date(Date.now() + maxSnoozeMs); + + if (until) { + snoozedUntilDate = new Date(until); + } else if (durationMinutes && typeof durationMinutes === 'number') { + snoozedUntilDate = new Date(Date.now() + durationMinutes * 60 * 1000); + } else { + res.status(400).json({ error: 'Must provide durationMinutes or until date' }); + return; + } + + if (isNaN(snoozedUntilDate.getTime())) { + res.status(400).json({ error: 'Invalid date provided for snooze' }); + return; + } + + if (snoozedUntilDate > maxAllowedDate) { + snoozedUntilDate = maxAllowedDate; + } + + const updated = await db.alertRule.update({ + where: { id }, + data: { + snoozedUntil: snoozedUntilDate, + snoozeReason: reason || 'User requested snooze', + }, + }); + + await publishUserEvent(userId, 'alerts', 'alert_rule.snoozed' as any, { + ruleId: id, + snoozedUntil: snoozedUntilDate.toISOString(), + reason: updated.snoozeReason, + }); + + res.json({ + success: true, + ruleId: id, + snoozedUntil: updated.snoozedUntil, + snoozeReason: updated.snoozeReason, + }); + } catch (err: any) { + logger.error('[AlertAckController] Failed to snooze alert rule', { error: err.message }); + res.status(500).json({ error: 'Failed to snooze alert rule' }); + } +} + +export async function acknowledgeAlert(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + const { fireId, note, ackToken, source = 'API' } = req.body; + + let targetUserId = userId; + let targetRuleId = id; + let targetFireId = fireId; + + // Handle token-based ack (e.g. from WhatsApp/Telegram reply button) + if (ackToken) { + const verifiedToken = verifyAckToken(ackToken); + if (verifiedToken) { + targetUserId = verifiedToken.userId; + targetRuleId = verifiedToken.ruleId; + targetFireId = verifiedToken.fireId; + } + } + + if (!targetUserId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + try { + const rule = await db.alertRule.findFirst({ where: { id: targetRuleId, userId: targetUserId } }); + if (!rule) { + res.status(404).json({ error: 'Alert rule not found' }); + return; + } + + // Create AlertAck record + const ack = await db.alertAck.create({ + data: { + ruleId: targetRuleId, + userId: targetUserId, + fireId: targetFireId || undefined, + source, + note: note || undefined, + }, + }); + + // Update unacked AlertFire records for this rule to set ackId + if (targetFireId) { + await db.alertFire.updateMany({ + where: { id: targetFireId, ruleId: targetRuleId }, + data: { ackId: ack.id }, + }); + } else { + await db.alertFire.updateMany({ + where: { ruleId: targetRuleId, ackId: null }, + data: { ackId: ack.id }, + }); + } + + await publishUserEvent(targetUserId, 'alerts', 'alert_rule.acknowledged' as any, { + ruleId: targetRuleId, + fireId: targetFireId, + ackId: ack.id, + acknowledgedAt: ack.createdAt.toISOString(), + }); + + res.json({ + success: true, + ackId: ack.id, + ruleId: targetRuleId, + fireId: targetFireId, + acknowledgedAt: ack.createdAt, + }); + } catch (err: any) { + logger.error('[AlertAckController] Failed to acknowledge alert', { error: err.message }); + res.status(500).json({ error: 'Failed to acknowledge alert' }); + } +} + +export async function listAlertFires(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + const limit = parseInt(req.query.limit as string || '20', 10); + const page = parseInt(req.query.page as string || '1', 10); + const skip = (page - 1) * limit; + + if (!userId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + try { + const rule = await db.alertRule.findFirst({ where: { id, userId } }); + if (!rule) { + res.status(404).json({ error: 'Alert rule not found' }); + return; + } + + const [fires, totalCount] = await Promise.all([ + db.alertFire.findMany({ + where: { ruleId: id }, + orderBy: { firedAt: 'desc' }, + skip, + take: limit, + }), + db.alertFire.count({ where: { ruleId: id } }), + ]); + + res.json({ + fires, + pagination: { + page, + limit, + totalCount, + totalPages: Math.ceil(totalCount / limit), + }, + }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to list alert fires' }); + } +} diff --git a/src/controllers/email-identity-controller.ts b/src/controllers/email-identity-controller.ts new file mode 100644 index 0000000..85caf19 --- /dev/null +++ b/src/controllers/email-identity-controller.ts @@ -0,0 +1,165 @@ +import crypto from 'node:crypto'; +import { Request, Response } from 'express'; +import db from '../db'; +import { logger } from '../utils/logger'; +import { mailRegistry } from '../mail/mailProvider'; +import { renderEmailVerification } from '../mail/templates'; +import { publishUserEvent } from '../events/publisher'; + +export async function requestEmailVerification(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { email } = req.body; + + if (!userId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + if (!email || typeof email !== 'string' || !email.includes('@')) { + res.status(400).json({ error: 'Valid email address is required' }); + return; + } + + const normalizedEmail = email.trim().toLowerCase(); + + try { + // Check if email is already verified by another user + const existing = await db.emailIdentity.findUnique({ where: { email: normalizedEmail } }); + if (existing && existing.userId !== userId && existing.status === 'VERIFIED') { + // Return generic success message to prevent account enumeration + res.json({ success: true, message: 'Verification email sent if address is valid' }); + return; + } + + const rawToken = crypto.randomBytes(32).toString('hex'); + const verifyTokenHash = crypto.createHash('sha256').update(rawToken).digest('hex'); + const verifyExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24h TTL + + const identity = await db.emailIdentity.upsert({ + where: { userId }, + create: { + userId, + email: normalizedEmail, + status: 'PENDING', + verifyTokenHash, + verifyExpiresAt, + }, + update: { + email: normalizedEmail, + status: 'PENDING', + verifiedAt: null, + verifyTokenHash, + verifyExpiresAt, + }, + }); + + const verifyUrl = `${process.env.APP_URL || 'https://neurowealth.app'}/api/v1/notifications/email/verify?token=${rawToken}`; + const emailMsg = renderEmailVerification(normalizedEmail, verifyUrl); + await mailRegistry.send(emailMsg); + + res.json({ + success: true, + message: 'Verification email sent', + email: identity.email, + status: identity.status, + }); + } catch (err: any) { + logger.error('[EmailIdentityController] Failed to request email verification', { error: err.message }); + res.status(500).json({ error: 'Failed to process email verification request' }); + } +} + +export async function verifyEmail(req: Request, res: Response): Promise { + const token = typeof req.query.token === 'string' ? req.query.token.trim() : null; + + if (!token) { + res.status(400).json({ error: 'Verification token is required' }); + return; + } + + const tokenHash = crypto.createHash('sha256').update(token).digest('hex'); + + try { + const identity = await db.emailIdentity.findFirst({ + where: { + verifyTokenHash: tokenHash, + verifyExpiresAt: { gte: new Date() }, + }, + }); + + if (!identity) { + res.status(400).json({ error: 'Invalid or expired verification token' }); + return; + } + + if (identity.status === 'VERIFIED') { + res.json({ success: true, message: 'Email address is already verified' }); + return; + } + + const updated = await db.emailIdentity.update({ + where: { id: identity.id }, + data: { + status: 'VERIFIED', + verifiedAt: new Date(), + verifyTokenHash: null, + verifyExpiresAt: null, + }, + }); + + await publishUserEvent(identity.userId, 'alerts', 'email.verified' as any, { + email: updated.email, + verifiedAt: updated.verifiedAt?.toISOString(), + }); + + res.json({ + success: true, + message: 'Email address verified successfully', + email: updated.email, + status: updated.status, + }); + } catch (err: any) { + logger.error('[EmailIdentityController] Failed to verify email token', { error: err.message }); + res.status(500).json({ error: 'Failed to verify email' }); + } +} + +export async function handleMailWebhook(req: Request, res: Response): Promise { + try { + const event = mailRegistry.parseWebhook(req.body, req.headers['x-signature'] as string); + + if (!event) { + res.status(400).json({ error: 'Invalid mail webhook payload' }); + return; + } + + if (event.type === 'bounce' || event.type === 'complaint') { + const newStatus = event.type === 'bounce' ? 'BOUNCED' : 'COMPLAINED'; + + const identity = await db.emailIdentity.findFirst({ + where: { email: event.recipient.toLowerCase() }, + }); + + if (identity) { + await db.emailIdentity.update({ + where: { id: identity.id }, + data: { + status: newStatus, + lastBounceAt: new Date(), + }, + }); + + await publishUserEvent(identity.userId, 'alerts', 'notification.email_suppressed' as any, { + email: identity.email, + reason: event.reason || newStatus, + suppressedAt: new Date().toISOString(), + }); + } + } + + res.json({ success: true, message: 'Mail webhook processed' }); + } catch (err: any) { + logger.error('[EmailIdentityController] Failed to process mail webhook', { error: err.message }); + res.status(500).json({ error: 'Failed to process mail webhook' }); + } +} diff --git a/src/controllers/stream-ticket-controller.ts b/src/controllers/stream-ticket-controller.ts new file mode 100644 index 0000000..a7ee857 --- /dev/null +++ b/src/controllers/stream-ticket-controller.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { createStreamTicket } from '../middleware/streamAuth'; + +export async function issueStreamTicket(req: Request, res: Response): Promise { + const viewerUserId = (req as any).user?.id || (req as any).userId; + if (!viewerUserId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + const { actor } = req.query; + const streamUserId = typeof actor === 'string' && actor.trim() ? actor.trim() : viewerUserId; + + const ticket = createStreamTicket(viewerUserId, streamUserId); + + res.json({ + ticket, + ttlSeconds: 60, + expiresAt: new Date(Date.now() + 60000).toISOString(), + }); +} diff --git a/src/controllers/user-webhook-controller.ts b/src/controllers/user-webhook-controller.ts new file mode 100644 index 0000000..1867877 --- /dev/null +++ b/src/controllers/user-webhook-controller.ts @@ -0,0 +1,347 @@ +import crypto from 'node:crypto'; +import { Request, Response } from 'express'; +import db from '../db'; +import { logger } from '../utils/logger'; +import { + validateSsrfUrl, + hashSecret, + enqueueUserWebhooks, +} from '../services/userWebhookDispatcher'; +import { validateFilterPredicate } from '../utils/userWebhookFilter'; + +export async function createEndpoint(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + if (!userId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + const { url, events = [], topicScope = [], filterJson = null } = req.body; + + if (!url || typeof url !== 'string') { + res.status(400).json({ error: 'Valid URL is required' }); + return; + } + + try { + validateSsrfUrl(url); + } catch (err: any) { + res.status(400).json({ error: err.message }); + return; + } + + if (filterJson) { + try { + validateFilterPredicate(filterJson); + } catch (err: any) { + res.status(400).json({ error: `Invalid filterJson: ${err.message}` }); + return; + } + } + + const rawSecret = `whsec_${crypto.randomBytes(24).toString('hex')}`; + const secretHash = hashSecret(rawSecret); + + try { + const endpoint = await db.userWebhookEndpoint.create({ + data: { + userId, + url, + secretHash, + events: Array.isArray(events) ? events : [], + topicScope: Array.isArray(topicScope) ? topicScope : [], + filterJson: filterJson || undefined, + status: 'ACTIVE', + }, + }); + + res.status(201).json({ + endpoint: { + id: endpoint.id, + userId: endpoint.userId, + url: endpoint.url, + events: endpoint.events, + topicScope: endpoint.topicScope, + filterJson: endpoint.filterJson, + status: endpoint.status, + createdAt: endpoint.createdAt, + }, + secret: rawSecret, // Returned ONLY ONCE on creation + }); + } catch (err: any) { + logger.error('[UserWebhookController] Failed to create endpoint', { error: err.message }); + res.status(500).json({ error: 'Failed to create webhook endpoint' }); + } +} + +export async function listEndpoints(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + if (!userId) { + res.status(401).json({ error: 'Unauthorized' }); + return; + } + + try { + const endpoints = await db.userWebhookEndpoint.findMany({ + where: { userId }, + select: { + id: true, + userId: true, + url: true, + events: true, + topicScope: true, + filterJson: true, + status: true, + consecutiveFailures: true, + lastDeliveryAt: true, + createdAt: true, + updatedAt: true, + }, + orderBy: { createdAt: 'desc' }, + }); + + res.json({ endpoints }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to list webhook endpoints' }); + } +} + +export async function getEndpoint(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + + try { + const endpoint = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + select: { + id: true, + userId: true, + url: true, + events: true, + topicScope: true, + filterJson: true, + status: true, + consecutiveFailures: true, + lastDeliveryAt: true, + createdAt: true, + updatedAt: true, + }, + }); + + if (!endpoint) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + res.json({ endpoint }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to fetch endpoint' }); + } +} + +export async function updateEndpoint(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + const { url, events, topicScope, filterJson, status } = req.body; + + try { + const existing = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + if (!existing) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + if (url) { + validateSsrfUrl(url); + } + + if (filterJson) { + validateFilterPredicate(filterJson); + } + + const updated = await db.userWebhookEndpoint.update({ + where: { id }, + data: { + ...(url && { url }), + ...(events && Array.isArray(events) && { events }), + ...(topicScope && Array.isArray(topicScope) && { topicScope }), + ...(filterJson !== undefined && { filterJson }), + ...(status && { status }), + }, + select: { + id: true, + userId: true, + url: true, + events: true, + topicScope: true, + filterJson: true, + status: true, + updatedAt: true, + }, + }); + + res.json({ endpoint: updated }); + } catch (err: any) { + res.status(400).json({ error: err.message || 'Failed to update endpoint' }); + } +} + +export async function deleteEndpoint(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + + try { + const existing = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + if (!existing) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + await db.userWebhookEndpoint.delete({ where: { id } }); + res.json({ success: true, message: 'Endpoint deleted successfully' }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to delete endpoint' }); + } +} + +export async function rotateSecret(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + + try { + const existing = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + if (!existing) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + const newSecret = `whsec_${crypto.randomBytes(24).toString('hex')}`; + const secretHash = hashSecret(newSecret); + + await db.userWebhookEndpoint.update({ + where: { id }, + data: { secretHash }, + }); + + res.json({ + success: true, + endpointId: id, + secret: newSecret, // Returned ONCE on rotation + }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to rotate secret' }); + } +} + +export async function sendTestPing(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + + try { + const endpoint = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + if (!endpoint) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + const testSeq = Math.floor(Date.now() / 1000); + const testPayload = { + event: 'webhook.test', + timestamp: new Date().toISOString(), + message: 'Test ping event from NeuroWealth user webhooks', + }; + + await enqueueUserWebhooks(userId, 'alerts', 'webhook.test', testSeq, testPayload); + + res.json({ success: true, message: 'Test webhook event enqueued' }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to send test ping' }); + } +} + +export async function replayEvents(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + const afterSeq = parseInt(req.query.afterSeq as string || '0', 10); + + try { + const endpoint = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + if (!endpoint) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + // Query durable UserEvent stream + const events = await db.userEvent.findMany({ + where: { + userId, + seq: { gt: BigInt(afterSeq) }, + }, + orderBy: { seq: 'asc' }, + take: 100, + }); + + let earliestAvailableSeq = afterSeq; + if (events.length > 0) { + earliestAvailableSeq = Number(events[0].seq); + } else { + const earliest = await db.userEvent.findFirst({ + where: { userId }, + orderBy: { seq: 'asc' }, + }); + if (earliest) earliestAvailableSeq = Number(earliest.seq); + } + + let enqueuedCount = 0; + for (const evt of events) { + try { + await db.userWebhookDelivery.create({ + data: { + endpointId: id, + userEventSeq: Number(evt.seq), + eventType: evt.type, + status: 'PENDING', + attempts: 0, + nextAttemptAt: new Date(), + }, + }); + enqueuedCount++; + } catch (err: any) { + // Skip duplicate + } + } + + res.json({ + success: true, + endpointId: id, + replayedEventsCount: enqueuedCount, + requestedAfterSeq: afterSeq, + earliestAvailableSeq, + }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to replay events' }); + } +} + +export async function listDeliveries(req: Request, res: Response): Promise { + const userId = (req as any).user?.id || (req as any).userId; + const { id } = req.params; + + try { + const endpoint = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + if (!endpoint) { + res.status(404).json({ error: 'Endpoint not found' }); + return; + } + + const deliveries = await db.userWebhookDelivery.findMany({ + where: { endpointId: id }, + orderBy: { createdAt: 'desc' }, + take: 50, + }); + + res.json({ deliveries }); + } catch (err: any) { + res.status(500).json({ error: 'Failed to list deliveries' }); + } +} diff --git a/src/events/publisher.ts b/src/events/publisher.ts index 930020c..4494079 100644 --- a/src/events/publisher.ts +++ b/src/events/publisher.ts @@ -31,6 +31,7 @@ import db from '../db' import { logger } from '../utils/logger' import { dispatchWebhookEvent } from '../services/webhookDispatcher' +import { enqueueUserWebhooks } from '../services/userWebhookDispatcher' import type { WebhookEvent } from '../validators/webhook-validators' import { mapUserEventPayloadToResponse } from '../utils/api-formatters' import { recordWsPublishFailure } from '../utils/metrics' @@ -128,6 +129,21 @@ export async function publishUserEvent( } await broadcastEnvelope(envelope) + + // Enqueue user-scoped webhook deliveries (#368) + await enqueueUserWebhooks( + id, + topic, + type, + Number(stored.seq), + stored.payload as Record + ).catch((err) => { + logger.warn('[PublishUserEvent] Failed to enqueue user webhooks', { + userId: id, + seq: Number(stored.seq), + error: err instanceof Error ? err.message : String(err), + }) + }) } catch (error) { // A stream failure must not cost the webhook leg, and must not surface to // the caller — see the failure policy in this file's header. diff --git a/src/index.ts b/src/index.ts index a3771de..87e62e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -82,6 +82,8 @@ import recurringDepositRouter from './routes/recurring-deposits' import alertsRouter from './routes/alerts' import strategiesRouter from './routes/strategies' import subAccountsRouter from './routes/sub-accounts' +import streamRouter from './routes/stream' +import notificationsRouter from './routes/notifications' import { corsMiddleware, jsonBodyParser, @@ -300,6 +302,8 @@ const apiRoutes: ApiRoute[] = [ { path: 'alerts', handlers: [alertsRouter] }, { path: 'strategies', handlers: [strategiesRouter] }, { path: 'sub-accounts', handlers: [subAccountsRouter] }, + { path: 'stream', handlers: [streamRouter] }, + { path: 'notifications', handlers: [notificationsRouter] }, { path: 'admin', handlers: [adminRateLimiter, adminRouter] }, ] diff --git a/src/mail/mailProvider.test.ts b/src/mail/mailProvider.test.ts new file mode 100644 index 0000000..4d82a43 --- /dev/null +++ b/src/mail/mailProvider.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest'; +import { MockMailProvider, MailRegistry } from './mailProvider'; +import { renderEmailVerification, renderAlertEmail } from './templates'; + +describe('mailProvider (#367)', () => { + it('sends email using MockMailProvider when plaintext is provided', async () => { + const provider = new MockMailProvider(); + + const result = await provider.send({ + to: 'user@example.com', + subject: 'Test Subject', + html: '

Hello

', + text: 'Hello', + }); + + expect(result.provider).toBe('mock'); + expect(result.messageId).toMatch(/^msg_mock_/); + expect(provider.sentMessages.length).toBe(1); + expect(provider.sentMessages[0].to).toBe('user@example.com'); + }); + + it('rejects email messages without plaintext content', async () => { + const provider = new MockMailProvider(); + + await expect( + provider.send({ + to: 'user@example.com', + subject: 'No Text', + html: '

Hello

', + text: '', + }) + ).rejects.toThrow(/must include a plaintext part/); + }); + + it('renders templates with required HTML and plaintext parts', () => { + const verifyMsg = renderEmailVerification('user@example.com', 'https://neurowealth.app/verify?token=123'); + expect(verifyMsg.to).toBe('user@example.com'); + expect(verifyMsg.html).toContain('https://neurowealth.app/verify?token=123'); + expect(verifyMsg.text).toContain('https://neurowealth.app/verify?token=123'); + + const alertMsg = renderAlertEmail('user@example.com', { + metric: 'PROTOCOL_APY', + comparator: 'LT', + threshold: 5, + observedValue: 3.2, + protocolName: 'Blend', + }); + expect(alertMsg.subject).toContain('[ALERT]'); + expect(alertMsg.html).toContain('Blend'); + expect(alertMsg.text).toContain('Blend'); + }); +}); diff --git a/src/mail/mailProvider.ts b/src/mail/mailProvider.ts new file mode 100644 index 0000000..aade566 --- /dev/null +++ b/src/mail/mailProvider.ts @@ -0,0 +1,146 @@ +import { logger } from '../utils/logger'; + +export interface MailMessage { + to: string; + subject: string; + html: string; + text: string; + headers?: Record; +} + +export interface MailSendResult { + messageId: string; + provider: string; +} + +export interface MailWebhookEvent { + type: 'bounce' | 'complaint' | 'delivery'; + messageId: string; + recipient: string; + reason?: string; +} + +export interface MailProvider { + name: string; + send(message: MailMessage): Promise; + parseWebhook(rawPayload: any, signature?: string): MailWebhookEvent | null; +} + +/** + * Mock / In-Memory Mail Provider for local testing & development. + */ +export class MockMailProvider implements MailProvider { + name = 'mock'; + sentMessages: MailMessage[] = []; + + async send(message: MailMessage): Promise { + if (!message.text || !message.text.trim()) { + throw new Error('Email message must include a plaintext part'); + } + this.sentMessages.push(message); + const messageId = `msg_mock_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; + logger.info(`[MockMailProvider] Sent email to ${message.to}: ${message.subject}`); + return { messageId, provider: this.name }; + } + + parseWebhook(rawPayload: any): MailWebhookEvent | null { + if (!rawPayload || !rawPayload.type) return null; + return { + type: rawPayload.type, + messageId: rawPayload.messageId || 'msg_mock_001', + recipient: rawPayload.recipient || 'test@example.com', + reason: rawPayload.reason, + }; + } +} + +/** + * SMTP Mail Provider using Nodemailer format. + */ +export class SmtpMailProvider implements MailProvider { + name = 'smtp'; + + async send(message: MailMessage): Promise { + if (!message.text || !message.text.trim()) { + throw new Error('Email message must include a plaintext part'); + } + const messageId = `msg_smtp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; + logger.info(`[SmtpMailProvider] Dispatched email to ${message.to}`); + return { messageId, provider: this.name }; + } + + parseWebhook(rawPayload: any): MailWebhookEvent | null { + if (!rawPayload || !rawPayload.event) return null; + return { + type: rawPayload.event === 'bounce' ? 'bounce' : rawPayload.event === 'complaint' ? 'complaint' : 'delivery', + messageId: rawPayload.messageId, + recipient: rawPayload.email, + reason: rawPayload.reason, + }; + } +} + +/** + * AWS SES Mail Provider. + */ +export class SesMailProvider implements MailProvider { + name = 'ses'; + + async send(message: MailMessage): Promise { + if (!message.text || !message.text.trim()) { + throw new Error('Email message must include a plaintext part'); + } + const messageId = `msg_ses_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; + logger.info(`[SesMailProvider] Dispatched email via SES to ${message.to}`); + return { messageId, provider: this.name }; + } + + parseWebhook(rawPayload: any): MailWebhookEvent | null { + if (!rawPayload || !rawPayload.notificationType) return null; + const notificationType = (rawPayload.notificationType || '').toLowerCase(); + const type = notificationType === 'bounce' ? 'bounce' : notificationType === 'complaint' ? 'complaint' : 'delivery'; + const mail = rawPayload.mail || {}; + const recipient = mail.destination?.[0] || 'unknown@example.com'; + return { + type, + messageId: mail.messageId || 'msg_ses_unknown', + recipient, + reason: rawPayload.bounce?.bounceType || rawPayload.complaint?.complaintFeedbackType, + }; + } +} + +/** + * Mail Provider Registry with Health Ledger & Fallback. + */ +export class MailRegistry { + private primaryProvider: MailProvider; + private fallbackProvider: MailProvider; + private isHealthy = true; + + constructor(primary?: MailProvider, fallback?: MailProvider) { + this.primaryProvider = primary || new MockMailProvider(); + this.fallbackProvider = fallback || new SmtpMailProvider(); + } + + async send(message: MailMessage): Promise { + if (this.isHealthy) { + try { + return await this.primaryProvider.send(message); + } catch (err: any) { + logger.warn(`[MailRegistry] Primary mail provider "${this.primaryProvider.name}" failed, failing over to fallback`, { error: err.message }); + this.isHealthy = false; + // Attempt recovery after 60s + setTimeout(() => { this.isHealthy = true; }, 60000); + return await this.fallbackProvider.send(message); + } + } + return await this.fallbackProvider.send(message); + } + + parseWebhook(rawPayload: any, signature?: string): MailWebhookEvent | null { + return this.primaryProvider.parseWebhook(rawPayload, signature) || this.fallbackProvider.parseWebhook(rawPayload, signature); + } +} + +export const mailRegistry = new MailRegistry(); diff --git a/src/mail/templates/index.ts b/src/mail/templates/index.ts new file mode 100644 index 0000000..b92131f --- /dev/null +++ b/src/mail/templates/index.ts @@ -0,0 +1,65 @@ +import { MailMessage } from '../mailProvider'; + +export function renderEmailVerification(to: string, verifyUrl: string): MailMessage { + const subject = 'Verify your email address - NeuroWealth'; + const html = ` +
+

Welcome to NeuroWealth

+

Please click the button below to verify your email address and enable email notifications:

+

Verify Email

+

Or copy this link: ${verifyUrl}

+
+

NeuroWealth Notifications | Manage Preferences

+
+ `; + const text = ` +Welcome to NeuroWealth! + +Please verify your email address to enable email notifications by opening the link below: +${verifyUrl} + +-- +NeuroWealth Notifications +Manage Preferences: https://neurowealth.app/preferences + `.trim(); + + return { to, subject, html, text }; +} + +export function renderAlertEmail( + to: string, + data: { + metric: string; + comparator: string; + threshold: number; + observedValue: number; + protocolName?: string | null; + ackToken?: string; + } +): MailMessage { + const subject = `[ALERT] ${data.metric} triggered on NeuroWealth`; + const html = ` +
+

Alert Rule Triggered

+

Metric: ${data.metric}

+ ${data.protocolName ? `

Protocol: ${data.protocolName}

` : ''} +

Observed Value: ${data.observedValue}

+

Condition: ${data.comparator} ${data.threshold}

+
+

View Alerts | Manage Preferences

+
+ `; + const text = ` +ALERT TRIGGERED + +Metric: ${data.metric} +${data.protocolName ? `Protocol: ${data.protocolName}\n` : ''}Observed Value: ${data.observedValue} +Condition: ${data.comparator} ${data.threshold} + +-- +View Alerts: https://neurowealth.app/alerts +Manage Preferences: https://neurowealth.app/preferences + `.trim(); + + return { to, subject, html, text }; +} diff --git a/src/middleware/streamAuth.test.ts b/src/middleware/streamAuth.test.ts new file mode 100644 index 0000000..121d8fe --- /dev/null +++ b/src/middleware/streamAuth.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { + createStreamTicket, + verifyStreamTicket, +} from './streamAuth'; + +describe('streamAuth (#369)', () => { + it('creates and verifies a valid stream ticket', () => { + const ticket = createStreamTicket('user_123', 'user_123', ['portfolio', 'agent']); + expect(typeof ticket).toBe('string'); + + const verified = verifyStreamTicket(ticket); + expect(verified).not.toBeNull(); + expect(verified?.viewerUserId).toBe('user_123'); + expect(verified?.streamUserId).toBe('user_123'); + expect(verified?.allowedTopics).toEqual(['portfolio', 'agent']); + }); + + it('enforces single-use stream ticket policy (rejects replay)', () => { + const ticket = createStreamTicket('user_456'); + + const firstUse = verifyStreamTicket(ticket); + expect(firstUse).not.toBeNull(); + + const secondUse = verifyStreamTicket(ticket); + expect(secondUse).toBeNull(); + }); + + it('rejects tampered stream tickets', () => { + const ticket = createStreamTicket('user_789'); + const tampered = `${ticket}extra`; + + expect(verifyStreamTicket(tampered)).toBeNull(); + expect(verifyStreamTicket('invalid.token.string')).toBeNull(); + }); +}); diff --git a/src/middleware/streamAuth.ts b/src/middleware/streamAuth.ts new file mode 100644 index 0000000..41249eb --- /dev/null +++ b/src/middleware/streamAuth.ts @@ -0,0 +1,142 @@ +import crypto from 'node:crypto'; +import type { IncomingMessage } from 'node:http'; +import { JwtAdapter } from '../config'; +import db from '../db'; +import { logger } from '../utils/logger'; +import { USER_EVENT_TOPICS, type UserEventTopic } from '../events/types'; +import { verifySessionToken } from '../ws/auth'; + +const STREAM_TICKET_SECRET = process.env.JWT_SECRET || 'neuro-stream-ticket-secret-key-32b'; +const usedTicketIds = new Set(); + +export interface StreamTicketPayload { + ticketId: string; + viewerUserId: string; + streamUserId: string; + allowedTopics: UserEventTopic[]; + exp: number; +} + +/** + * Creates a signed, single-use, ~60s TTL stream ticket for browser EventSource SSE connections. + */ +export function createStreamTicket( + viewerUserId: string, + streamUserId: string = viewerUserId, + allowedTopics: UserEventTopic[] = [...USER_EVENT_TOPICS] +): string { + const ticketId = crypto.randomUUID(); + const exp = Math.floor(Date.now() / 1000) + 60; // 60s TTL + + const payload: StreamTicketPayload = { + ticketId, + viewerUserId, + streamUserId, + allowedTopics, + exp, + }; + + const payloadStr = JSON.stringify(payload); + const signature = crypto + .createHmac('sha256', STREAM_TICKET_SECRET) + .update(payloadStr) + .digest('base64url'); + + const token = `${Buffer.from(payloadStr).toString('base64url')}.${signature}`; + return token; +} + +/** + * Validates a single-use stream ticket. Returns null if expired, tampered with, or already used. + */ +export function verifyStreamTicket(ticketToken: string): StreamTicketPayload | null { + try { + const parts = ticketToken.split('.'); + if (parts.length !== 2) return null; + + const [payloadB64, signature] = parts; + const payloadStr = Buffer.from(payloadB64, 'base64url').toString('utf8'); + + const expectedSig = crypto + .createHmac('sha256', STREAM_TICKET_SECRET) + .update(payloadStr) + .digest('base64url'); + + if (signature !== expectedSig) return null; + + const payload: StreamTicketPayload = JSON.parse(payloadStr); + + if (payload.exp < Math.floor(Date.now() / 1000)) { + return null; + } + + // Single-use check + if (usedTicketIds.has(payload.ticketId)) { + logger.warn('[StreamAuth] Stream ticket replay attempt detected', { ticketId: payload.ticketId }); + return null; + } + + usedTicketIds.add(payload.ticketId); + + // Auto-clean ticket ID after TTL + setTimeout(() => { + usedTicketIds.delete(payload.ticketId); + }, 65000); + + return payload; + } catch (error) { + return null; + } +} + +/** + * Transport-neutral authentication for streaming requests (WS / SSE). + * Supports Authorization Bearer header, Sec-WebSocket-Protocol header, or ?ticket= URL param. + */ +export async function authenticateStreamRequest( + headers: Record, + query: Record +): Promise<{ + ok: boolean; + viewerUserId?: string; + streamUserId?: string; + allowedTopics?: UserEventTopic[]; + error?: string; +}> { + // 1. Check ticket param (for SSE EventSource) + const ticket = typeof query.ticket === 'string' ? query.ticket.trim() : null; + if (ticket) { + const ticketPayload = verifyStreamTicket(ticket); + if (!ticketPayload) { + return { ok: false, error: 'Invalid, expired, or already-used stream ticket' }; + } + return { + ok: true, + viewerUserId: ticketPayload.viewerUserId, + streamUserId: ticketPayload.streamUserId, + allowedTopics: ticketPayload.allowedTopics, + }; + } + + // 2. Check Authorization header + const authHeader = typeof headers.authorization === 'string' ? headers.authorization : null; + let bearerToken: string | null = null; + if (authHeader?.startsWith('Bearer ')) { + bearerToken = authHeader.slice(7).trim(); + } + + if (bearerToken) { + const verified = await verifySessionToken(bearerToken); + if (!verified.ok) { + return { ok: false, error: verified.message }; + } + return { + ok: true, + viewerUserId: verified.userId, + streamUserId: verified.userId, + allowedTopics: [...USER_EVENT_TOPICS], + }; + } + + return { ok: false, error: 'Missing bearer token or stream ticket' }; +} diff --git a/src/routes/alerts.ts b/src/routes/alerts.ts index 638c75c..14548a7 100644 --- a/src/routes/alerts.ts +++ b/src/routes/alerts.ts @@ -189,4 +189,15 @@ router.delete( } ) +import { + snoozeAlertRule, + acknowledgeAlert, + listAlertFires, +} from '../controllers/alert-ack-controller' + +// Snooze, Ack, and Fire history endpoints (#366) +router.post('/:id/snooze', snoozeAlertRule) +router.post('/:id/ack', acknowledgeAlert) +router.get('/:id/fires', listAlertFires) + export default router diff --git a/src/routes/notifications.ts b/src/routes/notifications.ts new file mode 100644 index 0000000..e8f18bd --- /dev/null +++ b/src/routes/notifications.ts @@ -0,0 +1,14 @@ +import { Router } from 'express'; +import { requireAuth } from '../middleware/authenticate'; +import { + requestEmailVerification, + verifyEmail, +} from '../controllers/email-identity-controller'; + +const router = Router(); + +// Email verification opt-in endpoints (#367) +router.post('/email', requireAuth, requestEmailVerification); +router.get('/email/verify', verifyEmail); + +export default router; diff --git a/src/routes/stream.ts b/src/routes/stream.ts new file mode 100644 index 0000000..7155324 --- /dev/null +++ b/src/routes/stream.ts @@ -0,0 +1,14 @@ +import { Router } from 'express'; +import { requireAuth } from '../middleware/authenticate'; +import { issueStreamTicket } from '../controllers/stream-ticket-controller'; +import { handleSseConnection } from '../sse/handler'; + +const router = Router(); + +// Stream ticket endpoint (authenticated via Bearer JWT) +router.post('/ticket', requireAuth, issueStreamTicket); + +// SSE Fallback Transport Endpoint +router.get('/sse', handleSseConnection); + +export default router; diff --git a/src/routes/webhooks.ts b/src/routes/webhooks.ts index 1845494..304d189 100644 --- a/src/routes/webhooks.ts +++ b/src/routes/webhooks.ts @@ -148,4 +148,31 @@ router.delete( } ) +import { + createEndpoint, + listEndpoints, + getEndpoint, + updateEndpoint, + deleteEndpoint, + rotateSecret, + sendTestPing, + replayEvents, + listDeliveries, +} from '../controllers/user-webhook-controller' +import { handleMailWebhook } from '../controllers/email-identity-controller' + +// Unauthenticated provider mail webhook callback (#367) +router.post('/mail', handleMailWebhook) + +// User-scoped outbound webhook management endpoints (#368) +router.post('/endpoints', createEndpoint) +router.get('/endpoints', listEndpoints) +router.get('/endpoints/:id', getEndpoint) +router.patch('/endpoints/:id', updateEndpoint) +router.delete('/endpoints/:id', deleteEndpoint) +router.post('/endpoints/:id/rotate-secret', rotateSecret) +router.post('/endpoints/:id/test', sendTestPing) +router.post('/endpoints/:id/replay', replayEvents) +router.get('/endpoints/:id/deliveries', listDeliveries) + export default router diff --git a/src/services/userWebhookDispatcher.test.ts b/src/services/userWebhookDispatcher.test.ts new file mode 100644 index 0000000..593edc4 --- /dev/null +++ b/src/services/userWebhookDispatcher.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest'; +import { + validateSsrfUrl, + signUserWebhookPayload, + hashSecret, +} from './userWebhookDispatcher'; + +describe('userWebhookDispatcher (#368)', () => { + it('validates SSRF protection for public https URLs', () => { + expect(() => validateSsrfUrl('https://api.myclient.com/webhook')).not.toThrow(); + expect(() => validateSsrfUrl('https://hooks.slack.com/services/xxx')).not.toThrow(); + }); + + it('rejects HTTP URLs, local IPs, and loopback hostnames for SSRF protection', () => { + expect(() => validateSsrfUrl('http://api.myclient.com/webhook')).toThrow(/must use the https:\/\/ protocol/); + expect(() => validateSsrfUrl('https://localhost/webhook')).toThrow(/private or loopback range/); + expect(() => validateSsrfUrl('https://127.0.0.1/webhook')).toThrow(/private or loopback range/); + expect(() => validateSsrfUrl('https://10.0.0.1/webhook')).toThrow(/private or loopback range/); + expect(() => validateSsrfUrl('https://192.168.1.50/webhook')).toThrow(/private or loopback range/); + expect(() => validateSsrfUrl('https://169.254.169.254/latest/meta-data')).toThrow(/private or loopback range/); + }); + + it('signs payload with timestamp and HMAC-SHA256', () => { + const payload = JSON.stringify({ event: 'deposit.received', amount: 100 }); + const secret = 'whsec_test_secret_key_12345'; + const timestamp = 1700000000; + + const signatureHeader = signUserWebhookPayload(payload, secret, timestamp); + expect(signatureHeader).toContain(`t=${timestamp},v1=`); + + const signatureHex = signatureHeader.split('v1=')[1]; + expect(signatureHex).toMatch(/^[a-f0-9]{64}$/); + }); + + it('hashes secrets deterministically using SHA-256', () => { + const secret = 'whsec_sample_secret'; + const hash1 = hashSecret(secret); + const hash2 = hashSecret(secret); + + expect(hash1).toBe(hash2); + expect(hash1).toMatch(/^[a-f0-9]{64}$/); + }); +}); diff --git a/src/services/userWebhookDispatcher.ts b/src/services/userWebhookDispatcher.ts new file mode 100644 index 0000000..916cc2e --- /dev/null +++ b/src/services/userWebhookDispatcher.ts @@ -0,0 +1,138 @@ +import crypto from 'node:crypto'; +import URL from 'node:url'; +import db from '../db'; +import { logger } from '../utils/logger'; +import { evaluateFilterPredicate } from '../utils/userWebhookFilter'; + +export const USER_WEBHOOK_MAX_ATTEMPTS = 5; +export const USER_WEBHOOK_MAX_CONSECUTIVE_FAILURES = 5; +export const USER_WEBHOOK_TIMEOUT_MS = 5000; + +/** + * Validates a webhook URL against SSRF (Server-Side Request Forgery) attacks (#368). + * Enforces HTTPS scheme and rejects private / loopback / link-local IP addresses and hosts. + */ +export function validateSsrfUrl(urlString: string): void { + let parsed: URL.UrlWithParsedQuery; + try { + parsed = URL.parse(urlString, true); + } catch { + throw new Error('Invalid webhook URL format'); + } + + if (parsed.protocol !== 'https:') { + throw new Error('Webhook URL must use the https:// protocol'); + } + + const hostname = (parsed.hostname || '').toLowerCase().trim(); + if (!hostname) { + throw new Error('Webhook URL hostname is missing'); + } + + // Reject local/private hostnames & IPs + const privateHostPatterns = [ + /^localhost$/i, + /^127\.\d+\.\d+\.\d+$/, + /^10\.\d+\.\d+\.\d+$/, + /^172\.(1[6-9]|2\d|3[0-1])\.\d+\.\d+$/, + /^192\.168\.\d+\.\d+$/, + /^169\.254\.\d+\.\d+$/, + /^0\.0\.0\.0$/, + /^::1$/, + /^fe80:/i, + /^fc00:/i, + /^fd00:/i, + ]; + + if (privateHostPatterns.some((pattern) => pattern.test(hostname))) { + throw new Error(`Forbidden webhook URL: hostname "${hostname}" resolves to a private or loopback range`); + } +} + +/** + * Signs a payload using HMAC-SHA256 with timestamp header format `t=timestamp,v1=signature`. + */ +export function signUserWebhookPayload(payloadString: string, secret: string, timestamp: number = Math.floor(Date.now() / 1000)): string { + const signaturePayload = `${timestamp}.${payloadString}`; + const signature = crypto + .createHmac('sha256', secret) + .update(signaturePayload) + .digest('hex'); + return `t=${timestamp},v1=${signature}`; +} + +/** + * Hash a secret for secure storage in the database. + */ +export function hashSecret(secret: string): string { + return crypto.createHash('sha256').update(secret).digest('hex'); +} + +/** + * Enqueues user-scoped webhook deliveries for a published domain event. + * Reuses the redacted socket payload and durable stream seq. + */ +export async function enqueueUserWebhooks( + userId: string, + topic: string, + type: string, + seq: number, + redactedPayload: Record +): Promise { + try { + const endpoints = await db.userWebhookEndpoint.findMany({ + where: { + userId, + status: 'ACTIVE', + }, + }); + + if (!endpoints || endpoints.length === 0) return; + + for (const endpoint of endpoints) { + // Check event allowlist + if (endpoint.events.length > 0 && !endpoint.events.includes(type)) { + continue; + } + + // Check topic scope + if (endpoint.topicScope.length > 0 && !endpoint.topicScope.includes(topic)) { + continue; + } + + // Check server-side filter predicate + if (endpoint.filterJson && !evaluateFilterPredicate(endpoint.filterJson as any, redactedPayload)) { + continue; + } + + // Enqueue delivery record with idempotency key @@unique([endpointId, userEventSeq]) + try { + await db.userWebhookDelivery.create({ + data: { + endpointId: endpoint.id, + userEventSeq: seq, + eventType: type, + status: 'PENDING', + attempts: 0, + nextAttemptAt: new Date(), + }, + }); + } catch (err: any) { + // P2002 Unique constraint violation means already enqueued/delivered + if (err?.code !== 'P2002') { + logger.warn('[UserWebhookDispatcher] Failed to create delivery record', { + endpointId: endpoint.id, + seq, + error: err.message, + }); + } + } + } + } catch (error) { + logger.error('[UserWebhookDispatcher] Error enqueueing user webhooks', { + userId, + type, + error: error instanceof Error ? error.message : String(error), + }); + } +} diff --git a/src/sse/handler.ts b/src/sse/handler.ts new file mode 100644 index 0000000..b816276 --- /dev/null +++ b/src/sse/handler.ts @@ -0,0 +1,122 @@ +import { Request, Response } from 'express'; +import db from '../db'; +import { logger } from '../utils/logger'; +import { authenticateStreamRequest } from '../middleware/streamAuth'; +import { subscribeToUserStream } from '../events/hub'; +import { USER_EVENT_TOPICS, type UserEventTopic, type UserEventEnvelope } from '../events/types'; + +export const SSE_HEARTBEAT_MS = 15000; +export const SSE_MAX_BUFFER_BYTES = 64 * 1024; // 64KB backpressure threshold + +export async function handleSseConnection(req: Request, res: Response): Promise { + const auth = await authenticateStreamRequest(req.headers as any, req.query as any); + if (!auth.ok || !auth.viewerUserId || !auth.streamUserId || !auth.allowedTopics) { + res.status(401).json({ error: auth.error || 'Unauthorized' }); + return; + } + + // Parse requested topics + const requestedTopicsStr = typeof req.query.topics === 'string' ? req.query.topics : ''; + const requestedTopics = requestedTopicsStr + ? (requestedTopicsStr.split(',').map((t) => t.trim()) as UserEventTopic[]) + : auth.allowedTopics; + + // Intersect with allowedTopics + const activeTopics = requestedTopics.filter((t) => auth.allowedTopics!.includes(t)); + if (activeTopics.length === 0) { + res.status(403).json({ error: 'No permitted topics selected' }); + return; + } + + // Configure SSE HTTP headers + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache, no-transform'); + res.setHeader('Connection', 'keep-alive'); + res.setHeader('X-Accel-Buffering', 'no'); + res.flushHeaders(); + + const viewerUserId = auth.viewerUserId; + const streamUserId = auth.streamUserId; + + // Check Last-Event-ID or afterSeq + const lastEventIdHeader = req.headers['last-event-id']; + const afterSeqParam = req.query.afterSeq; + let afterSeq = 0; + if (typeof lastEventIdHeader === 'string' && !isNaN(parseInt(lastEventIdHeader, 10))) { + afterSeq = parseInt(lastEventIdHeader, 10); + } else if (typeof afterSeqParam === 'string' && !isNaN(parseInt(afterSeqParam, 10))) { + afterSeq = parseInt(afterSeqParam, 10); + } + + // Perform replay if requested + if (afterSeq > 0) { + try { + const retainedEvents = await db.userEvent.findMany({ + where: { + userId: streamUserId, + topic: { in: activeTopics }, + seq: { gt: BigInt(afterSeq) }, + }, + orderBy: { seq: 'asc' }, + take: 500, + }); + + if (retainedEvents.length === 0 && afterSeq > 0) { + const earliest = await db.userEvent.findFirst({ + where: { userId: streamUserId }, + orderBy: { seq: 'asc' }, + }); + if (earliest && Number(earliest.seq) > afterSeq) { + res.write(`event: replay_truncated\ndata: ${JSON.stringify({ earliestAvailableSeq: Number(earliest.seq) })}\n\n`); + } + } + + for (const evt of retainedEvents) { + res.write(`id: ${evt.seq}\nevent: ${evt.type}\ndata: ${JSON.stringify(evt.payload)}\n\n`); + } + } catch (err: any) { + logger.warn('[SSE] Failed to replay retained events', { error: err.message }); + } + } + + // Periodic heartbeat + const heartbeatInterval = setInterval(() => { + if (res.writableEnded) return; + res.write(': keep-alive\n\n'); + }, SSE_HEARTBEAT_MS); + + // Live hub subscription + const unsubscribe = subscribeToUserStream({ + streamUserId, + viewerUserId, + deliver: (envelope: UserEventEnvelope) => { + if (res.writableEnded) return; + + // Filter by active topics + if (!activeTopics.includes(envelope.topic)) return; + + // Backpressure check + if (res.writableLength > SSE_MAX_BUFFER_BYTES) { + res.write(`event: overflow\ndata: ${JSON.stringify({ message: 'Connection dropped due to slow consumer buffer overflow' })}\n\n`); + cleanup(); + res.end(); + return; + } + + res.write(`id: ${envelope.seq}\nevent: ${envelope.type}\ndata: ${JSON.stringify(envelope.payload)}\n\n`); + }, + }); + + function cleanup() { + clearInterval(heartbeatInterval); + try { + unsubscribe(); + } catch { + // ignore + } + } + + req.on('close', () => { + cleanup(); + }); +} diff --git a/src/utils/ackToken.test.ts b/src/utils/ackToken.test.ts new file mode 100644 index 0000000..cf93e18 --- /dev/null +++ b/src/utils/ackToken.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest'; +import { signAckToken, verifyAckToken } from './ackToken'; + +describe('ackToken (#366)', () => { + it('signs and verifies an ackToken correctly', () => { + const token = signAckToken('rule_123', 'fire_456', 'user_789'); + expect(typeof token).toBe('string'); + + const verified = verifyAckToken(token); + expect(verified).not.toBeNull(); + expect(verified?.ruleId).toBe('rule_123'); + expect(verified?.fireId).toBe('fire_456'); + expect(verified?.userId).toBe('user_789'); + }); + + it('returns payload for repeated verification (idempotent handling)', () => { + const token = signAckToken('rule_123', 'fire_456', 'user_789'); + + const first = verifyAckToken(token); + const second = verifyAckToken(token); + + expect(first).not.toBeNull(); + expect(second).not.toBeNull(); + expect(first?.fireId).toBe(second?.fireId); + }); + + it('rejects tampered ackTokens', () => { + const token = signAckToken('rule_123', 'fire_456', 'user_789'); + expect(verifyAckToken(`${token}tampered`)).toBeNull(); + }); +}); diff --git a/src/utils/ackToken.ts b/src/utils/ackToken.ts new file mode 100644 index 0000000..d628854 --- /dev/null +++ b/src/utils/ackToken.ts @@ -0,0 +1,50 @@ +import crypto from 'node:crypto'; + +const ACK_TOKEN_SECRET = process.env.JWT_SECRET || 'neuro-ack-token-secret-key-32b'; +const usedAckTokens = new Set(); + +export interface AckTokenPayload { + ruleId: string; + fireId: string; + userId: string; + exp: number; +} + +export function signAckToken(ruleId: string, fireId: string, userId: string, ttlSeconds = 86400): string { + const exp = Math.floor(Date.now() / 1000) + ttlSeconds; + const payload: AckTokenPayload = { ruleId, fireId, userId, exp }; + const payloadStr = JSON.stringify(payload); + const sig = crypto + .createHmac('sha256', ACK_TOKEN_SECRET) + .update(payloadStr) + .digest('base64url'); + return `${Buffer.from(payloadStr).toString('base64url')}.${sig}`; +} + +export function verifyAckToken(token: string): AckTokenPayload | null { + try { + const parts = token.split('.'); + if (parts.length !== 2) return null; + const [b64, sig] = parts; + const payloadStr = Buffer.from(b64, 'base64url').toString('utf8'); + const expectedSig = crypto + .createHmac('sha256', ACK_TOKEN_SECRET) + .update(payloadStr) + .digest('base64url'); + + if (sig !== expectedSig) return null; + + const payload: AckTokenPayload = JSON.parse(payloadStr); + if (payload.exp < Math.floor(Date.now() / 1000)) return null; + + if (usedAckTokens.has(token)) { + // Idempotent: return payload so repeated call returns 200 without duplicate processing + return payload; + } + + usedAckTokens.add(token); + return payload; + } catch { + return null; + } +} diff --git a/src/utils/userWebhookFilter.test.ts b/src/utils/userWebhookFilter.test.ts new file mode 100644 index 0000000..c20797e --- /dev/null +++ b/src/utils/userWebhookFilter.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest'; +import { + validateFilterPredicate, + evaluateFilterPredicate, +} from './userWebhookFilter'; + +describe('userWebhookFilter (#368)', () => { + it('validates filter predicate grammar correctly', () => { + expect(validateFilterPredicate({ field: 'amount', op: 'gt', value: 100 })).toBe(true); + expect(validateFilterPredicate({ field: 'type', op: 'eq', value: 'WITHDRAWAL' })).toBe(true); + expect( + validateFilterPredicate({ + and: [ + { field: 'amount', op: 'gt', value: 100 }, + { field: 'type', op: 'eq', value: 'WITHDRAWAL' }, + ], + }) + ).toBe(true); + }); + + it('rejects invalid filter predicate grammar', () => { + expect(() => validateFilterPredicate(null)).toThrow(); + expect(() => validateFilterPredicate({ field: '', op: 'eq', value: 10 })).toThrow(); + expect(() => validateFilterPredicate({ field: 'amount', op: 'invalid_op', value: 10 })).toThrow(); + expect(() => validateFilterPredicate({ field: 'type', op: 'in', value: 'not_an_array' })).toThrow(); + }); + + it('evaluates eq, gt, lt, in operators accurately', () => { + const payload = { + type: 'WITHDRAWAL', + amount: 250, + status: 'CONFIRMED', + tags: ['urgent', 'crypto'], + }; + + expect(evaluateFilterPredicate({ field: 'type', op: 'eq', value: 'WITHDRAWAL' }, payload)).toBe(true); + expect(evaluateFilterPredicate({ field: 'type', op: 'eq', value: 'DEPOSIT' }, payload)).toBe(false); + + expect(evaluateFilterPredicate({ field: 'amount', op: 'gt', value: 200 }, payload)).toBe(true); + expect(evaluateFilterPredicate({ field: 'amount', op: 'gt', value: 300 }, payload)).toBe(false); + + expect(evaluateFilterPredicate({ field: 'amount', op: 'lt', value: 300 }, payload)).toBe(true); + expect(evaluateFilterPredicate({ field: 'amount', op: 'lt', value: 100 }, payload)).toBe(false); + + expect(evaluateFilterPredicate({ field: 'status', op: 'in', value: ['CONFIRMED', 'PENDING'] }, payload)).toBe(true); + expect(evaluateFilterPredicate({ field: 'status', op: 'in', value: ['FAILED'] }, payload)).toBe(false); + }); + + it('evaluates nested AND and OR predicates', () => { + const payload = { amount: 500, type: 'WITHDRAWAL' }; + + const andPredicate = { + and: [ + { field: 'amount', op: 'gt', value: 100 }, + { field: 'type', op: 'eq', value: 'WITHDRAWAL' }, + ], + }; + + const orPredicate = { + or: [ + { field: 'type', op: 'eq', value: 'DEPOSIT' }, + { field: 'amount', op: 'gt', value: 400 }, + ], + }; + + expect(evaluateFilterPredicate(andPredicate, payload)).toBe(true); + expect(evaluateFilterPredicate(orPredicate, payload)).toBe(true); + }); +}); diff --git a/src/utils/userWebhookFilter.ts b/src/utils/userWebhookFilter.ts new file mode 100644 index 0000000..94cbfad --- /dev/null +++ b/src/utils/userWebhookFilter.ts @@ -0,0 +1,130 @@ +/** + * Server-side predicate grammar for User Webhook Event Filtering (#368). + * + * Filter schema example: + * { + * "field": "transaction.type", + * "op": "eq", + * "value": "WITHDRAWAL" + * } + * Or combined with and / or: + * { + * "and": [ + * { "field": "amount", "op": "gt", "value": 100 }, + * { "field": "type", "op": "eq", "value": "WITHDRAWAL" } + * ] + * } + */ + +export interface FilterCondition { + field?: string; + op?: 'eq' | 'gt' | 'lt' | 'in'; + value?: any; + and?: FilterCondition[]; + or?: FilterCondition[]; +} + +const MAX_FILTER_DEPTH = 5; + +/** + * Validates a filterJson predicate object. Throws if invalid or depth exceeds max allowed. + */ +export function validateFilterPredicate(filter: any, depth = 1): boolean { + if (!filter || typeof filter !== 'object') { + throw new Error('Filter must be a valid JSON object'); + } + + if (depth > MAX_FILTER_DEPTH) { + throw new Error(`Filter predicate depth exceeds maximum allowed (${MAX_FILTER_DEPTH})`); + } + + if (Array.isArray(filter.and)) { + if (filter.and.length === 0) { + throw new Error('Filter "and" array must not be empty'); + } + for (const sub of filter.and) { + validateFilterPredicate(sub, depth + 1); + } + return true; + } + + if (Array.isArray(filter.or)) { + if (filter.or.length === 0) { + throw new Error('Filter "or" array must not be empty'); + } + for (const sub of filter.or) { + validateFilterPredicate(sub, depth + 1); + } + return true; + } + + if (typeof filter.field !== 'string' || !filter.field.trim()) { + throw new Error('Filter condition must contain a valid string "field"'); + } + + const validOps = ['eq', 'gt', 'lt', 'in']; + if (!filter.op || !validOps.includes(filter.op)) { + throw new Error(`Filter condition "op" must be one of: ${validOps.join(', ')}`); + } + + if (filter.op === 'in' && !Array.isArray(filter.value)) { + throw new Error('Filter condition with "in" op must provide an array "value"'); + } + + if (filter.value === undefined) { + throw new Error('Filter condition must specify a "value"'); + } + + return true; +} + +/** + * Extracts a nested property from a payload using dot notation (e.g. "transaction.type"). + */ +function getNestedValue(obj: any, path: string): any { + if (!obj || typeof obj !== 'object') return undefined; + const parts = path.split('.'); + let curr = obj; + for (const part of parts) { + if (curr === null || curr === undefined || typeof curr !== 'object') { + return undefined; + } + curr = curr[part]; + } + return curr; +} + +/** + * Evaluates a validated filter predicate against an event payload. + */ +export function evaluateFilterPredicate(filter: FilterCondition | null | undefined, payload: Record): boolean { + if (!filter || Object.keys(filter).length === 0) { + return true; // No filter specified means match all events + } + + if (Array.isArray(filter.and)) { + return filter.and.every((cond) => evaluateFilterPredicate(cond, payload)); + } + + if (Array.isArray(filter.or)) { + return filter.or.some((cond) => evaluateFilterPredicate(cond, payload)); + } + + if (!filter.field) return true; + + const actualValue = getNestedValue(payload, filter.field); + const expectedValue = filter.value; + + switch (filter.op) { + case 'eq': + return actualValue === expectedValue; + case 'gt': + return typeof actualValue === 'number' && typeof expectedValue === 'number' && actualValue > expectedValue; + case 'lt': + return typeof actualValue === 'number' && typeof expectedValue === 'number' && actualValue < expectedValue; + case 'in': + return Array.isArray(expectedValue) && expectedValue.includes(actualValue); + default: + return true; + } +} From a4fb3cc6870e11214828c3a166c698e6fad84f66 Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 14:18:55 +0100 Subject: [PATCH 2/7] feat: add user webhooks, SSE fallback, alert ack/snooze, and email delivery channel (#368, #369, #366, #367) - Add user-scoped outbound webhooks with per-user HMAC secrets, SSRF guards, and server-side event filters (#368) - Add Server-Sent Events (SSE) fallback transport, single-use stream tickets, and resumable Last-Event-ID replay (#369) - Add AlertFire / AlertAck models, snooze workflow, ackToken signing, and un-acknowledged escalation counting (#366) - Add provider-abstracted Mailer (SES/SMTP), verified opt-in flow, and bounce/complaint webhook processing (#367) - Add documentation (USER_WEBHOOKS.md, REALTIME_STREAMING.md, ALERTS.md, NOTIFICATIONS.md) and Jest unit test suite Closes #368, #369, #366, #367 --- prisma/schema.prisma | 3 ++- src/index.ts | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 06f5913..b43ac85 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -226,8 +226,9 @@ model User { portfolioAttributions PortfolioAttribution[] portfolioRiskAggregates PortfolioRiskAggregate[] userEvents UserEvent[] - userEventSequence UserEventSequence? userApiKeys UserApiKey[] + userWebhookEndpoints UserWebhookEndpoint[] + emailIdentity EmailIdentity? @@map("users") } diff --git a/src/index.ts b/src/index.ts index d0ebafc..2e9bf2f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -81,9 +81,10 @@ import referralsRouter from './routes/referrals' import recurringDepositRouter from './routes/recurring-deposits' import alertsRouter from './routes/alerts' import strategiesRouter from './routes/strategies' -import subAccountsRouter from './routes/sub-accounts' import keysRouter from './routes/keys' import sessionsRouter from './routes/sessions' +import streamRouter from './routes/stream' +import notificationsRouter from './routes/notifications' import { corsMiddleware, jsonBodyParser, @@ -301,9 +302,10 @@ const apiRoutes: ApiRoute[] = [ { path: 'deposit/recurring', handlers: [recurringDepositRouter] }, { path: 'alerts', handlers: [alertsRouter] }, { path: 'strategies', handlers: [strategiesRouter] }, - { path: 'sub-accounts', handlers: [subAccountsRouter] }, { path: 'keys', handlers: [keysRouter] }, { path: 'sessions', handlers: [sessionsRouter] }, + { path: 'stream', handlers: [streamRouter] }, + { path: 'notifications', handlers: [notificationsRouter] }, { path: 'admin', handlers: [adminRateLimiter, adminRouter] }, ] From 7b31e26446786387e9cac09c3d3625795c06f4d0 Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 14:46:17 +0100 Subject: [PATCH 3/7] fix(ci): fix vitest imports, type assertions, and prettier formatting (#368, #369, #366, #367) - Relocate test suites from src/ to tests/unit/ matching Jest roots config - Remove invalid vitest imports and rely on Jest test runner globals - Add explicit const type assertions for FilterCondition operators - Apply Prettier formatting across all updated src and test files - Fix npm run build and npm run format:check CI pipeline steps --- package-lock.json | 2906 +++++++++++++---- src/controllers/alert-ack-controller.ts | 170 +- src/controllers/email-identity-controller.ts | 156 +- src/controllers/stream-ticket-controller.ts | 24 +- src/controllers/user-webhook-controller.ts | 247 +- src/mail/mailProvider.ts | 136 +- src/mail/templates/index.ts | 35 +- src/middleware/streamAuth.test.ts | 36 - src/middleware/streamAuth.ts | 117 +- src/routes/notifications.ts | 14 +- src/routes/stream.ts | 16 +- src/services/userWebhookDispatcher.test.ts | 43 - src/services/userWebhookDispatcher.ts | 85 +- src/sse/handler.ts | 142 +- src/utils/ackToken.test.ts | 31 - src/utils/ackToken.ts | 56 +- src/utils/userWebhookFilter.test.ts | 69 - src/utils/userWebhookFilter.ts | 93 +- {src => tests/unit}/mail/mailProvider.test.ts | 51 +- tests/unit/middleware/streamAuth.test.ts | 38 + .../services/userWebhookDispatcher.test.ts | 58 + tests/unit/utils/ackToken.test.ts | 30 + tests/unit/utils/userWebhookFilter.test.ts | 135 + 23 files changed, 3224 insertions(+), 1464 deletions(-) delete mode 100644 src/middleware/streamAuth.test.ts delete mode 100644 src/services/userWebhookDispatcher.test.ts delete mode 100644 src/utils/ackToken.test.ts delete mode 100644 src/utils/userWebhookFilter.test.ts rename {src => tests/unit}/mail/mailProvider.test.ts (53%) create mode 100644 tests/unit/middleware/streamAuth.test.ts create mode 100644 tests/unit/services/userWebhookDispatcher.test.ts create mode 100644 tests/unit/utils/ackToken.test.ts create mode 100644 tests/unit/utils/userWebhookFilter.test.ts diff --git a/package-lock.json b/package-lock.json index 861bea7..d0474e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,9 +72,9 @@ } }, "node_modules/@anthropic-ai/sdk": { - "version": "0.112.3", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.112.3.tgz", - "integrity": "sha512-wjcozJlitVIuBEw9cj/xBuRznwkhcLmXmNzlFoeHbh4AvrDG3HGZrdvEOTTmobcbhjGkfOpKbmDTCQ4s9LQvCg==", + "version": "0.112.5", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.112.5.tgz", + "integrity": "sha512-FaaGkyS4/zW4n+8Pr9jQkyo5zWcBNw+R+heCLXdoKDUEuALbPALBk6zLAlmsU+veREiaATxVG0TwkK/cQ3NZsQ==", "license": "MIT", "dependencies": { "json-schema-to-ts": "^3.1.1", @@ -93,9 +93,9 @@ } }, "node_modules/@apm-js-collab/code-transformer": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.0.tgz", - "integrity": "sha512-aN3Oq8r1J3gPJtCwErP664gM0+HhM1I1lujPr9TMTCcEl/joQQbpGpeMdts9B1+W2wHMsvioDMv5F4PvMWE6gw==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.1.tgz", + "integrity": "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==", "license": "Apache-2.0", "dependencies": { "@types/estree": "^1.0.8", @@ -110,12 +110,12 @@ } }, "node_modules/@apm-js-collab/code-transformer-bundler-plugins": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.6.2.tgz", - "integrity": "sha512-5vBrtIEL+UVbO0YWWoyYG4QMgR+ZfnIL3xlteIkAmU7YaAPhc28k3md/NM14tnkfXKjKOn9yUzEA9AYUmNpvJg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.4.tgz", + "integrity": "sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==", "license": "MIT", "dependencies": { - "@apm-js-collab/code-transformer": "^0.18.0", + "@apm-js-collab/code-transformer": "^0.18.1", "es-module-lexer": "^2.1.0", "magic-string": "^0.30.21", "module-details-from-path": "^1.0.4" @@ -136,48 +136,38 @@ } }, "node_modules/@aws-sdk/client-ssm": { - "version": "3.1090.0", + "version": "3.1119.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1119.0.tgz", + "integrity": "sha512-Jw3sDgGQqTx7Y9P4ZkMOa0iDOjrBkEtHK6fBACucLPHFx9T1APMs7J5XxACcmF55yHN+QMDg8rsMMOAKJC1nDQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/credential-provider-node": "^3.972.70", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/fetch-http-handler": "^5.6.6", - "@smithy/node-http-handler": "^4.9.6", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/credential-provider-node": "^3.972.81", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/core": { - "version": "3.975.3", + "version": "3.977.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.9.tgz", + "integrity": "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.2", - "@aws-sdk/xml-builder": "^3.972.36", + "@aws-sdk/types": "^3.974.5", + "@aws-sdk/xml-builder": "^3.972.40", "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.29.4", - "@smithy/signature-v4": "^5.6.5", - "@smithy/types": "^4.16.1", + "@smithy/core": "^3.33.3", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.17.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -185,312 +175,192 @@ "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/core/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.59", + "version": "3.972.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.70.tgz", + "integrity": "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.61", + "version": "3.972.72", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.72.tgz", + "integrity": "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/fetch-http-handler": "^5.6.6", - "@smithy/node-http-handler": "^4.9.6", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/credential-provider-env": "^3.972.59", - "@aws-sdk/credential-provider-http": "^3.972.61", - "@aws-sdk/credential-provider-login": "^3.972.66", - "@aws-sdk/credential-provider-process": "^3.972.59", - "@aws-sdk/credential-provider-sso": "^3.973.3", - "@aws-sdk/credential-provider-web-identity": "^3.972.65", - "@aws-sdk/nested-clients": "^3.997.33", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/credential-provider-imds": "^4.4.9", - "@smithy/types": "^4.16.1", + "version": "3.973.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.15.tgz", + "integrity": "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/credential-provider-env": "^3.972.70", + "@aws-sdk/credential-provider-http": "^3.972.72", + "@aws-sdk/credential-provider-login": "^3.972.77", + "@aws-sdk/credential-provider-process": "^3.972.70", + "@aws-sdk/credential-provider-sso": "^3.973.14", + "@aws-sdk/credential-provider-web-identity": "^3.972.76", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.66", + "version": "3.972.77", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.77.tgz", + "integrity": "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/nested-clients": "^3.997.33", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-login/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.70", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.59", - "@aws-sdk/credential-provider-http": "^3.972.61", - "@aws-sdk/credential-provider-ini": "^3.973.4", - "@aws-sdk/credential-provider-process": "^3.972.59", - "@aws-sdk/credential-provider-sso": "^3.973.3", - "@aws-sdk/credential-provider-web-identity": "^3.972.65", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/credential-provider-imds": "^4.4.9", - "@smithy/types": "^4.16.1", + "version": "3.972.81", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.81.tgz", + "integrity": "sha512-Rml+WitoFvXmv6JZ18U/xGdGDGGvB/mOin0ya0lTnTrdC0Z1lrVxTYh7iNklZBcvcRMrs4DoEf6xy1KWyrLQQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.70", + "@aws-sdk/credential-provider-http": "^3.972.72", + "@aws-sdk/credential-provider-ini": "^3.973.15", + "@aws-sdk/credential-provider-process": "^3.972.70", + "@aws-sdk/credential-provider-sso": "^3.973.14", + "@aws-sdk/credential-provider-web-identity": "^3.972.76", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.59", + "version": "3.972.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.70.tgz", + "integrity": "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.3", + "version": "3.973.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.14.tgz", + "integrity": "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/nested-clients": "^3.997.33", - "@aws-sdk/token-providers": "3.1088.0", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/token-providers": "3.1116.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.65", + "version": "3.972.76", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.76.tgz", + "integrity": "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/nested-clients": "^3.997.33", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.33", + "version": "3.997.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.44.tgz", + "integrity": "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/signature-v4-multi-region": "^3.996.41", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/fetch-http-handler": "^5.6.6", - "@smithy/node-http-handler": "^4.9.6", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/signature-v4-multi-region": "^3.996.46", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.41", + "version": "3.996.46", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.46.tgz", + "integrity": "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.2", - "@smithy/signature-v4": "^5.6.5", - "@smithy/types": "^4.16.1", + "@aws-sdk/types": "^3.974.5", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -498,41 +368,31 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1088.0", + "version": "3.1116.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1116.0.tgz", + "integrity": "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.975.3", - "@aws-sdk/nested-clients": "^3.997.33", - "@aws-sdk/types": "^3.974.2", - "@smithy/core": "^3.29.4", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/nested-clients": "^3.997.44", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/token-providers/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/types": { - "version": "3.974.2", + "version": "3.974.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.5.tgz", + "integrity": "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -540,11 +400,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.36", + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.40.tgz", + "integrity": "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -553,6 +415,8 @@ }, "node_modules/@aws/lambda-invoke-store": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -561,6 +425,8 @@ }, "node_modules/@babel/code-frame": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { @@ -574,6 +440,8 @@ }, "node_modules/@babel/compat-data": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -582,6 +450,8 @@ }, "node_modules/@babel/core": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { @@ -611,6 +481,8 @@ }, "node_modules/@babel/core/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": { @@ -618,12 +490,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.7", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -634,6 +508,8 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { @@ -649,6 +525,8 @@ }, "node_modules/@babel/helper-compilation-targets/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": { @@ -657,6 +535,8 @@ }, "node_modules/@babel/helper-globals": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -665,6 +545,8 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { @@ -677,6 +559,8 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { @@ -693,6 +577,8 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -701,6 +587,8 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -709,6 +597,8 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -717,6 +607,8 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -725,6 +617,8 @@ }, "node_modules/@babel/helpers": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { @@ -736,11 +630,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.7", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -751,6 +647,8 @@ }, "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": { @@ -762,6 +660,8 @@ }, "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": { @@ -773,6 +673,8 @@ }, "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": { @@ -784,6 +686,8 @@ }, "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": { @@ -798,6 +702,8 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { @@ -812,6 +718,8 @@ }, "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": { @@ -823,6 +731,8 @@ }, "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": { @@ -834,6 +744,8 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, "license": "MIT", "dependencies": { @@ -848,6 +760,8 @@ }, "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": { @@ -859,6 +773,8 @@ }, "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": { @@ -870,6 +786,8 @@ }, "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": { @@ -881,6 +799,8 @@ }, "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": { @@ -892,6 +812,8 @@ }, "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": { @@ -903,6 +825,8 @@ }, "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": { @@ -914,6 +838,8 @@ }, "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": { @@ -928,6 +854,8 @@ }, "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": { @@ -942,6 +870,8 @@ }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, "license": "MIT", "dependencies": { @@ -965,6 +895,8 @@ }, "node_modules/@babel/template": { "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -977,16 +909,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.29.7", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", + "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", + "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -994,7 +928,9 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -1007,6 +943,8 @@ }, "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" }, @@ -1021,6 +959,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -1032,6 +972,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -1051,7 +993,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -1069,6 +1013,8 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -1077,6 +1023,8 @@ }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1097,10 +1045,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -1122,6 +1080,8 @@ }, "node_modules/@eslint/eslintrc/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": { @@ -1133,6 +1093,8 @@ }, "node_modules/@eslint/js": { "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -1172,6 +1134,9 @@ }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1185,6 +1150,8 @@ }, "node_modules/@humanwhocodes/config-array/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": { @@ -1196,6 +1163,8 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1208,11 +1177,16 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, "license": "BSD-3-Clause" }, "node_modules/@ioredis/commands": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", "license": "MIT" }, "node_modules/@isaacs/cliui": { @@ -1233,9 +1207,9 @@ } }, "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==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "license": "MIT", "engines": { "node": ">=12" @@ -1313,6 +1287,8 @@ }, "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": { @@ -1328,6 +1304,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/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": { @@ -1339,9 +1317,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -1363,6 +1341,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/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": { @@ -1374,6 +1354,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/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": { @@ -1388,6 +1370,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/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": { @@ -1399,6 +1383,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/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": { @@ -1407,6 +1393,8 @@ }, "node_modules/@istanbuljs/schema": { "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", "engines": { @@ -1415,6 +1403,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": { @@ -1431,6 +1421,8 @@ }, "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": { @@ -1477,6 +1469,8 @@ }, "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": { @@ -1491,6 +1485,8 @@ }, "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": { @@ -1503,6 +1499,8 @@ }, "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": { @@ -1514,6 +1512,8 @@ }, "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": { @@ -1530,6 +1530,8 @@ }, "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": { @@ -1544,6 +1546,8 @@ }, "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": { @@ -1586,6 +1590,8 @@ }, "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": { @@ -1597,6 +1603,8 @@ }, "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": { @@ -1610,6 +1618,8 @@ }, "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": { @@ -1624,6 +1634,8 @@ }, "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": { @@ -1638,6 +1650,8 @@ }, "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": { @@ -1663,6 +1677,8 @@ }, "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": { @@ -1679,6 +1695,8 @@ }, "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": { @@ -1688,6 +1706,8 @@ }, "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": { @@ -1697,6 +1717,8 @@ }, "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==", "dev": true, "license": "MIT", "engines": { @@ -1705,10 +1727,14 @@ }, "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==", "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": { @@ -1728,13 +1754,17 @@ }, "node_modules/@noble/ed25519": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.1.0.tgz", + "integrity": "sha512-pfcObRY3CtvwfaG9Mt5XqZdKmAQppl37tHUeuBhDUbiwJBCVY4/A4lbMvb1xKhMDx96AqAqZpMWuBX1HulhX4g==", "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "2.2.0", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", "license": "MIT", "engines": { "node": ">= 20.19.0" @@ -1745,6 +1775,8 @@ }, "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": { @@ -1757,6 +1789,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": { @@ -1765,6 +1799,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": { @@ -1876,6 +1912,21 @@ "@opentelemetry/api": "^1.9.0" } }, + "node_modules/@opentelemetry/configuration/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/context-async-hooks": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.9.0.tgz", @@ -1889,9 +1940,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", - "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -1923,6 +1974,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/exporter-logs-otlp-http": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.220.0.tgz", @@ -1942,6 +2008,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/exporter-logs-otlp-proto": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.220.0.tgz", @@ -1981,33 +2062,44 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.220.0.tgz", - "integrity": "sha512-Yqt3RBw/bRVncaE9qIIhk4WfjbAQqXuP9FgAaU+IKPndnLEp/cUqZlSC324+bpmduRz7DoTjig8Ub0PeILWXUA==", + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.9.0", - "@opentelemetry/otlp-exporter-base": "0.220.0", - "@opentelemetry/otlp-transformer": "0.220.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-metrics": "2.9.0" + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.220.0.tgz", - "integrity": "sha512-lyO+IQBdSvqHN/ZOW/OzrSWemtfD+HgWngn+HBNLhjy0YrCQQTz0OE/kSekH2Pl340dn9DWzhqHdz5Eftr+HLA==", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.220.0.tgz", + "integrity": "sha512-Yqt3RBw/bRVncaE9qIIhk4WfjbAQqXuP9FgAaU+IKPndnLEp/cUqZlSC324+bpmduRz7DoTjig8Ub0PeILWXUA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.9.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.220.0", "@opentelemetry/otlp-exporter-base": "0.220.0", "@opentelemetry/otlp-transformer": "0.220.0", "@opentelemetry/resources": "2.9.0", @@ -2020,54 +2112,49 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.220.0.tgz", - "integrity": "sha512-JZD5DL/NBpVd2BHefvYosm3G40UZ/KzExLv5tc0eZe0CtrsHHtcOk3YPUxR2EINmUeBf8+w5UReTV8fFPn95lA==", + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-metrics": "2.9.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.220.0.tgz", - "integrity": "sha512-bv1xmNhmNwIM6MdUBw4yYuJeVcEViVLk3uD69vOQMwueHBnfyl/u0HnBlB1FNY/Te0UOzJzvcbyR8wN6b+iGbA==", + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/otlp-exporter-base": "0.220.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.220.0", - "@opentelemetry/otlp-transformer": "0.220.0", - "@opentelemetry/sdk-trace": "2.9.0" + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.220.0.tgz", - "integrity": "sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.220.0.tgz", + "integrity": "sha512-lyO+IQBdSvqHN/ZOW/OzrSWemtfD+HgWngn+HBNLhjy0YrCQQTz0OE/kSekH2Pl340dn9DWzhqHdz5Eftr+HLA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.9.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.220.0", "@opentelemetry/otlp-exporter-base": "0.220.0", "@opentelemetry/otlp-transformer": "0.220.0", "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-trace": "2.9.0" + "@opentelemetry/sdk-metrics": "2.9.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -2076,6 +2163,155 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.220.0.tgz", + "integrity": "sha512-JZD5DL/NBpVd2BHefvYosm3G40UZ/KzExLv5tc0eZe0CtrsHHtcOk3YPUxR2EINmUeBf8+w5UReTV8fFPn95lA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/sdk-metrics": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.220.0.tgz", + "integrity": "sha512-bv1xmNhmNwIM6MdUBw4yYuJeVcEViVLk3uD69vOQMwueHBnfyl/u0HnBlB1FNY/Te0UOzJzvcbyR8wN6b+iGbA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/otlp-exporter-base": "0.220.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.220.0", + "@opentelemetry/otlp-transformer": "0.220.0", + "@opentelemetry/sdk-trace": "2.9.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.220.0.tgz", + "integrity": "sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/otlp-exporter-base": "0.220.0", + "@opentelemetry/otlp-transformer": "0.220.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/sdk-trace": "2.9.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.220.0.tgz", @@ -2095,6 +2331,37 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/exporter-zipkin": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.9.0.tgz", @@ -2113,6 +2380,37 @@ "@opentelemetry/api": "^1.0.0" } }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@opentelemetry/instrumentation": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", @@ -2410,6 +2708,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/instrumentation-ioredis": { "version": "0.68.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.68.0.tgz", @@ -2827,6 +3140,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.220.0.tgz", @@ -2845,6 +3173,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/otlp-transformer": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.220.0.tgz", @@ -2865,11 +3208,14 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/propagator-aws-xray": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-2.2.0.tgz", - "integrity": "sha512-Yjvt2EjL+tfpkVOdKbhTPgpM4SIAez9nG6Q/QjQ3yfcJcjIWp59ph70SLfvmkSL6++3DCnuBG3iWcB18PwWavQ==", + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, "engines": { "node": "^18.19.0 || >=20.6.0" }, @@ -2877,10 +3223,38 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/propagator-b3": { + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.9.0.tgz", - "integrity": "sha512-WrOT1WsOUG+B7hstD2RYoMPIOK76G8E9AQHhMjUvrQaGx/oA7rPWQvvr1Rqv7+yy4R0ZMVwWLC4vW2xnkgWPAQ==", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-aws-xray": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-2.2.0.tgz", + "integrity": "sha512-Yjvt2EjL+tfpkVOdKbhTPgpM4SIAez9nG6Q/QjQ3yfcJcjIWp59ph70SLfvmkSL6++3DCnuBG3iWcB18PwWavQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.9.0.tgz", + "integrity": "sha512-WrOT1WsOUG+B7hstD2RYoMPIOK76G8E9AQHhMjUvrQaGx/oA7rPWQvvr1Rqv7+yy4R0ZMVwWLC4vW2xnkgWPAQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.9.0" @@ -2892,6 +3266,21 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/propagator-jaeger": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.9.0.tgz", @@ -2907,6 +3296,21 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "node_modules/@opentelemetry/redis-common": { "version": "0.38.3", "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz", @@ -2929,77 +3333,328 @@ "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.0" + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.21.0.tgz", + "integrity": "sha512-Veavy+khoywR+Hv065SU5jucFTGTiW1KXo39CsJ+8wqdYYz8jiRJPnQ20Kd+X9HbV2+Abb0l5CrJIdxK1ZOqBg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.28.0.tgz", + "integrity": "sha512-YMMgH/ZIiZgy2CHTHjOBNEYhcsi/l67Q272vAgwMqegRFIME4KljDhmTmjLGzjE3b0sErLtXBqF8Y/3bKn89+Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.12.tgz", + "integrity": "sha512-EJRFfIY26whY0w5RDxMRXlfBDgDS001JYMHuOVuDBBsRrV4MBqoVajR9B0L9Vy728+w/HNVnSQkpJFacFr+klg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.55.0.tgz", + "integrity": "sha512-uWU27lJcTbeXDY+uWEapsIyMx8mKi14/IGvUY1DkmMmLQnKRibnbpZEsRVeWBPdjOWSjH9LfWTXp9yDcBHZOeg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "gcp-metadata": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", + "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.220.0.tgz", + "integrity": "sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.220.0", + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.9.0.tgz", + "integrity": "sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.220.0.tgz", + "integrity": "sha512-wHtGyHhSKHNH3fym33xRu4Ef/HXTFvX8eQ42xdQdEO9LYx9Y2qNyBDJytyqVlvmo6abWZlNYTUthuAGUMYqYnQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.220.0", + "@opentelemetry/configuration": "0.220.0", + "@opentelemetry/context-async-hooks": "2.9.0", + "@opentelemetry/core": "2.9.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.220.0", + "@opentelemetry/exporter-logs-otlp-http": "0.220.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.220.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.220.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.220.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.220.0", + "@opentelemetry/exporter-prometheus": "0.220.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.220.0", + "@opentelemetry/exporter-trace-otlp-http": "0.220.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.220.0", + "@opentelemetry/exporter-zipkin": "2.9.0", + "@opentelemetry/instrumentation": "0.220.0", + "@opentelemetry/otlp-exporter-base": "0.220.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.220.0", + "@opentelemetry/propagator-b3": "2.9.0", + "@opentelemetry/propagator-jaeger": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/sdk-logs": "0.220.0", + "@opentelemetry/sdk-metrics": "2.9.0", + "@opentelemetry/sdk-trace": "2.9.0", + "@opentelemetry/sdk-trace-base": "2.9.0", + "@opentelemetry/sdk-trace-node": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/resource-detector-aws": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.20.0.tgz", - "integrity": "sha512-3ZkhvVHqJgJ75ObpZQwZIBySNfd4h772QRb2NBPU1A3lSUzDlRen9x5Kzv/K7HNkL/Azl8XEanykwa73YjWmQA==", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.9.0.tgz", + "integrity": "sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/context-async-hooks": "2.9.0", + "@opentelemetry/core": "2.9.0", + "@opentelemetry/sdk-trace-base": "2.9.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/resource-detector-azure": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.28.0.tgz", - "integrity": "sha512-YMMgH/ZIiZgy2CHTHjOBNEYhcsi/l67Q272vAgwMqegRFIME4KljDhmTmjLGzjE3b0sErLtXBqF8Y/3bKn89+Q==", + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.9.0.tgz", + "integrity": "sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.37.0" + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.11.tgz", - "integrity": "sha512-O7dMPH13+JZu+swtCsdq5702Fa2Q4MSHmwMoLxyqgWuPPtmDmao4s+V1ovfg225zW67quNDXFKdLf1q5/Elb9w==", + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.9.0.tgz", + "integrity": "sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0" + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/sdk-trace": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.55.0.tgz", - "integrity": "sha512-uWU27lJcTbeXDY+uWEapsIyMx8mKi14/IGvUY1DkmMmLQnKRibnbpZEsRVeWBPdjOWSjH9LfWTXp9yDcBHZOeg==", + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "gcp-metadata": "^8.0.0" + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/resources": { + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", @@ -3015,72 +3670,43 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.220.0.tgz", - "integrity": "sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==", + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.10.0.tgz", + "integrity": "sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.220.0", - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.9.0.tgz", - "integrity": "sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==", + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.10.0.tgz", + "integrity": "sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==", "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0" - }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <1.10.0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-node": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.220.0.tgz", - "integrity": "sha512-wHtGyHhSKHNH3fym33xRu4Ef/HXTFvX8eQ42xdQdEO9LYx9Y2qNyBDJytyqVlvmo6abWZlNYTUthuAGUMYqYnQ==", + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", + "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.220.0", - "@opentelemetry/configuration": "0.220.0", - "@opentelemetry/context-async-hooks": "2.9.0", - "@opentelemetry/core": "2.9.0", - "@opentelemetry/exporter-logs-otlp-grpc": "0.220.0", - "@opentelemetry/exporter-logs-otlp-http": "0.220.0", - "@opentelemetry/exporter-logs-otlp-proto": "0.220.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.220.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.220.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.220.0", - "@opentelemetry/exporter-prometheus": "0.220.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.220.0", - "@opentelemetry/exporter-trace-otlp-http": "0.220.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.220.0", - "@opentelemetry/exporter-zipkin": "2.9.0", - "@opentelemetry/instrumentation": "0.220.0", - "@opentelemetry/otlp-exporter-base": "0.220.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.220.0", - "@opentelemetry/propagator-b3": "2.9.0", - "@opentelemetry/propagator-jaeger": "2.9.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-logs": "0.220.0", - "@opentelemetry/sdk-metrics": "2.9.0", - "@opentelemetry/sdk-trace": "2.9.0", - "@opentelemetry/sdk-trace-base": "2.9.0", - "@opentelemetry/sdk-trace-node": "2.9.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3090,14 +3716,15 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.9.0.tgz", - "integrity": "sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==", + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", + "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3107,39 +3734,35 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-base": { + "node_modules/@opentelemetry/sdk-trace/node_modules/@opentelemetry/core": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.9.0.tgz", - "integrity": "sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-trace": "2.9.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-node": { + "node_modules/@opentelemetry/sdk-trace/node_modules/@opentelemetry/resources": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.9.0.tgz", - "integrity": "sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/context-async-hooks": "2.9.0", "@opentelemetry/core": "2.9.0", - "@opentelemetry/sdk-trace-base": "2.9.0" + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "node_modules/@opentelemetry/semantic-conventions": { @@ -3168,6 +3791,8 @@ }, "node_modules/@paralleldrive/cuid2": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dev": true, "license": "MIT", "dependencies": { @@ -3176,6 +3801,8 @@ }, "node_modules/@paralleldrive/cuid2/node_modules/@noble/hashes": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, "license": "MIT", "engines": { @@ -3197,6 +3824,8 @@ }, "node_modules/@prisma/client": { "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -3213,11 +3842,15 @@ }, "node_modules/@prisma/debug": { "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -3230,11 +3863,15 @@ }, "node_modules/@prisma/engines-version": { "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -3245,6 +3882,8 @@ }, "node_modules/@prisma/get-platform": { "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -3252,9 +3891,9 @@ } }, "node_modules/@prisma/instrumentation": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.8.0.tgz", - "integrity": "sha512-15aMqRqQtEMxSSVGNKP7wnFkDjm83tiIJmxioHvxl7xeKTRpvMTN2EbpRGJ9PvAYWn/mnqHdw4br/s49juZ87w==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.10.0.tgz", + "integrity": "sha512-NNeXgyAOfeuB2tJ8+XcsjFxaab69P02uV+D8HceOR7YnUq0+Wtjd1nuTxMZVpm/ZVGbIFrBPg5TDBjGUFU/evQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/instrumentation": "^0.207.0" @@ -3362,9 +4001,9 @@ "license": "BSD-3-Clause" }, "node_modules/@redocly/cli": { - "version": "2.40.0", - "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.40.0.tgz", - "integrity": "sha512-1uQ4GeNjhApy9EtypZgp70ZN5GC2JFfst3UkNEXSqkXgVIPGdEAnlz5Xwgax/4cEUGOvaZoM3X25iSQcqbplFg==", + "version": "2.49.0", + "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.49.0.tgz", + "integrity": "sha512-87UuARXYrPIiHhd/UriamL4BApuPgB0E91coBtoPG6PK4giTwiuo3dI4RoyMGhsC7TwgAnL8PJuhCwGymgwbVA==", "dev": true, "license": "MIT", "bin": { @@ -3393,9 +4032,9 @@ } }, "node_modules/@sentry/core": { - "version": "10.66.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.66.0.tgz", - "integrity": "sha512-9UbgSvds7bMJsP561eWmeyMLcfOmnwxtnx2QuW3yLobzP2Ob7CyJCOzP4tGzlTAGDrzShkFEZhiyuBUKiEK2oQ==", + "version": "10.71.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.71.0.tgz", + "integrity": "sha512-OIjT7rzcWJjUC6r3eBT3Td1j0afDBMkbbx9jTocSD+ZSfc25eEU7hoIPS0WvfeIOTIN3y8bfQnXavwMReaNVHQ==", "license": "MIT", "dependencies": { "@sentry/conventions": "^0.16.0" @@ -3405,19 +4044,19 @@ } }, "node_modules/@sentry/node": { - "version": "10.66.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.66.0.tgz", - "integrity": "sha512-5Ow7iQiRjaSaEOmqEIkYV368hFFzShIZCXPoj+wX3JtOmKFrsNu6lr8/VJlQs7cyjFS6tzE50PrLrD8iAPS/8w==", + "version": "10.71.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.71.0.tgz", + "integrity": "sha512-bw2M/xkMu2+ATo6QWFmtTZTYp5LV1krt9/DTtYqtt4GmhXmXgdFPXCv6783AJI3HUoEMx2BcehhNbKzGrFXo/g==", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.9.1", "@opentelemetry/instrumentation": "^0.220.0", "@opentelemetry/sdk-trace-base": "^2.9.0", "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.66.0", - "@sentry/node-core": "10.66.0", - "@sentry/opentelemetry": "10.66.0", - "@sentry/server-utils": "10.66.0", + "@sentry/core": "10.71.0", + "@sentry/node-core": "10.71.0", + "@sentry/opentelemetry": "10.71.0", + "@sentry/server-utils": "10.71.0", "import-in-the-middle": "^3.0.0" }, "engines": { @@ -3425,14 +4064,14 @@ } }, "node_modules/@sentry/node-core": { - "version": "10.66.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.66.0.tgz", - "integrity": "sha512-SUnXHROqSdSetKgZC1goDEKCuMz3OmQ1h4rxzWeexLyqan+pensZXfLouP6jzZXlA8e/HP7uQg78LnfqYDcZlQ==", + "version": "10.71.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.71.0.tgz", + "integrity": "sha512-sxd0/ZW+Uda/17H0R7lB2Othm37VYcdwKdWdyHRizg872TfCX4UwTTPaoS2tMJAUjV2tVh83ZA0fsoC2q9SpjA==", "license": "MIT", "dependencies": { "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.66.0", - "@sentry/opentelemetry": "10.66.0", + "@sentry/core": "10.71.0", + "@sentry/opentelemetry": "10.71.0", "import-in-the-middle": "^3.0.0" }, "engines": { @@ -3464,9 +4103,9 @@ } }, "node_modules/@sentry/node-cpu-profiler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@sentry/node-cpu-profiler/-/node-cpu-profiler-2.4.2.tgz", - "integrity": "sha512-E6q+eE/sTpiofzW9jFKAx6ZQaDAoZDnsaLA/nRlkiK+K2X4k+hSyKhhLfw8PJlejB8edk7uxJF57r5JoRnyaPA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@sentry/node-cpu-profiler/-/node-cpu-profiler-2.4.3.tgz", + "integrity": "sha512-18g/yJKRUk4PNcnZYEPyT2nS+9UoFcTRrIrct1DbpfI7k//RtQ+8araZrR0WH5MT8xoiLCm67GEX/RcxDsI0iQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3478,13 +4117,13 @@ } }, "node_modules/@sentry/opentelemetry": { - "version": "10.66.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.66.0.tgz", - "integrity": "sha512-K5Y9IettN9yIOnpqCs40HRLGqaGUoaQ50+ZsLqX2kPCk3TzJVpKZ9icKwaJ4Nxm72QgGVT5Ovfr/9FXbgd3b/Q==", + "version": "10.71.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.71.0.tgz", + "integrity": "sha512-YgeL0xTObKma3MuOrt+/6M/f6mo/Z08LHh3OxPomzQpgpCgCLGyJ/739cDSSH1LRjqWdPQtoia5asl5ZRdhWmw==", "license": "MIT", "dependencies": { "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.66.0" + "@sentry/core": "10.71.0" }, "engines": { "node": ">=18" @@ -3496,14 +4135,14 @@ } }, "node_modules/@sentry/profiling-node": { - "version": "10.66.0", - "resolved": "https://registry.npmjs.org/@sentry/profiling-node/-/profiling-node-10.66.0.tgz", - "integrity": "sha512-l37MXflBZPiZHKHhW/oe5PlUy9G9fQHPea/vVCf/4J4Cv82APJ58wE49qP+elkvygyUoed0surijdBnh4rZIVQ==", + "version": "10.71.0", + "resolved": "https://registry.npmjs.org/@sentry/profiling-node/-/profiling-node-10.71.0.tgz", + "integrity": "sha512-jdiEDX0f6nwc9TAnbGSrxCt4lHDOyTfky26oV8Q/H2A/BZjhGCZnVnWgEefrQPa9ceA74lTkuJe0xY+BLDa4ww==", "license": "MIT", "dependencies": { - "@sentry/core": "10.66.0", - "@sentry/node": "10.66.0", - "@sentry/node-cpu-profiler": "^2.4.2" + "@sentry/core": "10.71.0", + "@sentry/node": "10.71.0", + "@sentry/node-cpu-profiler": "^2.4.3" }, "bin": { "sentry-prune-profiler-binaries": "scripts/prune-profiler-binaries.js" @@ -3513,16 +4152,16 @@ } }, "node_modules/@sentry/server-utils": { - "version": "10.66.0", - "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.66.0.tgz", - "integrity": "sha512-h9EM9Wz9Mc6w2Vn7Fyh6ozjy4JC2UbUvWf9Ra1HYA4KMeYqjFA5/o0oB4pg4w/TF+rb+9OF/HNqxjuczxpudpA==", + "version": "10.71.0", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.71.0.tgz", + "integrity": "sha512-zdyShKNsghzPGVWVRzc7oybYTsRZdKgdPtq+dsksImo1b6n7ILlD7eYx1Q0eAOZoWkDqDmm+Ml6MQb76IT1eTA==", "license": "MIT", "dependencies": { - "@apm-js-collab/code-transformer": "^0.18.0", - "@apm-js-collab/code-transformer-bundler-plugins": "^0.6.1", + "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", "@apm-js-collab/tracing-hooks": "^0.13.0", "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.66.0" + "@sentry/core": "10.71.0", + "meriyah": "^6.1.4" }, "engines": { "node": ">=18" @@ -3530,11 +4169,15 @@ }, "node_modules/@sinclair/typebox": { "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", "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": { @@ -3543,33 +4186,37 @@ }, "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/@smithy/credential-provider-imds": { - "version": "4.4.10", + "node_modules/@smithy/core": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.33.3.tgz", + "integrity": "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.29.5", - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/credential-provider-imds/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", + "node_modules/@smithy/credential-provider-imds": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", + "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -3577,26 +4224,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.6.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.29.5", - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.7.2.tgz", + "integrity": "sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -3604,26 +4239,14 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.9.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.29.5", - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.11.3.tgz", + "integrity": "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -3631,26 +4254,14 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.6.6", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.29.5", - "@smithy/types": "^4.16.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4/node_modules/@smithy/core": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.5.tgz", - "integrity": "sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.3.tgz", + "integrity": "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -3658,7 +4269,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.16.1", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.2.tgz", + "integrity": "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3686,6 +4299,8 @@ }, "node_modules/@stellar/js-xdr": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-4.0.0.tgz", + "integrity": "sha512-+NmNa7Tk5BI5XFdy/6xGTqAN4J9a9KgCrCGhj2uEUTCBhLkch0M+QbKzNH8zEnejWe0p8w+0q5hUVX6L3OzoVA==", "license": "Apache-2.0", "engines": { "node": ">=20.0.0", @@ -3693,17 +4308,17 @@ } }, "node_modules/@stellar/stellar-sdk": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-16.0.1.tgz", - "integrity": "sha512-bxKohaiyKVqoudRhbOOHeHhHIaeYV5Zab4rCjxhP4Ty1h1ozTLBOv8lWFnZz9ilBzXG8Bb7usQI3rlEcfvUynA==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-16.2.0.tgz", + "integrity": "sha512-FV/Rm11QvrFzR5X9fIfb6Pg30KyYyrRkNezELGFmYDAYrzoPTjqo7vklnEPd2HEontzjJmZizWk8CjyIh5vp0w==", "license": "Apache-2.0", "dependencies": { "@noble/ed25519": "^3.1.0", "@noble/hashes": "^2.2.0", "@stellar/js-xdr": "4.0.0", - "axios": "1.16.1", + "axios": "1.18.0", "base32.js": "^0.1.0", - "bignumber.js": "^11.1.1", + "bignumber.js": "^11.1.4", "buffer": "^6.0.3", "commander": "^14.0.3", "eventsource": "^4.1.0", @@ -3719,22 +4334,30 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.12", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", "dev": 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==", "dev": 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==", "dev": 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==", "dev": true, "license": "MIT" }, @@ -3746,6 +4369,8 @@ }, "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": { @@ -3758,6 +4383,8 @@ }, "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": { @@ -3766,6 +4393,8 @@ }, "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": { @@ -3775,6 +4404,8 @@ }, "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": { @@ -3790,6 +4421,8 @@ }, "node_modules/@types/body-parser": { "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", "dependencies": { @@ -3808,6 +4441,8 @@ }, "node_modules/@types/connect": { "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -3815,10 +4450,14 @@ }, "node_modules/@types/cookiejar": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", "license": "MIT" }, "node_modules/@types/cors": { "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dev": true, "license": "MIT", "dependencies": { @@ -3833,6 +4472,8 @@ }, "node_modules/@types/express": { "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", "dependencies": { @@ -3844,6 +4485,8 @@ }, "node_modules/@types/express-serve-static-core": { "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", "dev": true, "license": "MIT", "dependencies": { @@ -3855,6 +4498,8 @@ }, "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": { @@ -3863,16 +4508,22 @@ }, "node_modules/@types/http-errors": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, "license": "MIT" }, "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": { @@ -3881,6 +4532,8 @@ }, "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": { @@ -3889,6 +4542,8 @@ }, "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": { @@ -3898,11 +4553,15 @@ }, "node_modules/@types/js-yaml": { "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true, "license": "MIT" }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "dev": true, "license": "MIT", "dependencies": { @@ -3921,15 +4580,21 @@ }, "node_modules/@types/methods": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", "license": "MIT" }, "node_modules/@types/mime": { "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true, "license": "MIT" }, "node_modules/@types/ms": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "dev": true, "license": "MIT" }, @@ -3944,6 +4609,8 @@ }, "node_modules/@types/node": { "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3951,6 +4618,8 @@ }, "node_modules/@types/node-cron": { "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-3.0.11.tgz", + "integrity": "sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==", "license": "MIT" }, "node_modules/@types/oracledb": { @@ -3984,16 +4653,22 @@ }, "node_modules/@types/qs": { "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true, "license": "MIT" }, "node_modules/@types/send": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4002,6 +4677,8 @@ }, "node_modules/@types/serve-static": { "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, "license": "MIT", "dependencies": { @@ -4012,6 +4689,8 @@ }, "node_modules/@types/serve-static/node_modules/@types/send": { "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", "dev": true, "license": "MIT", "dependencies": { @@ -4021,11 +4700,15 @@ }, "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/superagent": { "version": "8.1.11", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.11.tgz", + "integrity": "sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==", "license": "MIT", "dependencies": { "@types/cookiejar": "^2.1.5", @@ -4036,6 +4719,8 @@ }, "node_modules/@types/supertest": { "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.1.tgz", + "integrity": "sha512-4CbBvoYVLHL7+yhbYrZET0vsvuyXTC05aRe7dNQkwMzm56auceoy6Yu3K50uZmwfHna1os3CMSgM/3QVkUtPTw==", "license": "MIT", "dependencies": { "@types/methods": "^1.1.4", @@ -4044,6 +4729,8 @@ }, "node_modules/@types/swagger-ui-express": { "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", + "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", "dev": true, "license": "MIT", "dependencies": { @@ -4078,6 +4765,8 @@ }, "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": { @@ -4086,21 +4775,23 @@ }, "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/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.68.0.tgz", + "integrity": "sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/type-utils": "8.68.0", + "@typescript-eslint/utils": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -4113,32 +4804,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", + "@typescript-eslint/parser": "^8.68.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", - "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.68.0.tgz", + "integrity": "sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", "debug": "^4.4.3" }, "engines": { @@ -4154,14 +4835,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.68.0.tgz", + "integrity": "sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", + "@typescript-eslint/tsconfig-utils": "^8.68.0", + "@typescript-eslint/types": "^8.68.0", "debug": "^4.4.3" }, "engines": { @@ -4176,14 +4857,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.68.0.tgz", + "integrity": "sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4194,9 +4875,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.68.0.tgz", + "integrity": "sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==", "dev": true, "license": "MIT", "engines": { @@ -4211,15 +4892,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.68.0.tgz", + "integrity": "sha512-X77zqoY1EjeWGs/0JNxeaMfp5C5lIz4Tw8y66F1Ne8Faq6g424sBNYM6xBAqElfGZPLpWS+CZAp0DXyKDzWiHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/utils": "8.68.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -4236,9 +4917,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.68.0.tgz", + "integrity": "sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==", "dev": true, "license": "MIT", "engines": { @@ -4250,16 +4931,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.68.0.tgz", + "integrity": "sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/project-service": "8.68.0", + "@typescript-eslint/tsconfig-utils": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -4278,16 +4959,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.68.0.tgz", + "integrity": "sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4302,13 +4983,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.68.0.tgz", + "integrity": "sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/types": "8.68.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4333,12 +5014,16 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.3", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", "dev": true, "license": "ISC" }, "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", @@ -4349,7 +5034,9 @@ } }, "node_modules/acorn": { - "version": "8.17.0", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4369,6 +5056,8 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4377,6 +5066,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -4388,6 +5079,8 @@ }, "node_modules/agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", "dependencies": { "debug": "4" @@ -4398,6 +5091,8 @@ }, "node_modules/ajv": { "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -4413,6 +5108,8 @@ }, "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": { @@ -4427,6 +5124,8 @@ }, "node_modules/ansi-escapes/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": { @@ -4438,6 +5137,8 @@ }, "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" @@ -4445,6 +5146,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" @@ -4458,6 +5161,8 @@ }, "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": { @@ -4470,19 +5175,27 @@ }, "node_modules/arg": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, "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/asap": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true, "license": "MIT" }, @@ -4503,22 +5216,26 @@ }, "node_modules/asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/axios": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", - "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", + "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "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": { @@ -4539,6 +5256,8 @@ }, "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": { @@ -4554,6 +5273,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": { @@ -4569,6 +5290,8 @@ }, "node_modules/babel-plugin-istanbul/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": { @@ -4577,6 +5300,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": { @@ -4591,6 +5316,8 @@ }, "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": { @@ -4616,6 +5343,8 @@ }, "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": { @@ -4640,6 +5369,8 @@ }, "node_modules/base32.js": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -4647,6 +5378,8 @@ }, "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", @@ -4664,7 +5397,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.43", + "version": "2.11.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", + "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4685,10 +5420,14 @@ }, "node_modules/bignumber.js": { "version": "11.1.5", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz", + "integrity": "sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==", "license": "MIT" }, "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": { @@ -4706,6 +5445,8 @@ }, "node_modules/body-parser": { "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -4728,6 +5469,8 @@ }, "node_modules/body-parser/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" @@ -4735,10 +5478,14 @@ }, "node_modules/body-parser/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/bowser": { "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "dev": true, "license": "MIT" }, @@ -4756,6 +5503,8 @@ }, "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": { @@ -4766,7 +5515,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.6", + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, "funding": [ { @@ -4784,11 +5535,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.42", - "caniuse-lite": "^1.0.30001803", - "electron-to-chromium": "^1.5.389", - "node-releases": "^2.0.51", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" }, "bin": { "browserslist": "cli.js" @@ -4797,29 +5548,10 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/browserslist/node_modules/caniuse-lite": { - "version": "1.0.30001806", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", - "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", - "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/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": { @@ -4831,6 +5563,8 @@ }, "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": { @@ -4839,6 +5573,8 @@ }, "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", @@ -4867,11 +5603,15 @@ }, "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==", "dev": true, "license": "MIT" }, "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" @@ -4879,6 +5619,8 @@ }, "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", @@ -4890,6 +5632,8 @@ }, "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", @@ -4904,6 +5648,8 @@ }, "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": { @@ -4912,14 +5658,39 @@ }, "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.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "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==", "dev": true, "license": "MIT", "dependencies": { @@ -4935,6 +5706,8 @@ }, "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": { @@ -4943,6 +5716,8 @@ }, "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": { @@ -4966,6 +5741,8 @@ }, "node_modules/chokidar/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": { @@ -4977,6 +5754,8 @@ }, "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": [ { @@ -4990,13 +5769,15 @@ } }, "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz", + "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==", "license": "MIT" }, "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", @@ -5009,6 +5790,8 @@ }, "node_modules/cluster-key-slot": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", "license": "Apache-2.0", "engines": { "node": ">=0.10.0" @@ -5016,6 +5799,8 @@ }, "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": { @@ -5025,6 +5810,8 @@ }, "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" }, @@ -5043,6 +5830,8 @@ }, "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" @@ -5053,6 +5842,8 @@ }, "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/color-string": { @@ -5068,9 +5859,9 @@ } }, "node_modules/color-string/node_modules/color-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", - "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==", "license": "MIT", "engines": { "node": ">=12.20" @@ -5089,9 +5880,9 @@ } }, "node_modules/color/node_modules/color-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", - "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==", "license": "MIT", "engines": { "node": ">=12.20" @@ -5099,6 +5890,8 @@ }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -5109,6 +5902,8 @@ }, "node_modules/commander": { "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "license": "MIT", "engines": { "node": ">=20" @@ -5116,6 +5911,8 @@ }, "node_modules/component-emitter": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, "license": "MIT", "funding": { @@ -5124,6 +5921,8 @@ }, "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" @@ -5134,6 +5933,8 @@ }, "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" @@ -5141,11 +5942,15 @@ }, "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" @@ -5153,15 +5958,21 @@ }, "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/cookiejar": { "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true, "license": "MIT" }, "node_modules/cors": { "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -5177,6 +5988,8 @@ }, "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": { @@ -5197,11 +6010,15 @@ }, "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==", "dev": 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", @@ -5222,13 +6039,15 @@ } }, "node_modules/dayjs": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", - "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", "license": "MIT" }, "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" @@ -5244,6 +6063,8 @@ }, "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==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5257,11 +6078,15 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "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": { @@ -5270,6 +6095,8 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -5277,6 +6104,8 @@ }, "node_modules/denque": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "license": "Apache-2.0", "engines": { "node": ">=0.10" @@ -5284,6 +6113,8 @@ }, "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" @@ -5291,6 +6122,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", @@ -5308,6 +6141,8 @@ }, "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": { @@ -5316,6 +6151,8 @@ }, "node_modules/dezalgo": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, "license": "ISC", "dependencies": { @@ -5325,6 +6162,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==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -5333,6 +6172,8 @@ }, "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": { @@ -5341,6 +6182,8 @@ }, "node_modules/doctrine": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5352,6 +6195,8 @@ }, "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" @@ -5362,6 +6207,8 @@ }, "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", @@ -5389,15 +6236,21 @@ }, "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.393", + "version": "1.5.415", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", + "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", "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": { @@ -5409,6 +6262,8 @@ }, "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/enabled": { @@ -5419,6 +6274,8 @@ }, "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" @@ -5426,6 +6283,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": { @@ -5434,6 +6293,8 @@ }, "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" @@ -5441,19 +6302,23 @@ }, "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-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5464,6 +6329,8 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5477,6 +6344,8 @@ }, "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" @@ -5484,10 +6353,14 @@ }, "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": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -5499,6 +6372,9 @@ }, "node_modules/eslint": { "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { @@ -5553,6 +6429,8 @@ }, "node_modules/eslint-scope": { "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5568,6 +6446,8 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5577,10 +6457,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/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/eslint/node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -5602,6 +6492,8 @@ }, "node_modules/eslint/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": { @@ -5613,6 +6505,8 @@ }, "node_modules/espree": { "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5629,6 +6523,8 @@ }, "node_modules/esquery": { "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -5639,6 +6535,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5650,6 +6548,8 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -5657,6 +6557,8 @@ }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -5665,13 +6567,17 @@ }, "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/eventsource": { - "version": "4.1.0", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.1.tgz", + "integrity": "sha512-D6bTRWh6KahHTK/m4WnjPQyEinNPf9eFLEZSEoj7d6fTibspnAVYfzHvirL7u/aoX5d9YYfIkBVAhmigUELk9w==", "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -5681,7 +6587,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -5689,6 +6597,8 @@ }, "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": { @@ -5711,6 +6621,8 @@ }, "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" @@ -5718,6 +6630,8 @@ }, "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": { @@ -5733,6 +6647,8 @@ }, "node_modules/express": { "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -5776,9 +6692,9 @@ } }, "node_modules/express-rate-limit": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.0.tgz", - "integrity": "sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==", + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz", + "integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -5796,6 +6712,8 @@ }, "node_modules/express/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" @@ -5803,6 +6721,8 @@ }, "node_modules/express/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/extend": { @@ -5813,21 +6733,29 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "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-levenshtein": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "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==", "dev": true, "license": "MIT" }, @@ -5839,6 +6767,8 @@ }, "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": { @@ -5847,6 +6777,8 @@ }, "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": { @@ -5855,6 +6787,8 @@ }, "node_modules/feaxios": { "version": "0.0.23", + "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.23.tgz", + "integrity": "sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==", "license": "MIT", "dependencies": { "is-retry-allowed": "^3.0.0" @@ -5891,6 +6825,8 @@ }, "node_modules/file-entry-cache": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", "dependencies": { @@ -5902,6 +6838,8 @@ }, "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": { @@ -5913,6 +6851,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", @@ -5929,6 +6869,8 @@ }, "node_modules/finalhandler/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" @@ -5936,10 +6878,14 @@ }, "node_modules/finalhandler/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/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -5955,6 +6901,8 @@ }, "node_modules/flat-cache": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", "dependencies": { @@ -5967,7 +6915,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -5979,6 +6929,8 @@ }, "node_modules/follow-redirects": { "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -6025,6 +6977,8 @@ }, "node_modules/form-data": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -6051,6 +7005,8 @@ }, "node_modules/formidable": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6065,6 +7021,8 @@ }, "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" @@ -6078,6 +7036,8 @@ }, "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" @@ -6085,11 +7045,17 @@ }, "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": [ @@ -6101,6 +7067,8 @@ }, "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" @@ -6195,9 +7163,9 @@ } }, "node_modules/gcp-metadata": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.3.tgz", - "integrity": "sha512-ziTrzUhhpL9Zk5k0HHzgP/KIpWDJT0VMBC/ynt/QIBvTW+UUcSivQRl6VlwTf/EilDxtSWklHoRsKy1c4k+59w==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", "license": "Apache-2.0", "dependencies": { "gaxios": "7.1.3", @@ -6210,6 +7178,8 @@ }, "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": { @@ -6218,6 +7188,8 @@ }, "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.*" @@ -6225,6 +7197,8 @@ }, "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", @@ -6247,6 +7221,8 @@ }, "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": { @@ -6255,6 +7231,8 @@ }, "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", @@ -6266,6 +7244,8 @@ }, "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": { @@ -6277,6 +7257,9 @@ }, "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": { @@ -6296,6 +7279,8 @@ }, "node_modules/glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -6307,6 +7292,8 @@ }, "node_modules/glob/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": { @@ -6318,6 +7305,8 @@ }, "node_modules/globals": { "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6341,6 +7330,8 @@ }, "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" @@ -6351,16 +7342,22 @@ }, "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/graphemer": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, "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": { @@ -6381,6 +7378,8 @@ }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -6389,6 +7388,8 @@ }, "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" @@ -6399,6 +7400,8 @@ }, "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" @@ -6412,6 +7415,8 @@ }, "node_modules/hasown": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -6434,11 +7439,15 @@ }, "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", @@ -6457,6 +7466,8 @@ }, "node_modules/https-proxy-agent": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "dependencies": { "agent-base": "6", @@ -6468,6 +7479,8 @@ }, "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": { @@ -6492,6 +7505,8 @@ }, "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" @@ -6502,6 +7517,8 @@ }, "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", @@ -6519,7 +7536,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.2", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -6528,11 +7547,15 @@ }, "node_modules/ignore-by-default": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true, "license": "ISC" }, "node_modules/import-fresh": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6547,9 +7570,9 @@ } }, "node_modules/import-in-the-middle": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.1.tgz", - "integrity": "sha512-0rymlHSFLwZ0ixx8DaQkoIyZojJPY2a0K2nEYslhKJ6jIYO/m0IcCb7iQsFPmS7WmKwISZiIrv5Icstrw/CmqA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.3.tgz", + "integrity": "sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==", "license": "Apache-2.0", "dependencies": { "cjs-module-lexer": "^2.2.0", @@ -6562,6 +7585,8 @@ }, "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": { @@ -6580,6 +7605,8 @@ }, "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": { @@ -6588,6 +7615,9 @@ }, "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": { @@ -6597,10 +7627,14 @@ }, "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/ioredis": { "version": "5.11.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", + "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", "license": "MIT", "dependencies": { "@ioredis/commands": "1.10.0", @@ -6630,6 +7664,8 @@ }, "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" @@ -6637,11 +7673,15 @@ }, "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": { @@ -6653,6 +7693,8 @@ }, "node_modules/is-core-module": { "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -6667,6 +7709,8 @@ }, "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": { @@ -6675,6 +7719,8 @@ }, "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" @@ -6682,6 +7728,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": { @@ -6690,6 +7738,8 @@ }, "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": { @@ -6701,6 +7751,8 @@ }, "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": { @@ -6709,6 +7761,8 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { @@ -6717,6 +7771,8 @@ }, "node_modules/is-retry-allowed": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz", + "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==", "license": "MIT", "engines": { "node": ">=12" @@ -6727,6 +7783,8 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -6737,10 +7795,14 @@ }, "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": { @@ -6749,6 +7811,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": { @@ -6764,6 +7828,8 @@ }, "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": { @@ -6777,6 +7843,8 @@ }, "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": { @@ -6790,6 +7858,8 @@ }, "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": { @@ -6817,6 +7887,8 @@ }, "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": { @@ -6842,6 +7914,8 @@ }, "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": { @@ -6855,6 +7929,8 @@ }, "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": { @@ -6885,6 +7961,8 @@ }, "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": { @@ -6917,6 +7995,8 @@ }, "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": { @@ -6961,6 +8041,8 @@ }, "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": { @@ -6975,6 +8057,8 @@ }, "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": { @@ -6986,6 +8070,8 @@ }, "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": { @@ -7001,6 +8087,8 @@ }, "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": { @@ -7017,6 +8105,8 @@ }, "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": { @@ -7025,6 +8115,8 @@ }, "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": { @@ -7049,6 +8141,8 @@ }, "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": { @@ -7061,6 +8155,8 @@ }, "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": { @@ -7075,6 +8171,8 @@ }, "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": { @@ -7094,6 +8192,8 @@ }, "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": { @@ -7107,6 +8207,8 @@ }, "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": { @@ -7123,6 +8225,8 @@ }, "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": { @@ -7131,6 +8235,8 @@ }, "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": { @@ -7150,6 +8256,8 @@ }, "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": { @@ -7162,6 +8270,8 @@ }, "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": { @@ -7193,6 +8303,8 @@ }, "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": { @@ -7225,11 +8337,15 @@ }, "node_modules/jest-runtime/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/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": { @@ -7260,6 +8376,8 @@ }, "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": { @@ -7276,6 +8394,8 @@ }, "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": { @@ -7292,6 +8412,8 @@ }, "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": { @@ -7303,6 +8425,8 @@ }, "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": { @@ -7321,6 +8445,8 @@ }, "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": { @@ -7335,6 +8461,8 @@ }, "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": { @@ -7349,13 +8477,15 @@ }, "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": "5.2.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.2.tgz", - "integrity": "sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", + "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", "funding": [ { "type": "github", @@ -7376,6 +8506,8 @@ }, "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": { @@ -7405,11 +8537,15 @@ }, "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "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" }, @@ -7428,16 +8564,22 @@ }, "node_modules/json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "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": { @@ -7492,6 +8634,8 @@ }, "node_modules/keyv": { "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -7500,6 +8644,8 @@ }, "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": { @@ -7514,6 +8660,8 @@ }, "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": { @@ -7522,6 +8670,8 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7534,11 +8684,15 @@ }, "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": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -7595,11 +8749,15 @@ }, "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/lodash.merge": { "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, @@ -7634,6 +8792,8 @@ }, "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": { @@ -7651,6 +8811,8 @@ }, "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": { @@ -7665,11 +8827,15 @@ }, "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==", "dev": 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": { @@ -7678,6 +8844,8 @@ }, "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" @@ -7685,6 +8853,8 @@ }, "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" @@ -7692,6 +8862,8 @@ }, "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" @@ -7699,6 +8871,8 @@ }, "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" }, @@ -7713,6 +8887,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" @@ -7720,6 +8896,8 @@ }, "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": { @@ -7732,6 +8910,8 @@ }, "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" @@ -7742,6 +8922,8 @@ }, "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" @@ -7749,6 +8931,8 @@ }, "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" @@ -7759,6 +8943,8 @@ }, "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": { @@ -7766,13 +8952,13 @@ } }, "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -7783,6 +8969,8 @@ }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", "funding": { @@ -7806,15 +8994,21 @@ }, "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/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" @@ -7822,13 +9016,15 @@ }, "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-abi": { - "version": "3.94.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz", - "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", + "version": "3.95.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.95.0.tgz", + "integrity": "sha512-T9iGctuocf0qIWFFOTxPzjT5q0SILqaBYXt272tlBHvTKC5+3JnkMirLxNJNkXHtFyBjU2Jx+NL4Zipr0B/c6Q==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -7886,11 +9082,15 @@ }, "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.51", + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", "dev": true, "license": "MIT", "engines": { @@ -7899,6 +9099,8 @@ }, "node_modules/nodemon": { "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", "dev": true, "license": "MIT", "dependencies": { @@ -7926,6 +9128,8 @@ }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { @@ -7934,6 +9138,8 @@ }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { @@ -7945,6 +9151,8 @@ }, "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": { @@ -7953,6 +9161,8 @@ }, "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": { @@ -7964,6 +9174,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" @@ -7971,6 +9183,8 @@ }, "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" @@ -7981,6 +9195,8 @@ }, "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" @@ -7991,6 +9207,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": { @@ -8008,6 +9226,8 @@ }, "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": { @@ -8022,6 +9242,8 @@ }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -8038,6 +9260,8 @@ }, "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": { @@ -8052,6 +9276,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -8066,6 +9292,8 @@ }, "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": { @@ -8080,6 +9308,8 @@ }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -8091,6 +9321,8 @@ }, "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": { @@ -8108,6 +9340,8 @@ }, "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" @@ -8115,6 +9349,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": { @@ -8123,6 +9359,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": { @@ -8131,6 +9369,8 @@ }, "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" @@ -8138,6 +9378,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" }, @@ -8165,6 +9407,8 @@ }, "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/pg-int8": { @@ -8177,9 +9421,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", - "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==", "license": "MIT" }, "node_modules/pg-types": { @@ -8200,11 +9444,15 @@ }, "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": { @@ -8216,6 +9464,8 @@ }, "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": { @@ -8224,6 +9474,8 @@ }, "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": { @@ -8235,6 +9487,8 @@ }, "node_modules/pkg-dir/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": { @@ -8247,6 +9501,8 @@ }, "node_modules/pkg-dir/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": { @@ -8258,6 +9514,8 @@ }, "node_modules/pkg-dir/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": { @@ -8272,6 +9530,8 @@ }, "node_modules/pkg-dir/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": { @@ -8322,6 +9582,8 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -8329,7 +9591,9 @@ } }, "node_modules/prettier": { - "version": "3.9.5", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -8344,6 +9608,8 @@ }, "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": { @@ -8357,6 +9623,8 @@ }, "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": { @@ -8368,6 +9636,8 @@ }, "node_modules/prisma": { "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -8388,6 +9658,7 @@ "version": "15.1.3", "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", + "deprecated": "prom-client has been replaced by @prometheus-io/client", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.4.0", @@ -8399,6 +9670,8 @@ }, "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": { @@ -8410,9 +9683,9 @@ } }, "node_modules/protobufjs": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", - "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -8434,6 +9707,8 @@ }, "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", @@ -8445,6 +9720,8 @@ }, "node_modules/proxy-from-env": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "license": "MIT", "engines": { "node": ">=10" @@ -8452,11 +9729,15 @@ }, "node_modules/pstree.remy": { "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true, "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -8465,6 +9746,8 @@ }, "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": [ { @@ -8480,6 +9763,8 @@ }, "node_modules/qs": { "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -8494,6 +9779,8 @@ }, "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": [ { @@ -8513,6 +9800,8 @@ }, "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" @@ -8520,6 +9809,8 @@ }, "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", @@ -8533,6 +9824,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" }, @@ -8552,6 +9845,8 @@ }, "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": { @@ -8563,6 +9858,8 @@ }, "node_modules/redis-errors": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", "license": "MIT", "engines": { "node": ">=4" @@ -8570,6 +9867,8 @@ }, "node_modules/redis-parser": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "license": "MIT", "dependencies": { "redis-errors": "^1.0.0" @@ -8580,6 +9879,8 @@ }, "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" @@ -8600,6 +9901,8 @@ }, "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": { @@ -8620,6 +9923,8 @@ }, "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": { @@ -8631,6 +9936,8 @@ }, "node_modules/resolve-cwd/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": { @@ -8639,6 +9946,8 @@ }, "node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -8647,6 +9956,8 @@ }, "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": { @@ -8655,6 +9966,8 @@ }, "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": { @@ -8664,6 +9977,9 @@ }, "node_modules/rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -8678,6 +9994,8 @@ }, "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": [ { @@ -8700,6 +10018,8 @@ }, "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", @@ -8727,6 +10047,8 @@ }, "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/scmp": { @@ -8744,6 +10066,8 @@ }, "node_modules/semver": { "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8754,6 +10078,8 @@ }, "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", @@ -8776,6 +10102,8 @@ }, "node_modules/send/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" @@ -8783,10 +10111,14 @@ }, "node_modules/send/node_modules/debug/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/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", @@ -8800,10 +10132,14 @@ }, "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/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" @@ -8814,6 +10150,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" @@ -8821,6 +10159,8 @@ }, "node_modules/side-channel": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8838,6 +10178,8 @@ }, "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", @@ -8852,6 +10194,8 @@ }, "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", @@ -8868,6 +10212,8 @@ }, "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", @@ -8885,11 +10231,15 @@ }, "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/simple-update-notifier": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "license": "MIT", "dependencies": { @@ -8901,11 +10251,15 @@ }, "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": { @@ -8913,7 +10267,9 @@ } }, "node_modules/smol-toml": { - "version": "1.7.0", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz", + "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -8924,6 +10280,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==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8931,6 +10289,8 @@ }, "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": { @@ -8949,6 +10309,8 @@ }, "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": { @@ -8960,6 +10322,8 @@ }, "node_modules/stack-utils/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": { @@ -8968,6 +10332,8 @@ }, "node_modules/standard-as-callback": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", "license": "MIT" }, "node_modules/standardwebhooks": { @@ -8982,6 +10348,8 @@ }, "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" @@ -8998,6 +10366,8 @@ }, "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": { @@ -9010,6 +10380,8 @@ }, "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", @@ -9037,6 +10409,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" @@ -9060,6 +10434,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": { @@ -9068,6 +10444,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": { @@ -9076,6 +10454,8 @@ }, "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": { @@ -9087,6 +10467,9 @@ }, "node_modules/superagent": { "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net", "dev": true, "license": "MIT", "dependencies": { @@ -9107,6 +10490,8 @@ }, "node_modules/superagent/node_modules/mime": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "license": "MIT", "bin": { @@ -9118,6 +10503,9 @@ }, "node_modules/supertest": { "version": "6.3.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", + "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", + "deprecated": "Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net", "dev": true, "license": "MIT", "dependencies": { @@ -9130,6 +10518,8 @@ }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -9141,6 +10531,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": { @@ -9151,9 +10543,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.32.9", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.9.tgz", - "integrity": "sha512-8i2tzJQi+7bgxESMD2hg/UBumbTsf6vLbtu4cW5ETPz/B070UuS0rTP1hu6WSH81HcsHqYalJE+rP21Vg96rUQ==", + "version": "5.32.14", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.14.tgz", + "integrity": "sha512-nOA2pSQhcmODMUQZpJHYKNuwniDUqcOWGNaSCOoZv12FdOSJ9JxV95HtyRGNMqEBj6h6lCNTy20TgZDYTSuUIg==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" @@ -9175,9 +10567,9 @@ } }, "node_modules/systeminformation": { - "version": "5.32.0", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.32.0.tgz", - "integrity": "sha512-7gfXs43T91miPxxTTtrYitotR/8MPsI2gy3XgUMs6kmOE/JCVqZp6nJpx4XkSutoSqDh6+Y2ovvb2A3RQCR+8w==", + "version": "5.33.4", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.4.tgz", + "integrity": "sha512-poXpX8M9NBll1NDMW1ho6qRFkjrlttfm8E/YtPmn5x0TQ1z8LoJUHg9kwr/sRDmCjktqfnFRxqsoUNpaRakeMQ==", "license": "MIT", "os": [ "darwin", @@ -9201,9 +10593,9 @@ } }, "node_modules/tdigest": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", - "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.3.tgz", + "integrity": "sha512-zbRt+lT+/H4fRItHshczHErVCQnitJk8MfMT24MqFJf3YL7SJJPqGIGeuOdvxXxM/AHFzKBl7WoyaYwqO9s3Kw==", "license": "MIT", "dependencies": { "bintrees": "1.0.2" @@ -9211,6 +10603,8 @@ }, "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": { @@ -9224,6 +10618,8 @@ }, "node_modules/test-exclude/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": { @@ -9241,6 +10637,8 @@ }, "node_modules/text-table": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, "license": "MIT" }, @@ -9280,9 +10678,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -9294,11 +10692,15 @@ }, "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-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": { @@ -9310,6 +10712,8 @@ }, "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" @@ -9317,6 +10721,8 @@ }, "node_modules/touch": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "dev": true, "license": "ISC", "bin": { @@ -9406,6 +10812,8 @@ }, "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": { @@ -9417,6 +10825,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -9459,13 +10869,15 @@ }, "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==", "dev": true, "license": "0BSD" }, "node_modules/twilio": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/twilio/-/twilio-6.0.2.tgz", - "integrity": "sha512-RN3TZxUtxLz2HBZVt62+LdZxQbrMVgYKtuzLgwmO7nqKvR+gQS5mCackD9hf4Y7MmoK/bX7tCm7kaJC8kC8zFA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-6.1.0.tgz", + "integrity": "sha512-a226jNHitbQQoLs4chB9TGzG2mOrxKR9NHs5XH8ZE4k48MQWdAwh+Hh6JsVB0hmCrsZpyHCsybf81Se6u6627A==", "license": "MIT", "dependencies": { "axios": "^1.13.5", @@ -9482,6 +10894,8 @@ }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -9493,6 +10907,8 @@ }, "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": { @@ -9501,6 +10917,8 @@ }, "node_modules/type-fest": { "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -9512,6 +10930,8 @@ }, "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", @@ -9523,6 +10943,8 @@ }, "node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -9535,6 +10957,8 @@ }, "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, @@ -9547,6 +10971,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" @@ -9557,22 +10983,30 @@ }, "node_modules/undefsafe": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true, "license": "MIT" }, "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==", "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", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", "dev": true, "funding": [ { @@ -9602,6 +11036,8 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9616,6 +11052,8 @@ }, "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" @@ -9623,11 +11061,15 @@ }, "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==", "dev": 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": { @@ -9641,6 +11083,8 @@ }, "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" @@ -9648,6 +11092,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": { @@ -9665,6 +11111,8 @@ }, "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" @@ -9714,6 +11162,8 @@ }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -9722,11 +11172,15 @@ }, "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", @@ -9760,11 +11214,15 @@ }, "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": { @@ -9816,6 +11274,8 @@ }, "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" @@ -9823,6 +11283,8 @@ }, "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" }, @@ -9843,6 +11305,8 @@ }, "node_modules/yargs": { "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -9859,6 +11323,8 @@ }, "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" @@ -9866,6 +11332,8 @@ }, "node_modules/yn": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "license": "MIT", "engines": { @@ -9874,6 +11342,8 @@ }, "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": { diff --git a/src/controllers/alert-ack-controller.ts b/src/controllers/alert-ack-controller.ts index 386e601..6d26c7c 100644 --- a/src/controllers/alert-ack-controller.ts +++ b/src/controllers/alert-ack-controller.ts @@ -1,48 +1,53 @@ -import { Request, Response } from 'express'; -import db from '../db'; -import { logger } from '../utils/logger'; -import { publishUserEvent } from '../events/publisher'; -import { verifyAckToken } from '../utils/ackToken'; - -export const ALERT_MAX_SNOOZE_DAYS = 30; - -export async function snoozeAlertRule(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; - const { durationMinutes, until, reason } = req.body; +import { Request, Response } from 'express' +import db from '../db' +import { logger } from '../utils/logger' +import { publishUserEvent } from '../events/publisher' +import { verifyAckToken } from '../utils/ackToken' + +export const ALERT_MAX_SNOOZE_DAYS = 30 + +export async function snoozeAlertRule( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params + const { durationMinutes, until, reason } = req.body if (!userId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } try { - const rule = await db.alertRule.findFirst({ where: { id, userId } }); + const rule = await db.alertRule.findFirst({ where: { id, userId } }) if (!rule) { - res.status(404).json({ error: 'Alert rule not found' }); - return; + res.status(404).json({ error: 'Alert rule not found' }) + return } - let snoozedUntilDate: Date; - const maxSnoozeMs = ALERT_MAX_SNOOZE_DAYS * 24 * 60 * 60 * 1000; - const maxAllowedDate = new Date(Date.now() + maxSnoozeMs); + let snoozedUntilDate: Date + const maxSnoozeMs = ALERT_MAX_SNOOZE_DAYS * 24 * 60 * 60 * 1000 + const maxAllowedDate = new Date(Date.now() + maxSnoozeMs) if (until) { - snoozedUntilDate = new Date(until); + snoozedUntilDate = new Date(until) } else if (durationMinutes && typeof durationMinutes === 'number') { - snoozedUntilDate = new Date(Date.now() + durationMinutes * 60 * 1000); + snoozedUntilDate = new Date(Date.now() + durationMinutes * 60 * 1000) } else { - res.status(400).json({ error: 'Must provide durationMinutes or until date' }); - return; + res + .status(400) + .json({ error: 'Must provide durationMinutes or until date' }) + return } if (isNaN(snoozedUntilDate.getTime())) { - res.status(400).json({ error: 'Invalid date provided for snooze' }); - return; + res.status(400).json({ error: 'Invalid date provided for snooze' }) + return } if (snoozedUntilDate > maxAllowedDate) { - snoozedUntilDate = maxAllowedDate; + snoozedUntilDate = maxAllowedDate } const updated = await db.alertRule.update({ @@ -51,55 +56,62 @@ export async function snoozeAlertRule(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; - const { fireId, note, ackToken, source = 'API' } = req.body; +export async function acknowledgeAlert( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params + const { fireId, note, ackToken, source = 'API' } = req.body - let targetUserId = userId; - let targetRuleId = id; - let targetFireId = fireId; + let targetUserId = userId + let targetRuleId = id + let targetFireId = fireId // Handle token-based ack (e.g. from WhatsApp/Telegram reply button) if (ackToken) { - const verifiedToken = verifyAckToken(ackToken); + const verifiedToken = verifyAckToken(ackToken) if (verifiedToken) { - targetUserId = verifiedToken.userId; - targetRuleId = verifiedToken.ruleId; - targetFireId = verifiedToken.fireId; + targetUserId = verifiedToken.userId + targetRuleId = verifiedToken.ruleId + targetFireId = verifiedToken.fireId } } if (!targetUserId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } try { - const rule = await db.alertRule.findFirst({ where: { id: targetRuleId, userId: targetUserId } }); + const rule = await db.alertRule.findFirst({ + where: { id: targetRuleId, userId: targetUserId }, + }) if (!rule) { - res.status(404).json({ error: 'Alert rule not found' }); - return; + res.status(404).json({ error: 'Alert rule not found' }) + return } // Create AlertAck record @@ -111,27 +123,32 @@ export async function acknowledgeAlert(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; - const limit = parseInt(req.query.limit as string || '20', 10); - const page = parseInt(req.query.page as string || '1', 10); - const skip = (page - 1) * limit; +export async function listAlertFires( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params + const limit = parseInt((req.query.limit as string) || '20', 10) + const page = parseInt((req.query.page as string) || '1', 10) + const skip = (page - 1) * limit if (!userId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } try { - const rule = await db.alertRule.findFirst({ where: { id, userId } }); + const rule = await db.alertRule.findFirst({ where: { id, userId } }) if (!rule) { - res.status(404).json({ error: 'Alert rule not found' }); - return; + res.status(404).json({ error: 'Alert rule not found' }) + return } const [fires, totalCount] = await Promise.all([ @@ -173,7 +195,7 @@ export async function listAlertFires(req: Request, res: Response): Promise take: limit, }), db.alertFire.count({ where: { ruleId: id } }), - ]); + ]) res.json({ fires, @@ -183,8 +205,8 @@ export async function listAlertFires(req: Request, res: Response): Promise totalCount, totalPages: Math.ceil(totalCount / limit), }, - }); + }) } catch (err: any) { - res.status(500).json({ error: 'Failed to list alert fires' }); + res.status(500).json({ error: 'Failed to list alert fires' }) } } diff --git a/src/controllers/email-identity-controller.ts b/src/controllers/email-identity-controller.ts index 85caf19..d0fb583 100644 --- a/src/controllers/email-identity-controller.ts +++ b/src/controllers/email-identity-controller.ts @@ -1,39 +1,54 @@ -import crypto from 'node:crypto'; -import { Request, Response } from 'express'; -import db from '../db'; -import { logger } from '../utils/logger'; -import { mailRegistry } from '../mail/mailProvider'; -import { renderEmailVerification } from '../mail/templates'; -import { publishUserEvent } from '../events/publisher'; - -export async function requestEmailVerification(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { email } = req.body; +import crypto from 'node:crypto' +import { Request, Response } from 'express' +import db from '../db' +import { logger } from '../utils/logger' +import { mailRegistry } from '../mail/mailProvider' +import { renderEmailVerification } from '../mail/templates' +import { publishUserEvent } from '../events/publisher' + +export async function requestEmailVerification( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { email } = req.body if (!userId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } if (!email || typeof email !== 'string' || !email.includes('@')) { - res.status(400).json({ error: 'Valid email address is required' }); - return; + res.status(400).json({ error: 'Valid email address is required' }) + return } - const normalizedEmail = email.trim().toLowerCase(); + const normalizedEmail = email.trim().toLowerCase() try { // Check if email is already verified by another user - const existing = await db.emailIdentity.findUnique({ where: { email: normalizedEmail } }); - if (existing && existing.userId !== userId && existing.status === 'VERIFIED') { + const existing = await db.emailIdentity.findUnique({ + where: { email: normalizedEmail }, + }) + if ( + existing && + existing.userId !== userId && + existing.status === 'VERIFIED' + ) { // Return generic success message to prevent account enumeration - res.json({ success: true, message: 'Verification email sent if address is valid' }); - return; + res.json({ + success: true, + message: 'Verification email sent if address is valid', + }) + return } - const rawToken = crypto.randomBytes(32).toString('hex'); - const verifyTokenHash = crypto.createHash('sha256').update(rawToken).digest('hex'); - const verifyExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24h TTL + const rawToken = crypto.randomBytes(32).toString('hex') + const verifyTokenHash = crypto + .createHash('sha256') + .update(rawToken) + .digest('hex') + const verifyExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000) // 24h TTL const identity = await db.emailIdentity.upsert({ where: { userId }, @@ -51,33 +66,39 @@ export async function requestEmailVerification(req: Request, res: Response): Pro verifyTokenHash, verifyExpiresAt, }, - }); + }) - const verifyUrl = `${process.env.APP_URL || 'https://neurowealth.app'}/api/v1/notifications/email/verify?token=${rawToken}`; - const emailMsg = renderEmailVerification(normalizedEmail, verifyUrl); - await mailRegistry.send(emailMsg); + const verifyUrl = `${process.env.APP_URL || 'https://neurowealth.app'}/api/v1/notifications/email/verify?token=${rawToken}` + const emailMsg = renderEmailVerification(normalizedEmail, verifyUrl) + await mailRegistry.send(emailMsg) res.json({ success: true, message: 'Verification email sent', email: identity.email, status: identity.status, - }); + }) } catch (err: any) { - logger.error('[EmailIdentityController] Failed to request email verification', { error: err.message }); - res.status(500).json({ error: 'Failed to process email verification request' }); + logger.error( + '[EmailIdentityController] Failed to request email verification', + { error: err.message } + ) + res + .status(500) + .json({ error: 'Failed to process email verification request' }) } } export async function verifyEmail(req: Request, res: Response): Promise { - const token = typeof req.query.token === 'string' ? req.query.token.trim() : null; + const token = + typeof req.query.token === 'string' ? req.query.token.trim() : null if (!token) { - res.status(400).json({ error: 'Verification token is required' }); - return; + res.status(400).json({ error: 'Verification token is required' }) + return } - const tokenHash = crypto.createHash('sha256').update(token).digest('hex'); + const tokenHash = crypto.createHash('sha256').update(token).digest('hex') try { const identity = await db.emailIdentity.findFirst({ @@ -85,16 +106,16 @@ export async function verifyEmail(req: Request, res: Response): Promise { verifyTokenHash: tokenHash, verifyExpiresAt: { gte: new Date() }, }, - }); + }) if (!identity) { - res.status(400).json({ error: 'Invalid or expired verification token' }); - return; + res.status(400).json({ error: 'Invalid or expired verification token' }) + return } if (identity.status === 'VERIFIED') { - res.json({ success: true, message: 'Email address is already verified' }); - return; + res.json({ success: true, message: 'Email address is already verified' }) + return } const updated = await db.emailIdentity.update({ @@ -105,40 +126,48 @@ export async function verifyEmail(req: Request, res: Response): Promise { verifyTokenHash: null, verifyExpiresAt: null, }, - }); + }) await publishUserEvent(identity.userId, 'alerts', 'email.verified' as any, { email: updated.email, verifiedAt: updated.verifiedAt?.toISOString(), - }); + }) res.json({ success: true, message: 'Email address verified successfully', email: updated.email, status: updated.status, - }); + }) } catch (err: any) { - logger.error('[EmailIdentityController] Failed to verify email token', { error: err.message }); - res.status(500).json({ error: 'Failed to verify email' }); + logger.error('[EmailIdentityController] Failed to verify email token', { + error: err.message, + }) + res.status(500).json({ error: 'Failed to verify email' }) } } -export async function handleMailWebhook(req: Request, res: Response): Promise { +export async function handleMailWebhook( + req: Request, + res: Response +): Promise { try { - const event = mailRegistry.parseWebhook(req.body, req.headers['x-signature'] as string); + const event = mailRegistry.parseWebhook( + req.body, + req.headers['x-signature'] as string + ) if (!event) { - res.status(400).json({ error: 'Invalid mail webhook payload' }); - return; + res.status(400).json({ error: 'Invalid mail webhook payload' }) + return } if (event.type === 'bounce' || event.type === 'complaint') { - const newStatus = event.type === 'bounce' ? 'BOUNCED' : 'COMPLAINED'; + const newStatus = event.type === 'bounce' ? 'BOUNCED' : 'COMPLAINED' const identity = await db.emailIdentity.findFirst({ where: { email: event.recipient.toLowerCase() }, - }); + }) if (identity) { await db.emailIdentity.update({ @@ -147,19 +176,26 @@ export async function handleMailWebhook(req: Request, res: Response): Promise { - const viewerUserId = (req as any).user?.id || (req as any).userId; +export async function issueStreamTicket( + req: Request, + res: Response +): Promise { + const viewerUserId = (req as any).user?.id || (req as any).userId if (!viewerUserId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } - const { actor } = req.query; - const streamUserId = typeof actor === 'string' && actor.trim() ? actor.trim() : viewerUserId; + const { actor } = req.query + const streamUserId = + typeof actor === 'string' && actor.trim() ? actor.trim() : viewerUserId - const ticket = createStreamTicket(viewerUserId, streamUserId); + const ticket = createStreamTicket(viewerUserId, streamUserId) res.json({ ticket, ttlSeconds: 60, expiresAt: new Date(Date.now() + 60000).toISOString(), - }); + }) } diff --git a/src/controllers/user-webhook-controller.ts b/src/controllers/user-webhook-controller.ts index 1867877..3de08b7 100644 --- a/src/controllers/user-webhook-controller.ts +++ b/src/controllers/user-webhook-controller.ts @@ -1,46 +1,49 @@ -import crypto from 'node:crypto'; -import { Request, Response } from 'express'; -import db from '../db'; -import { logger } from '../utils/logger'; +import crypto from 'node:crypto' +import { Request, Response } from 'express' +import db from '../db' +import { logger } from '../utils/logger' import { validateSsrfUrl, hashSecret, enqueueUserWebhooks, -} from '../services/userWebhookDispatcher'; -import { validateFilterPredicate } from '../utils/userWebhookFilter'; - -export async function createEndpoint(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; +} from '../services/userWebhookDispatcher' +import { validateFilterPredicate } from '../utils/userWebhookFilter' + +export async function createEndpoint( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId if (!userId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } - const { url, events = [], topicScope = [], filterJson = null } = req.body; + const { url, events = [], topicScope = [], filterJson = null } = req.body if (!url || typeof url !== 'string') { - res.status(400).json({ error: 'Valid URL is required' }); - return; + res.status(400).json({ error: 'Valid URL is required' }) + return } try { - validateSsrfUrl(url); + validateSsrfUrl(url) } catch (err: any) { - res.status(400).json({ error: err.message }); - return; + res.status(400).json({ error: err.message }) + return } if (filterJson) { try { - validateFilterPredicate(filterJson); + validateFilterPredicate(filterJson) } catch (err: any) { - res.status(400).json({ error: `Invalid filterJson: ${err.message}` }); - return; + res.status(400).json({ error: `Invalid filterJson: ${err.message}` }) + return } } - const rawSecret = `whsec_${crypto.randomBytes(24).toString('hex')}`; - const secretHash = hashSecret(rawSecret); + const rawSecret = `whsec_${crypto.randomBytes(24).toString('hex')}` + const secretHash = hashSecret(rawSecret) try { const endpoint = await db.userWebhookEndpoint.create({ @@ -53,7 +56,7 @@ export async function createEndpoint(req: Request, res: Response): Promise filterJson: filterJson || undefined, status: 'ACTIVE', }, - }); + }) res.status(201).json({ endpoint: { @@ -67,18 +70,23 @@ export async function createEndpoint(req: Request, res: Response): Promise createdAt: endpoint.createdAt, }, secret: rawSecret, // Returned ONLY ONCE on creation - }); + }) } catch (err: any) { - logger.error('[UserWebhookController] Failed to create endpoint', { error: err.message }); - res.status(500).json({ error: 'Failed to create webhook endpoint' }); + logger.error('[UserWebhookController] Failed to create endpoint', { + error: err.message, + }) + res.status(500).json({ error: 'Failed to create webhook endpoint' }) } } -export async function listEndpoints(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; +export async function listEndpoints( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId if (!userId) { - res.status(401).json({ error: 'Unauthorized' }); - return; + res.status(401).json({ error: 'Unauthorized' }) + return } try { @@ -98,17 +106,17 @@ export async function listEndpoints(req: Request, res: Response): Promise updatedAt: true, }, orderBy: { createdAt: 'desc' }, - }); + }) - res.json({ endpoints }); + res.json({ endpoints }) } catch (err: any) { - res.status(500).json({ error: 'Failed to list webhook endpoints' }); + res.status(500).json({ error: 'Failed to list webhook endpoints' }) } } export async function getEndpoint(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params try { const endpoint = await db.userWebhookEndpoint.findFirst({ @@ -126,37 +134,42 @@ export async function getEndpoint(req: Request, res: Response): Promise { createdAt: true, updatedAt: true, }, - }); + }) if (!endpoint) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } - res.json({ endpoint }); + res.json({ endpoint }) } catch (err: any) { - res.status(500).json({ error: 'Failed to fetch endpoint' }); + res.status(500).json({ error: 'Failed to fetch endpoint' }) } } -export async function updateEndpoint(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; - const { url, events, topicScope, filterJson, status } = req.body; +export async function updateEndpoint( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params + const { url, events, topicScope, filterJson, status } = req.body try { - const existing = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + const existing = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + }) if (!existing) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } if (url) { - validateSsrfUrl(url); + validateSsrfUrl(url) } if (filterJson) { - validateFilterPredicate(filterJson); + validateFilterPredicate(filterJson) } const updated = await db.userWebhookEndpoint.update({ @@ -178,97 +191,114 @@ export async function updateEndpoint(req: Request, res: Response): Promise status: true, updatedAt: true, }, - }); + }) - res.json({ endpoint: updated }); + res.json({ endpoint: updated }) } catch (err: any) { - res.status(400).json({ error: err.message || 'Failed to update endpoint' }); + res.status(400).json({ error: err.message || 'Failed to update endpoint' }) } } -export async function deleteEndpoint(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; +export async function deleteEndpoint( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params try { - const existing = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + const existing = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + }) if (!existing) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } - await db.userWebhookEndpoint.delete({ where: { id } }); - res.json({ success: true, message: 'Endpoint deleted successfully' }); + await db.userWebhookEndpoint.delete({ where: { id } }) + res.json({ success: true, message: 'Endpoint deleted successfully' }) } catch (err: any) { - res.status(500).json({ error: 'Failed to delete endpoint' }); + res.status(500).json({ error: 'Failed to delete endpoint' }) } } export async function rotateSecret(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params try { - const existing = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + const existing = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + }) if (!existing) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } - const newSecret = `whsec_${crypto.randomBytes(24).toString('hex')}`; - const secretHash = hashSecret(newSecret); + const newSecret = `whsec_${crypto.randomBytes(24).toString('hex')}` + const secretHash = hashSecret(newSecret) await db.userWebhookEndpoint.update({ where: { id }, data: { secretHash }, - }); + }) res.json({ success: true, endpointId: id, secret: newSecret, // Returned ONCE on rotation - }); + }) } catch (err: any) { - res.status(500).json({ error: 'Failed to rotate secret' }); + res.status(500).json({ error: 'Failed to rotate secret' }) } } export async function sendTestPing(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params try { - const endpoint = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + const endpoint = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + }) if (!endpoint) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } - const testSeq = Math.floor(Date.now() / 1000); + const testSeq = Math.floor(Date.now() / 1000) const testPayload = { event: 'webhook.test', timestamp: new Date().toISOString(), message: 'Test ping event from NeuroWealth user webhooks', - }; + } - await enqueueUserWebhooks(userId, 'alerts', 'webhook.test', testSeq, testPayload); + await enqueueUserWebhooks( + userId, + 'alerts', + 'webhook.test', + testSeq, + testPayload + ) - res.json({ success: true, message: 'Test webhook event enqueued' }); + res.json({ success: true, message: 'Test webhook event enqueued' }) } catch (err: any) { - res.status(500).json({ error: 'Failed to send test ping' }); + res.status(500).json({ error: 'Failed to send test ping' }) } } export async function replayEvents(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; - const afterSeq = parseInt(req.query.afterSeq as string || '0', 10); + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params + const afterSeq = parseInt((req.query.afterSeq as string) || '0', 10) try { - const endpoint = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + const endpoint = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + }) if (!endpoint) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } // Query durable UserEvent stream @@ -279,20 +309,20 @@ export async function replayEvents(req: Request, res: Response): Promise { }, orderBy: { seq: 'asc' }, take: 100, - }); + }) - let earliestAvailableSeq = afterSeq; + let earliestAvailableSeq = afterSeq if (events.length > 0) { - earliestAvailableSeq = Number(events[0].seq); + earliestAvailableSeq = Number(events[0].seq) } else { const earliest = await db.userEvent.findFirst({ where: { userId }, orderBy: { seq: 'asc' }, - }); - if (earliest) earliestAvailableSeq = Number(earliest.seq); + }) + if (earliest) earliestAvailableSeq = Number(earliest.seq) } - let enqueuedCount = 0; + let enqueuedCount = 0 for (const evt of events) { try { await db.userWebhookDelivery.create({ @@ -304,8 +334,8 @@ export async function replayEvents(req: Request, res: Response): Promise { attempts: 0, nextAttemptAt: new Date(), }, - }); - enqueuedCount++; + }) + enqueuedCount++ } catch (err: any) { // Skip duplicate } @@ -317,31 +347,36 @@ export async function replayEvents(req: Request, res: Response): Promise { replayedEventsCount: enqueuedCount, requestedAfterSeq: afterSeq, earliestAvailableSeq, - }); + }) } catch (err: any) { - res.status(500).json({ error: 'Failed to replay events' }); + res.status(500).json({ error: 'Failed to replay events' }) } } -export async function listDeliveries(req: Request, res: Response): Promise { - const userId = (req as any).user?.id || (req as any).userId; - const { id } = req.params; +export async function listDeliveries( + req: Request, + res: Response +): Promise { + const userId = (req as any).user?.id || (req as any).userId + const { id } = req.params try { - const endpoint = await db.userWebhookEndpoint.findFirst({ where: { id, userId } }); + const endpoint = await db.userWebhookEndpoint.findFirst({ + where: { id, userId }, + }) if (!endpoint) { - res.status(404).json({ error: 'Endpoint not found' }); - return; + res.status(404).json({ error: 'Endpoint not found' }) + return } const deliveries = await db.userWebhookDelivery.findMany({ where: { endpointId: id }, orderBy: { createdAt: 'desc' }, take: 50, - }); + }) - res.json({ deliveries }); + res.json({ deliveries }) } catch (err: any) { - res.status(500).json({ error: 'Failed to list deliveries' }); + res.status(500).json({ error: 'Failed to list deliveries' }) } } diff --git a/src/mail/mailProvider.ts b/src/mail/mailProvider.ts index aade566..4e27618 100644 --- a/src/mail/mailProvider.ts +++ b/src/mail/mailProvider.ts @@ -1,56 +1,58 @@ -import { logger } from '../utils/logger'; +import { logger } from '../utils/logger' export interface MailMessage { - to: string; - subject: string; - html: string; - text: string; - headers?: Record; + to: string + subject: string + html: string + text: string + headers?: Record } export interface MailSendResult { - messageId: string; - provider: string; + messageId: string + provider: string } export interface MailWebhookEvent { - type: 'bounce' | 'complaint' | 'delivery'; - messageId: string; - recipient: string; - reason?: string; + type: 'bounce' | 'complaint' | 'delivery' + messageId: string + recipient: string + reason?: string } export interface MailProvider { - name: string; - send(message: MailMessage): Promise; - parseWebhook(rawPayload: any, signature?: string): MailWebhookEvent | null; + name: string + send(message: MailMessage): Promise + parseWebhook(rawPayload: any, signature?: string): MailWebhookEvent | null } /** * Mock / In-Memory Mail Provider for local testing & development. */ export class MockMailProvider implements MailProvider { - name = 'mock'; - sentMessages: MailMessage[] = []; + name = 'mock' + sentMessages: MailMessage[] = [] async send(message: MailMessage): Promise { if (!message.text || !message.text.trim()) { - throw new Error('Email message must include a plaintext part'); + throw new Error('Email message must include a plaintext part') } - this.sentMessages.push(message); - const messageId = `msg_mock_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; - logger.info(`[MockMailProvider] Sent email to ${message.to}: ${message.subject}`); - return { messageId, provider: this.name }; + this.sentMessages.push(message) + const messageId = `msg_mock_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` + logger.info( + `[MockMailProvider] Sent email to ${message.to}: ${message.subject}` + ) + return { messageId, provider: this.name } } parseWebhook(rawPayload: any): MailWebhookEvent | null { - if (!rawPayload || !rawPayload.type) return null; + if (!rawPayload || !rawPayload.type) return null return { type: rawPayload.type, messageId: rawPayload.messageId || 'msg_mock_001', recipient: rawPayload.recipient || 'test@example.com', reason: rawPayload.reason, - }; + } } } @@ -58,25 +60,30 @@ export class MockMailProvider implements MailProvider { * SMTP Mail Provider using Nodemailer format. */ export class SmtpMailProvider implements MailProvider { - name = 'smtp'; + name = 'smtp' async send(message: MailMessage): Promise { if (!message.text || !message.text.trim()) { - throw new Error('Email message must include a plaintext part'); + throw new Error('Email message must include a plaintext part') } - const messageId = `msg_smtp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; - logger.info(`[SmtpMailProvider] Dispatched email to ${message.to}`); - return { messageId, provider: this.name }; + const messageId = `msg_smtp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` + logger.info(`[SmtpMailProvider] Dispatched email to ${message.to}`) + return { messageId, provider: this.name } } parseWebhook(rawPayload: any): MailWebhookEvent | null { - if (!rawPayload || !rawPayload.event) return null; + if (!rawPayload || !rawPayload.event) return null return { - type: rawPayload.event === 'bounce' ? 'bounce' : rawPayload.event === 'complaint' ? 'complaint' : 'delivery', + type: + rawPayload.event === 'bounce' + ? 'bounce' + : rawPayload.event === 'complaint' + ? 'complaint' + : 'delivery', messageId: rawPayload.messageId, recipient: rawPayload.email, reason: rawPayload.reason, - }; + } } } @@ -84,29 +91,36 @@ export class SmtpMailProvider implements MailProvider { * AWS SES Mail Provider. */ export class SesMailProvider implements MailProvider { - name = 'ses'; + name = 'ses' async send(message: MailMessage): Promise { if (!message.text || !message.text.trim()) { - throw new Error('Email message must include a plaintext part'); + throw new Error('Email message must include a plaintext part') } - const messageId = `msg_ses_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; - logger.info(`[SesMailProvider] Dispatched email via SES to ${message.to}`); - return { messageId, provider: this.name }; + const messageId = `msg_ses_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` + logger.info(`[SesMailProvider] Dispatched email via SES to ${message.to}`) + return { messageId, provider: this.name } } parseWebhook(rawPayload: any): MailWebhookEvent | null { - if (!rawPayload || !rawPayload.notificationType) return null; - const notificationType = (rawPayload.notificationType || '').toLowerCase(); - const type = notificationType === 'bounce' ? 'bounce' : notificationType === 'complaint' ? 'complaint' : 'delivery'; - const mail = rawPayload.mail || {}; - const recipient = mail.destination?.[0] || 'unknown@example.com'; + if (!rawPayload || !rawPayload.notificationType) return null + const notificationType = (rawPayload.notificationType || '').toLowerCase() + const type = + notificationType === 'bounce' + ? 'bounce' + : notificationType === 'complaint' + ? 'complaint' + : 'delivery' + const mail = rawPayload.mail || {} + const recipient = mail.destination?.[0] || 'unknown@example.com' return { type, messageId: mail.messageId || 'msg_ses_unknown', recipient, - reason: rawPayload.bounce?.bounceType || rawPayload.complaint?.complaintFeedbackType, - }; + reason: + rawPayload.bounce?.bounceType || + rawPayload.complaint?.complaintFeedbackType, + } } } @@ -114,33 +128,41 @@ export class SesMailProvider implements MailProvider { * Mail Provider Registry with Health Ledger & Fallback. */ export class MailRegistry { - private primaryProvider: MailProvider; - private fallbackProvider: MailProvider; - private isHealthy = true; + private primaryProvider: MailProvider + private fallbackProvider: MailProvider + private isHealthy = true constructor(primary?: MailProvider, fallback?: MailProvider) { - this.primaryProvider = primary || new MockMailProvider(); - this.fallbackProvider = fallback || new SmtpMailProvider(); + this.primaryProvider = primary || new MockMailProvider() + this.fallbackProvider = fallback || new SmtpMailProvider() } async send(message: MailMessage): Promise { if (this.isHealthy) { try { - return await this.primaryProvider.send(message); + return await this.primaryProvider.send(message) } catch (err: any) { - logger.warn(`[MailRegistry] Primary mail provider "${this.primaryProvider.name}" failed, failing over to fallback`, { error: err.message }); - this.isHealthy = false; + logger.warn( + `[MailRegistry] Primary mail provider "${this.primaryProvider.name}" failed, failing over to fallback`, + { error: err.message } + ) + this.isHealthy = false // Attempt recovery after 60s - setTimeout(() => { this.isHealthy = true; }, 60000); - return await this.fallbackProvider.send(message); + setTimeout(() => { + this.isHealthy = true + }, 60000) + return await this.fallbackProvider.send(message) } } - return await this.fallbackProvider.send(message); + return await this.fallbackProvider.send(message) } parseWebhook(rawPayload: any, signature?: string): MailWebhookEvent | null { - return this.primaryProvider.parseWebhook(rawPayload, signature) || this.fallbackProvider.parseWebhook(rawPayload, signature); + return ( + this.primaryProvider.parseWebhook(rawPayload, signature) || + this.fallbackProvider.parseWebhook(rawPayload, signature) + ) } } -export const mailRegistry = new MailRegistry(); +export const mailRegistry = new MailRegistry() diff --git a/src/mail/templates/index.ts b/src/mail/templates/index.ts index b92131f..5ff2eb8 100644 --- a/src/mail/templates/index.ts +++ b/src/mail/templates/index.ts @@ -1,7 +1,10 @@ -import { MailMessage } from '../mailProvider'; +import { MailMessage } from '../mailProvider' -export function renderEmailVerification(to: string, verifyUrl: string): MailMessage { - const subject = 'Verify your email address - NeuroWealth'; +export function renderEmailVerification( + to: string, + verifyUrl: string +): MailMessage { + const subject = 'Verify your email address - NeuroWealth' const html = `

Welcome to NeuroWealth

@@ -11,7 +14,7 @@ export function renderEmailVerification(to: string, verifyUrl: string): MailMess

NeuroWealth Notifications | Manage Preferences

- `; + ` const text = ` Welcome to NeuroWealth! @@ -21,23 +24,23 @@ ${verifyUrl} -- NeuroWealth Notifications Manage Preferences: https://neurowealth.app/preferences - `.trim(); + `.trim() - return { to, subject, html, text }; + return { to, subject, html, text } } export function renderAlertEmail( to: string, data: { - metric: string; - comparator: string; - threshold: number; - observedValue: number; - protocolName?: string | null; - ackToken?: string; + metric: string + comparator: string + threshold: number + observedValue: number + protocolName?: string | null + ackToken?: string } ): MailMessage { - const subject = `[ALERT] ${data.metric} triggered on NeuroWealth`; + const subject = `[ALERT] ${data.metric} triggered on NeuroWealth` const html = `

Alert Rule Triggered

@@ -48,7 +51,7 @@ export function renderAlertEmail(

View Alerts | Manage Preferences

- `; + ` const text = ` ALERT TRIGGERED @@ -59,7 +62,7 @@ Condition: ${data.comparator} ${data.threshold} -- View Alerts: https://neurowealth.app/alerts Manage Preferences: https://neurowealth.app/preferences - `.trim(); + `.trim() - return { to, subject, html, text }; + return { to, subject, html, text } } diff --git a/src/middleware/streamAuth.test.ts b/src/middleware/streamAuth.test.ts deleted file mode 100644 index 121d8fe..0000000 --- a/src/middleware/streamAuth.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { - createStreamTicket, - verifyStreamTicket, -} from './streamAuth'; - -describe('streamAuth (#369)', () => { - it('creates and verifies a valid stream ticket', () => { - const ticket = createStreamTicket('user_123', 'user_123', ['portfolio', 'agent']); - expect(typeof ticket).toBe('string'); - - const verified = verifyStreamTicket(ticket); - expect(verified).not.toBeNull(); - expect(verified?.viewerUserId).toBe('user_123'); - expect(verified?.streamUserId).toBe('user_123'); - expect(verified?.allowedTopics).toEqual(['portfolio', 'agent']); - }); - - it('enforces single-use stream ticket policy (rejects replay)', () => { - const ticket = createStreamTicket('user_456'); - - const firstUse = verifyStreamTicket(ticket); - expect(firstUse).not.toBeNull(); - - const secondUse = verifyStreamTicket(ticket); - expect(secondUse).toBeNull(); - }); - - it('rejects tampered stream tickets', () => { - const ticket = createStreamTicket('user_789'); - const tampered = `${ticket}extra`; - - expect(verifyStreamTicket(tampered)).toBeNull(); - expect(verifyStreamTicket('invalid.token.string')).toBeNull(); - }); -}); diff --git a/src/middleware/streamAuth.ts b/src/middleware/streamAuth.ts index 41249eb..2eff3a3 100644 --- a/src/middleware/streamAuth.ts +++ b/src/middleware/streamAuth.ts @@ -1,20 +1,21 @@ -import crypto from 'node:crypto'; -import type { IncomingMessage } from 'node:http'; -import { JwtAdapter } from '../config'; -import db from '../db'; -import { logger } from '../utils/logger'; -import { USER_EVENT_TOPICS, type UserEventTopic } from '../events/types'; -import { verifySessionToken } from '../ws/auth'; - -const STREAM_TICKET_SECRET = process.env.JWT_SECRET || 'neuro-stream-ticket-secret-key-32b'; -const usedTicketIds = new Set(); +import crypto from 'node:crypto' +import type { IncomingMessage } from 'node:http' +import { JwtAdapter } from '../config' +import db from '../db' +import { logger } from '../utils/logger' +import { USER_EVENT_TOPICS, type UserEventTopic } from '../events/types' +import { verifySessionToken } from '../ws/auth' + +const STREAM_TICKET_SECRET = + process.env.JWT_SECRET || 'neuro-stream-ticket-secret-key-32b' +const usedTicketIds = new Set() export interface StreamTicketPayload { - ticketId: string; - viewerUserId: string; - streamUserId: string; - allowedTopics: UserEventTopic[]; - exp: number; + ticketId: string + viewerUserId: string + streamUserId: string + allowedTopics: UserEventTopic[] + exp: number } /** @@ -25,8 +26,8 @@ export function createStreamTicket( streamUserId: string = viewerUserId, allowedTopics: UserEventTopic[] = [...USER_EVENT_TOPICS] ): string { - const ticketId = crypto.randomUUID(); - const exp = Math.floor(Date.now() / 1000) + 60; // 60s TTL + const ticketId = crypto.randomUUID() + const exp = Math.floor(Date.now() / 1000) + 60 // 60s TTL const payload: StreamTicketPayload = { ticketId, @@ -34,58 +35,62 @@ export function createStreamTicket( streamUserId, allowedTopics, exp, - }; + } - const payloadStr = JSON.stringify(payload); + const payloadStr = JSON.stringify(payload) const signature = crypto .createHmac('sha256', STREAM_TICKET_SECRET) .update(payloadStr) - .digest('base64url'); + .digest('base64url') - const token = `${Buffer.from(payloadStr).toString('base64url')}.${signature}`; - return token; + const token = `${Buffer.from(payloadStr).toString('base64url')}.${signature}` + return token } /** * Validates a single-use stream ticket. Returns null if expired, tampered with, or already used. */ -export function verifyStreamTicket(ticketToken: string): StreamTicketPayload | null { +export function verifyStreamTicket( + ticketToken: string +): StreamTicketPayload | null { try { - const parts = ticketToken.split('.'); - if (parts.length !== 2) return null; + const parts = ticketToken.split('.') + if (parts.length !== 2) return null - const [payloadB64, signature] = parts; - const payloadStr = Buffer.from(payloadB64, 'base64url').toString('utf8'); + const [payloadB64, signature] = parts + const payloadStr = Buffer.from(payloadB64, 'base64url').toString('utf8') const expectedSig = crypto .createHmac('sha256', STREAM_TICKET_SECRET) .update(payloadStr) - .digest('base64url'); + .digest('base64url') - if (signature !== expectedSig) return null; + if (signature !== expectedSig) return null - const payload: StreamTicketPayload = JSON.parse(payloadStr); + const payload: StreamTicketPayload = JSON.parse(payloadStr) if (payload.exp < Math.floor(Date.now() / 1000)) { - return null; + return null } // Single-use check if (usedTicketIds.has(payload.ticketId)) { - logger.warn('[StreamAuth] Stream ticket replay attempt detected', { ticketId: payload.ticketId }); - return null; + logger.warn('[StreamAuth] Stream ticket replay attempt detected', { + ticketId: payload.ticketId, + }) + return null } - usedTicketIds.add(payload.ticketId); + usedTicketIds.add(payload.ticketId) // Auto-clean ticket ID after TTL setTimeout(() => { - usedTicketIds.delete(payload.ticketId); - }, 65000); + usedTicketIds.delete(payload.ticketId) + }, 65000) - return payload; + return payload } catch (error) { - return null; + return null } } @@ -97,46 +102,50 @@ export async function authenticateStreamRequest( headers: Record, query: Record ): Promise<{ - ok: boolean; - viewerUserId?: string; - streamUserId?: string; - allowedTopics?: UserEventTopic[]; - error?: string; + ok: boolean + viewerUserId?: string + streamUserId?: string + allowedTopics?: UserEventTopic[] + error?: string }> { // 1. Check ticket param (for SSE EventSource) - const ticket = typeof query.ticket === 'string' ? query.ticket.trim() : null; + const ticket = typeof query.ticket === 'string' ? query.ticket.trim() : null if (ticket) { - const ticketPayload = verifyStreamTicket(ticket); + const ticketPayload = verifyStreamTicket(ticket) if (!ticketPayload) { - return { ok: false, error: 'Invalid, expired, or already-used stream ticket' }; + return { + ok: false, + error: 'Invalid, expired, or already-used stream ticket', + } } return { ok: true, viewerUserId: ticketPayload.viewerUserId, streamUserId: ticketPayload.streamUserId, allowedTopics: ticketPayload.allowedTopics, - }; + } } // 2. Check Authorization header - const authHeader = typeof headers.authorization === 'string' ? headers.authorization : null; - let bearerToken: string | null = null; + const authHeader = + typeof headers.authorization === 'string' ? headers.authorization : null + let bearerToken: string | null = null if (authHeader?.startsWith('Bearer ')) { - bearerToken = authHeader.slice(7).trim(); + bearerToken = authHeader.slice(7).trim() } if (bearerToken) { - const verified = await verifySessionToken(bearerToken); + const verified = await verifySessionToken(bearerToken) if (!verified.ok) { - return { ok: false, error: verified.message }; + return { ok: false, error: verified.message } } return { ok: true, viewerUserId: verified.userId, streamUserId: verified.userId, allowedTopics: [...USER_EVENT_TOPICS], - }; + } } - return { ok: false, error: 'Missing bearer token or stream ticket' }; + return { ok: false, error: 'Missing bearer token or stream ticket' } } diff --git a/src/routes/notifications.ts b/src/routes/notifications.ts index e8f18bd..e2a5cb5 100644 --- a/src/routes/notifications.ts +++ b/src/routes/notifications.ts @@ -1,14 +1,14 @@ -import { Router } from 'express'; -import { requireAuth } from '../middleware/authenticate'; +import { Router } from 'express' +import { requireAuth } from '../middleware/authenticate' import { requestEmailVerification, verifyEmail, -} from '../controllers/email-identity-controller'; +} from '../controllers/email-identity-controller' -const router = Router(); +const router = Router() // Email verification opt-in endpoints (#367) -router.post('/email', requireAuth, requestEmailVerification); -router.get('/email/verify', verifyEmail); +router.post('/email', requireAuth, requestEmailVerification) +router.get('/email/verify', verifyEmail) -export default router; +export default router diff --git a/src/routes/stream.ts b/src/routes/stream.ts index 7155324..6afccae 100644 --- a/src/routes/stream.ts +++ b/src/routes/stream.ts @@ -1,14 +1,14 @@ -import { Router } from 'express'; -import { requireAuth } from '../middleware/authenticate'; -import { issueStreamTicket } from '../controllers/stream-ticket-controller'; -import { handleSseConnection } from '../sse/handler'; +import { Router } from 'express' +import { requireAuth } from '../middleware/authenticate' +import { issueStreamTicket } from '../controllers/stream-ticket-controller' +import { handleSseConnection } from '../sse/handler' -const router = Router(); +const router = Router() // Stream ticket endpoint (authenticated via Bearer JWT) -router.post('/ticket', requireAuth, issueStreamTicket); +router.post('/ticket', requireAuth, issueStreamTicket) // SSE Fallback Transport Endpoint -router.get('/sse', handleSseConnection); +router.get('/sse', handleSseConnection) -export default router; +export default router diff --git a/src/services/userWebhookDispatcher.test.ts b/src/services/userWebhookDispatcher.test.ts deleted file mode 100644 index 593edc4..0000000 --- a/src/services/userWebhookDispatcher.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { - validateSsrfUrl, - signUserWebhookPayload, - hashSecret, -} from './userWebhookDispatcher'; - -describe('userWebhookDispatcher (#368)', () => { - it('validates SSRF protection for public https URLs', () => { - expect(() => validateSsrfUrl('https://api.myclient.com/webhook')).not.toThrow(); - expect(() => validateSsrfUrl('https://hooks.slack.com/services/xxx')).not.toThrow(); - }); - - it('rejects HTTP URLs, local IPs, and loopback hostnames for SSRF protection', () => { - expect(() => validateSsrfUrl('http://api.myclient.com/webhook')).toThrow(/must use the https:\/\/ protocol/); - expect(() => validateSsrfUrl('https://localhost/webhook')).toThrow(/private or loopback range/); - expect(() => validateSsrfUrl('https://127.0.0.1/webhook')).toThrow(/private or loopback range/); - expect(() => validateSsrfUrl('https://10.0.0.1/webhook')).toThrow(/private or loopback range/); - expect(() => validateSsrfUrl('https://192.168.1.50/webhook')).toThrow(/private or loopback range/); - expect(() => validateSsrfUrl('https://169.254.169.254/latest/meta-data')).toThrow(/private or loopback range/); - }); - - it('signs payload with timestamp and HMAC-SHA256', () => { - const payload = JSON.stringify({ event: 'deposit.received', amount: 100 }); - const secret = 'whsec_test_secret_key_12345'; - const timestamp = 1700000000; - - const signatureHeader = signUserWebhookPayload(payload, secret, timestamp); - expect(signatureHeader).toContain(`t=${timestamp},v1=`); - - const signatureHex = signatureHeader.split('v1=')[1]; - expect(signatureHex).toMatch(/^[a-f0-9]{64}$/); - }); - - it('hashes secrets deterministically using SHA-256', () => { - const secret = 'whsec_sample_secret'; - const hash1 = hashSecret(secret); - const hash2 = hashSecret(secret); - - expect(hash1).toBe(hash2); - expect(hash1).toMatch(/^[a-f0-9]{64}$/); - }); -}); diff --git a/src/services/userWebhookDispatcher.ts b/src/services/userWebhookDispatcher.ts index 916cc2e..78bc130 100644 --- a/src/services/userWebhookDispatcher.ts +++ b/src/services/userWebhookDispatcher.ts @@ -1,32 +1,32 @@ -import crypto from 'node:crypto'; -import URL from 'node:url'; -import db from '../db'; -import { logger } from '../utils/logger'; -import { evaluateFilterPredicate } from '../utils/userWebhookFilter'; +import crypto from 'node:crypto' +import URL from 'node:url' +import db from '../db' +import { logger } from '../utils/logger' +import { evaluateFilterPredicate } from '../utils/userWebhookFilter' -export const USER_WEBHOOK_MAX_ATTEMPTS = 5; -export const USER_WEBHOOK_MAX_CONSECUTIVE_FAILURES = 5; -export const USER_WEBHOOK_TIMEOUT_MS = 5000; +export const USER_WEBHOOK_MAX_ATTEMPTS = 5 +export const USER_WEBHOOK_MAX_CONSECUTIVE_FAILURES = 5 +export const USER_WEBHOOK_TIMEOUT_MS = 5000 /** * Validates a webhook URL against SSRF (Server-Side Request Forgery) attacks (#368). * Enforces HTTPS scheme and rejects private / loopback / link-local IP addresses and hosts. */ export function validateSsrfUrl(urlString: string): void { - let parsed: URL.UrlWithParsedQuery; + let parsed: URL.UrlWithParsedQuery try { - parsed = URL.parse(urlString, true); + parsed = URL.parse(urlString, true) } catch { - throw new Error('Invalid webhook URL format'); + throw new Error('Invalid webhook URL format') } if (parsed.protocol !== 'https:') { - throw new Error('Webhook URL must use the https:// protocol'); + throw new Error('Webhook URL must use the https:// protocol') } - const hostname = (parsed.hostname || '').toLowerCase().trim(); + const hostname = (parsed.hostname || '').toLowerCase().trim() if (!hostname) { - throw new Error('Webhook URL hostname is missing'); + throw new Error('Webhook URL hostname is missing') } // Reject local/private hostnames & IPs @@ -42,30 +42,36 @@ export function validateSsrfUrl(urlString: string): void { /^fe80:/i, /^fc00:/i, /^fd00:/i, - ]; + ] if (privateHostPatterns.some((pattern) => pattern.test(hostname))) { - throw new Error(`Forbidden webhook URL: hostname "${hostname}" resolves to a private or loopback range`); + throw new Error( + `Forbidden webhook URL: hostname "${hostname}" resolves to a private or loopback range` + ) } } /** * Signs a payload using HMAC-SHA256 with timestamp header format `t=timestamp,v1=signature`. */ -export function signUserWebhookPayload(payloadString: string, secret: string, timestamp: number = Math.floor(Date.now() / 1000)): string { - const signaturePayload = `${timestamp}.${payloadString}`; +export function signUserWebhookPayload( + payloadString: string, + secret: string, + timestamp: number = Math.floor(Date.now() / 1000) +): string { + const signaturePayload = `${timestamp}.${payloadString}` const signature = crypto .createHmac('sha256', secret) .update(signaturePayload) - .digest('hex'); - return `t=${timestamp},v1=${signature}`; + .digest('hex') + return `t=${timestamp},v1=${signature}` } /** * Hash a secret for secure storage in the database. */ export function hashSecret(secret: string): string { - return crypto.createHash('sha256').update(secret).digest('hex'); + return crypto.createHash('sha256').update(secret).digest('hex') } /** @@ -85,24 +91,30 @@ export async function enqueueUserWebhooks( userId, status: 'ACTIVE', }, - }); + }) - if (!endpoints || endpoints.length === 0) return; + if (!endpoints || endpoints.length === 0) return for (const endpoint of endpoints) { // Check event allowlist if (endpoint.events.length > 0 && !endpoint.events.includes(type)) { - continue; + continue } // Check topic scope - if (endpoint.topicScope.length > 0 && !endpoint.topicScope.includes(topic)) { - continue; + if ( + endpoint.topicScope.length > 0 && + !endpoint.topicScope.includes(topic) + ) { + continue } // Check server-side filter predicate - if (endpoint.filterJson && !evaluateFilterPredicate(endpoint.filterJson as any, redactedPayload)) { - continue; + if ( + endpoint.filterJson && + !evaluateFilterPredicate(endpoint.filterJson as any, redactedPayload) + ) { + continue } // Enqueue delivery record with idempotency key @@unique([endpointId, userEventSeq]) @@ -116,15 +128,18 @@ export async function enqueueUserWebhooks( attempts: 0, nextAttemptAt: new Date(), }, - }); + }) } catch (err: any) { // P2002 Unique constraint violation means already enqueued/delivered if (err?.code !== 'P2002') { - logger.warn('[UserWebhookDispatcher] Failed to create delivery record', { - endpointId: endpoint.id, - seq, - error: err.message, - }); + logger.warn( + '[UserWebhookDispatcher] Failed to create delivery record', + { + endpointId: endpoint.id, + seq, + error: err.message, + } + ) } } } @@ -133,6 +148,6 @@ export async function enqueueUserWebhooks( userId, type, error: error instanceof Error ? error.message : String(error), - }); + }) } } diff --git a/src/sse/handler.ts b/src/sse/handler.ts index b816276..13d6ca0 100644 --- a/src/sse/handler.ts +++ b/src/sse/handler.ts @@ -1,51 +1,75 @@ -import { Request, Response } from 'express'; -import db from '../db'; -import { logger } from '../utils/logger'; -import { authenticateStreamRequest } from '../middleware/streamAuth'; -import { subscribeToUserStream } from '../events/hub'; -import { USER_EVENT_TOPICS, type UserEventTopic, type UserEventEnvelope } from '../events/types'; - -export const SSE_HEARTBEAT_MS = 15000; -export const SSE_MAX_BUFFER_BYTES = 64 * 1024; // 64KB backpressure threshold - -export async function handleSseConnection(req: Request, res: Response): Promise { - const auth = await authenticateStreamRequest(req.headers as any, req.query as any); - if (!auth.ok || !auth.viewerUserId || !auth.streamUserId || !auth.allowedTopics) { - res.status(401).json({ error: auth.error || 'Unauthorized' }); - return; +import { Request, Response } from 'express' +import db from '../db' +import { logger } from '../utils/logger' +import { authenticateStreamRequest } from '../middleware/streamAuth' +import { subscribeToUserStream } from '../events/hub' +import { + USER_EVENT_TOPICS, + type UserEventTopic, + type UserEventEnvelope, +} from '../events/types' + +export const SSE_HEARTBEAT_MS = 15000 +export const SSE_MAX_BUFFER_BYTES = 64 * 1024 // 64KB backpressure threshold + +export async function handleSseConnection( + req: Request, + res: Response +): Promise { + const auth = await authenticateStreamRequest( + req.headers as any, + req.query as any + ) + if ( + !auth.ok || + !auth.viewerUserId || + !auth.streamUserId || + !auth.allowedTopics + ) { + res.status(401).json({ error: auth.error || 'Unauthorized' }) + return } // Parse requested topics - const requestedTopicsStr = typeof req.query.topics === 'string' ? req.query.topics : ''; + const requestedTopicsStr = + typeof req.query.topics === 'string' ? req.query.topics : '' const requestedTopics = requestedTopicsStr ? (requestedTopicsStr.split(',').map((t) => t.trim()) as UserEventTopic[]) - : auth.allowedTopics; + : auth.allowedTopics // Intersect with allowedTopics - const activeTopics = requestedTopics.filter((t) => auth.allowedTopics!.includes(t)); + const activeTopics = requestedTopics.filter((t) => + auth.allowedTopics!.includes(t) + ) if (activeTopics.length === 0) { - res.status(403).json({ error: 'No permitted topics selected' }); - return; + res.status(403).json({ error: 'No permitted topics selected' }) + return } // Configure SSE HTTP headers - res.setHeader('Content-Type', 'text/event-stream'); - res.setHeader('Cache-Control', 'no-cache, no-transform'); - res.setHeader('Connection', 'keep-alive'); - res.setHeader('X-Accel-Buffering', 'no'); - res.flushHeaders(); + res.setHeader('Content-Type', 'text/event-stream') + res.setHeader('Cache-Control', 'no-cache, no-transform') + res.setHeader('Connection', 'keep-alive') + res.setHeader('X-Accel-Buffering', 'no') + res.flushHeaders() - const viewerUserId = auth.viewerUserId; - const streamUserId = auth.streamUserId; + const viewerUserId = auth.viewerUserId + const streamUserId = auth.streamUserId // Check Last-Event-ID or afterSeq - const lastEventIdHeader = req.headers['last-event-id']; - const afterSeqParam = req.query.afterSeq; - let afterSeq = 0; - if (typeof lastEventIdHeader === 'string' && !isNaN(parseInt(lastEventIdHeader, 10))) { - afterSeq = parseInt(lastEventIdHeader, 10); - } else if (typeof afterSeqParam === 'string' && !isNaN(parseInt(afterSeqParam, 10))) { - afterSeq = parseInt(afterSeqParam, 10); + const lastEventIdHeader = req.headers['last-event-id'] + const afterSeqParam = req.query.afterSeq + let afterSeq = 0 + if ( + typeof lastEventIdHeader === 'string' && + !isNaN(parseInt(lastEventIdHeader, 10)) + ) { + afterSeq = parseInt(lastEventIdHeader, 10) + } else if ( + typeof afterSeqParam === 'string' && + !isNaN(parseInt(afterSeqParam, 10)) + ) { + afterSeq = parseInt(afterSeqParam, 10) } // Perform replay if requested @@ -59,64 +83,74 @@ export async function handleSseConnection(req: Request, res: Response): Promise< }, orderBy: { seq: 'asc' }, take: 500, - }); + }) if (retainedEvents.length === 0 && afterSeq > 0) { const earliest = await db.userEvent.findFirst({ where: { userId: streamUserId }, orderBy: { seq: 'asc' }, - }); + }) if (earliest && Number(earliest.seq) > afterSeq) { - res.write(`event: replay_truncated\ndata: ${JSON.stringify({ earliestAvailableSeq: Number(earliest.seq) })}\n\n`); + res.write( + `event: replay_truncated\ndata: ${JSON.stringify({ earliestAvailableSeq: Number(earliest.seq) })}\n\n` + ) } } for (const evt of retainedEvents) { - res.write(`id: ${evt.seq}\nevent: ${evt.type}\ndata: ${JSON.stringify(evt.payload)}\n\n`); + res.write( + `id: ${evt.seq}\nevent: ${evt.type}\ndata: ${JSON.stringify(evt.payload)}\n\n` + ) } } catch (err: any) { - logger.warn('[SSE] Failed to replay retained events', { error: err.message }); + logger.warn('[SSE] Failed to replay retained events', { + error: err.message, + }) } } // Periodic heartbeat const heartbeatInterval = setInterval(() => { - if (res.writableEnded) return; - res.write(': keep-alive\n\n'); - }, SSE_HEARTBEAT_MS); + if (res.writableEnded) return + res.write(': keep-alive\n\n') + }, SSE_HEARTBEAT_MS) // Live hub subscription const unsubscribe = subscribeToUserStream({ streamUserId, viewerUserId, deliver: (envelope: UserEventEnvelope) => { - if (res.writableEnded) return; + if (res.writableEnded) return // Filter by active topics - if (!activeTopics.includes(envelope.topic)) return; + if (!activeTopics.includes(envelope.topic)) return // Backpressure check if (res.writableLength > SSE_MAX_BUFFER_BYTES) { - res.write(`event: overflow\ndata: ${JSON.stringify({ message: 'Connection dropped due to slow consumer buffer overflow' })}\n\n`); - cleanup(); - res.end(); - return; + res.write( + `event: overflow\ndata: ${JSON.stringify({ message: 'Connection dropped due to slow consumer buffer overflow' })}\n\n` + ) + cleanup() + res.end() + return } - res.write(`id: ${envelope.seq}\nevent: ${envelope.type}\ndata: ${JSON.stringify(envelope.payload)}\n\n`); + res.write( + `id: ${envelope.seq}\nevent: ${envelope.type}\ndata: ${JSON.stringify(envelope.payload)}\n\n` + ) }, - }); + }) function cleanup() { - clearInterval(heartbeatInterval); + clearInterval(heartbeatInterval) try { - unsubscribe(); + unsubscribe() } catch { // ignore } } req.on('close', () => { - cleanup(); - }); + cleanup() + }) } diff --git a/src/utils/ackToken.test.ts b/src/utils/ackToken.test.ts deleted file mode 100644 index cf93e18..0000000 --- a/src/utils/ackToken.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { signAckToken, verifyAckToken } from './ackToken'; - -describe('ackToken (#366)', () => { - it('signs and verifies an ackToken correctly', () => { - const token = signAckToken('rule_123', 'fire_456', 'user_789'); - expect(typeof token).toBe('string'); - - const verified = verifyAckToken(token); - expect(verified).not.toBeNull(); - expect(verified?.ruleId).toBe('rule_123'); - expect(verified?.fireId).toBe('fire_456'); - expect(verified?.userId).toBe('user_789'); - }); - - it('returns payload for repeated verification (idempotent handling)', () => { - const token = signAckToken('rule_123', 'fire_456', 'user_789'); - - const first = verifyAckToken(token); - const second = verifyAckToken(token); - - expect(first).not.toBeNull(); - expect(second).not.toBeNull(); - expect(first?.fireId).toBe(second?.fireId); - }); - - it('rejects tampered ackTokens', () => { - const token = signAckToken('rule_123', 'fire_456', 'user_789'); - expect(verifyAckToken(`${token}tampered`)).toBeNull(); - }); -}); diff --git a/src/utils/ackToken.ts b/src/utils/ackToken.ts index d628854..16b8223 100644 --- a/src/utils/ackToken.ts +++ b/src/utils/ackToken.ts @@ -1,50 +1,56 @@ -import crypto from 'node:crypto'; +import crypto from 'node:crypto' -const ACK_TOKEN_SECRET = process.env.JWT_SECRET || 'neuro-ack-token-secret-key-32b'; -const usedAckTokens = new Set(); +const ACK_TOKEN_SECRET = + process.env.JWT_SECRET || 'neuro-ack-token-secret-key-32b' +const usedAckTokens = new Set() export interface AckTokenPayload { - ruleId: string; - fireId: string; - userId: string; - exp: number; + ruleId: string + fireId: string + userId: string + exp: number } -export function signAckToken(ruleId: string, fireId: string, userId: string, ttlSeconds = 86400): string { - const exp = Math.floor(Date.now() / 1000) + ttlSeconds; - const payload: AckTokenPayload = { ruleId, fireId, userId, exp }; - const payloadStr = JSON.stringify(payload); +export function signAckToken( + ruleId: string, + fireId: string, + userId: string, + ttlSeconds = 86400 +): string { + const exp = Math.floor(Date.now() / 1000) + ttlSeconds + const payload: AckTokenPayload = { ruleId, fireId, userId, exp } + const payloadStr = JSON.stringify(payload) const sig = crypto .createHmac('sha256', ACK_TOKEN_SECRET) .update(payloadStr) - .digest('base64url'); - return `${Buffer.from(payloadStr).toString('base64url')}.${sig}`; + .digest('base64url') + return `${Buffer.from(payloadStr).toString('base64url')}.${sig}` } export function verifyAckToken(token: string): AckTokenPayload | null { try { - const parts = token.split('.'); - if (parts.length !== 2) return null; - const [b64, sig] = parts; - const payloadStr = Buffer.from(b64, 'base64url').toString('utf8'); + const parts = token.split('.') + if (parts.length !== 2) return null + const [b64, sig] = parts + const payloadStr = Buffer.from(b64, 'base64url').toString('utf8') const expectedSig = crypto .createHmac('sha256', ACK_TOKEN_SECRET) .update(payloadStr) - .digest('base64url'); + .digest('base64url') - if (sig !== expectedSig) return null; + if (sig !== expectedSig) return null - const payload: AckTokenPayload = JSON.parse(payloadStr); - if (payload.exp < Math.floor(Date.now() / 1000)) return null; + const payload: AckTokenPayload = JSON.parse(payloadStr) + if (payload.exp < Math.floor(Date.now() / 1000)) return null if (usedAckTokens.has(token)) { // Idempotent: return payload so repeated call returns 200 without duplicate processing - return payload; + return payload } - usedAckTokens.add(token); - return payload; + usedAckTokens.add(token) + return payload } catch { - return null; + return null } } diff --git a/src/utils/userWebhookFilter.test.ts b/src/utils/userWebhookFilter.test.ts deleted file mode 100644 index c20797e..0000000 --- a/src/utils/userWebhookFilter.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { - validateFilterPredicate, - evaluateFilterPredicate, -} from './userWebhookFilter'; - -describe('userWebhookFilter (#368)', () => { - it('validates filter predicate grammar correctly', () => { - expect(validateFilterPredicate({ field: 'amount', op: 'gt', value: 100 })).toBe(true); - expect(validateFilterPredicate({ field: 'type', op: 'eq', value: 'WITHDRAWAL' })).toBe(true); - expect( - validateFilterPredicate({ - and: [ - { field: 'amount', op: 'gt', value: 100 }, - { field: 'type', op: 'eq', value: 'WITHDRAWAL' }, - ], - }) - ).toBe(true); - }); - - it('rejects invalid filter predicate grammar', () => { - expect(() => validateFilterPredicate(null)).toThrow(); - expect(() => validateFilterPredicate({ field: '', op: 'eq', value: 10 })).toThrow(); - expect(() => validateFilterPredicate({ field: 'amount', op: 'invalid_op', value: 10 })).toThrow(); - expect(() => validateFilterPredicate({ field: 'type', op: 'in', value: 'not_an_array' })).toThrow(); - }); - - it('evaluates eq, gt, lt, in operators accurately', () => { - const payload = { - type: 'WITHDRAWAL', - amount: 250, - status: 'CONFIRMED', - tags: ['urgent', 'crypto'], - }; - - expect(evaluateFilterPredicate({ field: 'type', op: 'eq', value: 'WITHDRAWAL' }, payload)).toBe(true); - expect(evaluateFilterPredicate({ field: 'type', op: 'eq', value: 'DEPOSIT' }, payload)).toBe(false); - - expect(evaluateFilterPredicate({ field: 'amount', op: 'gt', value: 200 }, payload)).toBe(true); - expect(evaluateFilterPredicate({ field: 'amount', op: 'gt', value: 300 }, payload)).toBe(false); - - expect(evaluateFilterPredicate({ field: 'amount', op: 'lt', value: 300 }, payload)).toBe(true); - expect(evaluateFilterPredicate({ field: 'amount', op: 'lt', value: 100 }, payload)).toBe(false); - - expect(evaluateFilterPredicate({ field: 'status', op: 'in', value: ['CONFIRMED', 'PENDING'] }, payload)).toBe(true); - expect(evaluateFilterPredicate({ field: 'status', op: 'in', value: ['FAILED'] }, payload)).toBe(false); - }); - - it('evaluates nested AND and OR predicates', () => { - const payload = { amount: 500, type: 'WITHDRAWAL' }; - - const andPredicate = { - and: [ - { field: 'amount', op: 'gt', value: 100 }, - { field: 'type', op: 'eq', value: 'WITHDRAWAL' }, - ], - }; - - const orPredicate = { - or: [ - { field: 'type', op: 'eq', value: 'DEPOSIT' }, - { field: 'amount', op: 'gt', value: 400 }, - ], - }; - - expect(evaluateFilterPredicate(andPredicate, payload)).toBe(true); - expect(evaluateFilterPredicate(orPredicate, payload)).toBe(true); - }); -}); diff --git a/src/utils/userWebhookFilter.ts b/src/utils/userWebhookFilter.ts index 94cbfad..04ab083 100644 --- a/src/utils/userWebhookFilter.ts +++ b/src/utils/userWebhookFilter.ts @@ -17,114 +17,131 @@ */ export interface FilterCondition { - field?: string; - op?: 'eq' | 'gt' | 'lt' | 'in'; - value?: any; - and?: FilterCondition[]; - or?: FilterCondition[]; + field?: string + op?: 'eq' | 'gt' | 'lt' | 'in' + value?: any + and?: FilterCondition[] + or?: FilterCondition[] } -const MAX_FILTER_DEPTH = 5; +const MAX_FILTER_DEPTH = 5 /** * Validates a filterJson predicate object. Throws if invalid or depth exceeds max allowed. */ export function validateFilterPredicate(filter: any, depth = 1): boolean { if (!filter || typeof filter !== 'object') { - throw new Error('Filter must be a valid JSON object'); + throw new Error('Filter must be a valid JSON object') } if (depth > MAX_FILTER_DEPTH) { - throw new Error(`Filter predicate depth exceeds maximum allowed (${MAX_FILTER_DEPTH})`); + throw new Error( + `Filter predicate depth exceeds maximum allowed (${MAX_FILTER_DEPTH})` + ) } if (Array.isArray(filter.and)) { if (filter.and.length === 0) { - throw new Error('Filter "and" array must not be empty'); + throw new Error('Filter "and" array must not be empty') } for (const sub of filter.and) { - validateFilterPredicate(sub, depth + 1); + validateFilterPredicate(sub, depth + 1) } - return true; + return true } if (Array.isArray(filter.or)) { if (filter.or.length === 0) { - throw new Error('Filter "or" array must not be empty'); + throw new Error('Filter "or" array must not be empty') } for (const sub of filter.or) { - validateFilterPredicate(sub, depth + 1); + validateFilterPredicate(sub, depth + 1) } - return true; + return true } if (typeof filter.field !== 'string' || !filter.field.trim()) { - throw new Error('Filter condition must contain a valid string "field"'); + throw new Error('Filter condition must contain a valid string "field"') } - const validOps = ['eq', 'gt', 'lt', 'in']; + const validOps = ['eq', 'gt', 'lt', 'in'] if (!filter.op || !validOps.includes(filter.op)) { - throw new Error(`Filter condition "op" must be one of: ${validOps.join(', ')}`); + throw new Error( + `Filter condition "op" must be one of: ${validOps.join(', ')}` + ) } if (filter.op === 'in' && !Array.isArray(filter.value)) { - throw new Error('Filter condition with "in" op must provide an array "value"'); + throw new Error( + 'Filter condition with "in" op must provide an array "value"' + ) } if (filter.value === undefined) { - throw new Error('Filter condition must specify a "value"'); + throw new Error('Filter condition must specify a "value"') } - return true; + return true } /** * Extracts a nested property from a payload using dot notation (e.g. "transaction.type"). */ function getNestedValue(obj: any, path: string): any { - if (!obj || typeof obj !== 'object') return undefined; - const parts = path.split('.'); - let curr = obj; + if (!obj || typeof obj !== 'object') return undefined + const parts = path.split('.') + let curr = obj for (const part of parts) { if (curr === null || curr === undefined || typeof curr !== 'object') { - return undefined; + return undefined } - curr = curr[part]; + curr = curr[part] } - return curr; + return curr } /** * Evaluates a validated filter predicate against an event payload. */ -export function evaluateFilterPredicate(filter: FilterCondition | null | undefined, payload: Record): boolean { +export function evaluateFilterPredicate( + filter: FilterCondition | null | undefined, + payload: Record +): boolean { if (!filter || Object.keys(filter).length === 0) { - return true; // No filter specified means match all events + return true // No filter specified means match all events } if (Array.isArray(filter.and)) { - return filter.and.every((cond) => evaluateFilterPredicate(cond, payload)); + return filter.and.every((cond) => evaluateFilterPredicate(cond, payload)) } if (Array.isArray(filter.or)) { - return filter.or.some((cond) => evaluateFilterPredicate(cond, payload)); + return filter.or.some((cond) => evaluateFilterPredicate(cond, payload)) } - if (!filter.field) return true; + if (!filter.field) return true - const actualValue = getNestedValue(payload, filter.field); - const expectedValue = filter.value; + const actualValue = getNestedValue(payload, filter.field) + const expectedValue = filter.value switch (filter.op) { case 'eq': - return actualValue === expectedValue; + return actualValue === expectedValue case 'gt': - return typeof actualValue === 'number' && typeof expectedValue === 'number' && actualValue > expectedValue; + return ( + typeof actualValue === 'number' && + typeof expectedValue === 'number' && + actualValue > expectedValue + ) case 'lt': - return typeof actualValue === 'number' && typeof expectedValue === 'number' && actualValue < expectedValue; + return ( + typeof actualValue === 'number' && + typeof expectedValue === 'number' && + actualValue < expectedValue + ) case 'in': - return Array.isArray(expectedValue) && expectedValue.includes(actualValue); + return Array.isArray(expectedValue) && expectedValue.includes(actualValue) default: - return true; + return true } } diff --git a/src/mail/mailProvider.test.ts b/tests/unit/mail/mailProvider.test.ts similarity index 53% rename from src/mail/mailProvider.test.ts rename to tests/unit/mail/mailProvider.test.ts index 4d82a43..2faf045 100644 --- a/src/mail/mailProvider.test.ts +++ b/tests/unit/mail/mailProvider.test.ts @@ -1,26 +1,28 @@ -import { describe, expect, it } from 'vitest'; -import { MockMailProvider, MailRegistry } from './mailProvider'; -import { renderEmailVerification, renderAlertEmail } from './templates'; +import { MockMailProvider } from '../../../src/mail/mailProvider' +import { + renderEmailVerification, + renderAlertEmail, +} from '../../../src/mail/templates' describe('mailProvider (#367)', () => { it('sends email using MockMailProvider when plaintext is provided', async () => { - const provider = new MockMailProvider(); + const provider = new MockMailProvider() const result = await provider.send({ to: 'user@example.com', subject: 'Test Subject', html: '

Hello

', text: 'Hello', - }); + }) - expect(result.provider).toBe('mock'); - expect(result.messageId).toMatch(/^msg_mock_/); - expect(provider.sentMessages.length).toBe(1); - expect(provider.sentMessages[0].to).toBe('user@example.com'); - }); + expect(result.provider).toBe('mock') + expect(result.messageId).toMatch(/^msg_mock_/) + expect(provider.sentMessages.length).toBe(1) + expect(provider.sentMessages[0].to).toBe('user@example.com') + }) it('rejects email messages without plaintext content', async () => { - const provider = new MockMailProvider(); + const provider = new MockMailProvider() await expect( provider.send({ @@ -29,14 +31,17 @@ describe('mailProvider (#367)', () => { html: '

Hello

', text: '', }) - ).rejects.toThrow(/must include a plaintext part/); - }); + ).rejects.toThrow(/must include a plaintext part/) + }) it('renders templates with required HTML and plaintext parts', () => { - const verifyMsg = renderEmailVerification('user@example.com', 'https://neurowealth.app/verify?token=123'); - expect(verifyMsg.to).toBe('user@example.com'); - expect(verifyMsg.html).toContain('https://neurowealth.app/verify?token=123'); - expect(verifyMsg.text).toContain('https://neurowealth.app/verify?token=123'); + const verifyMsg = renderEmailVerification( + 'user@example.com', + 'https://neurowealth.app/verify?token=123' + ) + expect(verifyMsg.to).toBe('user@example.com') + expect(verifyMsg.html).toContain('https://neurowealth.app/verify?token=123') + expect(verifyMsg.text).toContain('https://neurowealth.app/verify?token=123') const alertMsg = renderAlertEmail('user@example.com', { metric: 'PROTOCOL_APY', @@ -44,9 +49,9 @@ describe('mailProvider (#367)', () => { threshold: 5, observedValue: 3.2, protocolName: 'Blend', - }); - expect(alertMsg.subject).toContain('[ALERT]'); - expect(alertMsg.html).toContain('Blend'); - expect(alertMsg.text).toContain('Blend'); - }); -}); + }) + expect(alertMsg.subject).toContain('[ALERT]') + expect(alertMsg.html).toContain('Blend') + expect(alertMsg.text).toContain('Blend') + }) +}) diff --git a/tests/unit/middleware/streamAuth.test.ts b/tests/unit/middleware/streamAuth.test.ts new file mode 100644 index 0000000..c96ca79 --- /dev/null +++ b/tests/unit/middleware/streamAuth.test.ts @@ -0,0 +1,38 @@ +import { + createStreamTicket, + verifyStreamTicket, +} from '../../../src/middleware/streamAuth' + +describe('streamAuth (#369)', () => { + it('creates and verifies a valid stream ticket', () => { + const ticket = createStreamTicket('user_123', 'user_123', [ + 'portfolio', + 'agent', + ]) + expect(typeof ticket).toBe('string') + + const verified = verifyStreamTicket(ticket) + expect(verified).not.toBeNull() + expect(verified?.viewerUserId).toBe('user_123') + expect(verified?.streamUserId).toBe('user_123') + expect(verified?.allowedTopics).toEqual(['portfolio', 'agent']) + }) + + it('enforces single-use stream ticket policy (rejects replay)', () => { + const ticket = createStreamTicket('user_456') + + const firstUse = verifyStreamTicket(ticket) + expect(firstUse).not.toBeNull() + + const secondUse = verifyStreamTicket(ticket) + expect(secondUse).toBeNull() + }) + + it('rejects tampered stream tickets', () => { + const ticket = createStreamTicket('user_789') + const tampered = `${ticket}extra` + + expect(verifyStreamTicket(tampered)).toBeNull() + expect(verifyStreamTicket('invalid.token.string')).toBeNull() + }) +}) diff --git a/tests/unit/services/userWebhookDispatcher.test.ts b/tests/unit/services/userWebhookDispatcher.test.ts new file mode 100644 index 0000000..0a40451 --- /dev/null +++ b/tests/unit/services/userWebhookDispatcher.test.ts @@ -0,0 +1,58 @@ +import { + validateSsrfUrl, + signUserWebhookPayload, + hashSecret, +} from '../../../src/services/userWebhookDispatcher' + +describe('userWebhookDispatcher (#368)', () => { + it('validates SSRF protection for public https URLs', () => { + expect(() => + validateSsrfUrl('https://api.myclient.com/webhook') + ).not.toThrow() + expect(() => + validateSsrfUrl('https://hooks.slack.com/services/xxx') + ).not.toThrow() + }) + + it('rejects HTTP URLs, local IPs, and loopback hostnames for SSRF protection', () => { + expect(() => validateSsrfUrl('http://api.myclient.com/webhook')).toThrow( + /must use the https:\/\/ protocol/ + ) + expect(() => validateSsrfUrl('https://localhost/webhook')).toThrow( + /private or loopback range/ + ) + expect(() => validateSsrfUrl('https://127.0.0.1/webhook')).toThrow( + /private or loopback range/ + ) + expect(() => validateSsrfUrl('https://10.0.0.1/webhook')).toThrow( + /private or loopback range/ + ) + expect(() => validateSsrfUrl('https://192.168.1.50/webhook')).toThrow( + /private or loopback range/ + ) + expect(() => + validateSsrfUrl('https://169.254.169.254/latest/meta-data') + ).toThrow(/private or loopback range/) + }) + + it('signs payload with timestamp and HMAC-SHA256', () => { + const payload = JSON.stringify({ event: 'deposit.received', amount: 100 }) + const secret = 'whsec_test_secret_key_12345' + const timestamp = 1700000000 + + const signatureHeader = signUserWebhookPayload(payload, secret, timestamp) + expect(signatureHeader).toContain(`t=${timestamp},v1=`) + + const signatureHex = signatureHeader.split('v1=')[1] + expect(signatureHex).toMatch(/^[a-f0-9]{64}$/) + }) + + it('hashes secrets deterministically using SHA-256', () => { + const secret = 'whsec_sample_secret' + const hash1 = hashSecret(secret) + const hash2 = hashSecret(secret) + + expect(hash1).toBe(hash2) + expect(hash1).toMatch(/^[a-f0-9]{64}$/) + }) +}) diff --git a/tests/unit/utils/ackToken.test.ts b/tests/unit/utils/ackToken.test.ts new file mode 100644 index 0000000..0fa8253 --- /dev/null +++ b/tests/unit/utils/ackToken.test.ts @@ -0,0 +1,30 @@ +import { signAckToken, verifyAckToken } from '../../../src/utils/ackToken' + +describe('ackToken (#366)', () => { + it('signs and verifies an ackToken correctly', () => { + const token = signAckToken('rule_123', 'fire_456', 'user_789') + expect(typeof token).toBe('string') + + const verified = verifyAckToken(token) + expect(verified).not.toBeNull() + expect(verified?.ruleId).toBe('rule_123') + expect(verified?.fireId).toBe('fire_456') + expect(verified?.userId).toBe('user_789') + }) + + it('returns payload for repeated verification (idempotent handling)', () => { + const token = signAckToken('rule_123', 'fire_456', 'user_789') + + const first = verifyAckToken(token) + const second = verifyAckToken(token) + + expect(first).not.toBeNull() + expect(second).not.toBeNull() + expect(first?.fireId).toBe(second?.fireId) + }) + + it('rejects tampered ackTokens', () => { + const token = signAckToken('rule_123', 'fire_456', 'user_789') + expect(verifyAckToken(`${token}tampered`)).toBeNull() + }) +}) diff --git a/tests/unit/utils/userWebhookFilter.test.ts b/tests/unit/utils/userWebhookFilter.test.ts new file mode 100644 index 0000000..086015b --- /dev/null +++ b/tests/unit/utils/userWebhookFilter.test.ts @@ -0,0 +1,135 @@ +import { + validateFilterPredicate, + evaluateFilterPredicate, + FilterCondition, +} from '../../../src/utils/userWebhookFilter' + +describe('userWebhookFilter (#368)', () => { + it('validates filter predicate grammar correctly', () => { + expect( + validateFilterPredicate({ + field: 'amount', + op: 'gt' as const, + value: 100, + }) + ).toBe(true) + expect( + validateFilterPredicate({ + field: 'type', + op: 'eq' as const, + value: 'WITHDRAWAL', + }) + ).toBe(true) + expect( + validateFilterPredicate({ + and: [ + { field: 'amount', op: 'gt' as const, value: 100 }, + { field: 'type', op: 'eq' as const, value: 'WITHDRAWAL' }, + ], + }) + ).toBe(true) + }) + + it('rejects invalid filter predicate grammar', () => { + expect(() => validateFilterPredicate(null)).toThrow() + expect(() => + validateFilterPredicate({ field: '', op: 'eq' as const, value: 10 }) + ).toThrow() + expect(() => + validateFilterPredicate({ + field: 'amount', + op: 'invalid_op' as any, + value: 10, + }) + ).toThrow() + expect(() => + validateFilterPredicate({ + field: 'type', + op: 'in' as const, + value: 'not_an_array', + }) + ).toThrow() + }) + + it('evaluates eq, gt, lt, in operators accurately', () => { + const payload = { + type: 'WITHDRAWAL', + amount: 250, + status: 'CONFIRMED', + tags: ['urgent', 'crypto'], + } + + expect( + evaluateFilterPredicate( + { field: 'type', op: 'eq' as const, value: 'WITHDRAWAL' }, + payload + ) + ).toBe(true) + expect( + evaluateFilterPredicate( + { field: 'type', op: 'eq' as const, value: 'DEPOSIT' }, + payload + ) + ).toBe(false) + + expect( + evaluateFilterPredicate( + { field: 'amount', op: 'gt' as const, value: 200 }, + payload + ) + ).toBe(true) + expect( + evaluateFilterPredicate( + { field: 'amount', op: 'gt' as const, value: 300 }, + payload + ) + ).toBe(false) + + expect( + evaluateFilterPredicate( + { field: 'amount', op: 'lt' as const, value: 300 }, + payload + ) + ).toBe(true) + expect( + evaluateFilterPredicate( + { field: 'amount', op: 'lt' as const, value: 100 }, + payload + ) + ).toBe(false) + + expect( + evaluateFilterPredicate( + { field: 'status', op: 'in' as const, value: ['CONFIRMED', 'PENDING'] }, + payload + ) + ).toBe(true) + expect( + evaluateFilterPredicate( + { field: 'status', op: 'in' as const, value: ['FAILED'] }, + payload + ) + ).toBe(false) + }) + + it('evaluates nested AND and OR predicates', () => { + const payload = { amount: 500, type: 'WITHDRAWAL' } + + const andPredicate: FilterCondition = { + and: [ + { field: 'amount', op: 'gt' as const, value: 100 }, + { field: 'type', op: 'eq' as const, value: 'WITHDRAWAL' }, + ], + } + + const orPredicate: FilterCondition = { + or: [ + { field: 'type', op: 'eq' as const, value: 'DEPOSIT' }, + { field: 'amount', op: 'gt' as const, value: 400 }, + ], + } + + expect(evaluateFilterPredicate(andPredicate, payload)).toBe(true) + expect(evaluateFilterPredicate(orPredicate, payload)).toBe(true) + }) +}) From ab64485c06fccd146d85eb5c73600476c5e88dbc Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 15:05:35 +0100 Subject: [PATCH 4/7] fix(schema): resolve Prisma schema relation alignment for UserWebhookEndpoint and EmailIdentity (#368, #367) - Rebase branch onto latest upstream/main (including PR #378 schema updates) - Align relation fields on model User for userWebhookEndpoints and emailIdentity - Verify npx prisma validate and npx prisma generate succeed cleanly --- prisma/schema.prisma | 84 ++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b43ac85..bfdb316 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -229,6 +229,7 @@ model User { userApiKeys UserApiKey[] userWebhookEndpoints UserWebhookEndpoint[] emailIdentity EmailIdentity? + UserEventSequence UserEventSequence[] @@map("users") } @@ -257,16 +258,16 @@ model UserApiKey { } model IdempotencyRecord { - id String @id @default(uuid()) + id String @id @default(uuid()) userId String idempotencyKey String fingerprint String - status String // in_progress | completed + status String // in_progress | completed statusCode Int? responseBody Json? completedAt DateTime? expiresAt DateTime - createdAt DateTime @default(now()) + createdAt DateTime @default(now()) @@unique([userId, idempotencyKey]) @@index([expiresAt]) @@ -360,14 +361,14 @@ enum AuditBlockType { } model AuditBlock { - id String @id @default(uuid()) - height Int @unique + id String @id @default(uuid()) + height Int @unique prevHash String - hash String @unique + hash String @unique blockType AuditBlockType - payloadCount Int @default(0) + payloadCount Int @default(0) payloadHash String - createdAt DateTime @default(now()) + createdAt DateTime @default(now()) @@index([blockType]) @@index([createdAt]) @@ -375,11 +376,11 @@ model AuditBlock { } model AuditPayloadHash { - id String @id @default(uuid()) - tableName String - kind String + id String @id @default(uuid()) + tableName String + kind String payloadHash String - createdAt DateTime @default(now()) + createdAt DateTime @default(now()) @@index([tableName]) @@index([kind]) @@ -725,7 +726,7 @@ model WebhookSubscription { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) deliveries WebhookDelivery[] deadLetters WebhookDeadLetter[] @@ -761,7 +762,7 @@ model WebhookDeadLetter { firstFailedAt DateTime lastError String attempts Int - status String // PENDING | REPLAYED | RESOLVED | DISCARDED + status String // PENDING | REPLAYED | RESOLVED | DISCARDED createdAt DateTime @default(now()) subscription WebhookSubscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade) @@ -829,35 +830,35 @@ model SubAccount { /// provider's webhook alone. No plaintext bank/card details are ever stored /// here — the provider's hosted checkout captures all PII/payment details. model FiatOrder { - id String @id @default(uuid()) - userId String - provider String // e.g. "moonpay" — provider key, no provider-specific logic elsewhere - providerOrderId String - direction FiatDirection - fiatAmount Decimal @db.Decimal(36, 18) - fiatCurrency String - cryptoAmount Decimal? @db.Decimal(36, 18) - assetSymbol String - status FiatOrderStatus @default(PENDING) - transactionId String? // linked once the resulting on-chain Transaction is matched - checkoutUrl String? // provider hosted checkout URL (on-ramp) - kycUrl String? // provider KYC next-step link, surfaced to the user when required - failureReason String? // populated on FAILED/REFUNDED for operator/user visibility - settledAt DateTime? + id String @id @default(uuid()) + userId String + provider String // e.g. "moonpay" — provider key, no provider-specific logic elsewhere + providerOrderId String + direction FiatDirection + fiatAmount Decimal @db.Decimal(36, 18) + fiatCurrency String + cryptoAmount Decimal? @db.Decimal(36, 18) + assetSymbol String + status FiatOrderStatus @default(PENDING) + transactionId String? // linked once the resulting on-chain Transaction is matched + checkoutUrl String? // provider hosted checkout URL (on-ramp) + kycUrl String? // provider KYC next-step link, surfaced to the user when required + failureReason String? // populated on FAILED/REFUNDED for operator/user visibility + settledAt DateTime? // Best-execution / rate-drift protection (#313). quoteRate/quotedCryptoAmount // and fees are captured from the locked quote at order-creation time so // cryptoAmount is never an unexplained number; settledRate/settledCryptoAmount // are populated at on-chain settlement so the quoted-vs-settled delta is // always inspectable on the order itself. - quoteRate Decimal? @db.Decimal(36, 18) - quotedCryptoAmount Decimal? @db.Decimal(36, 18) + quoteRate Decimal? @db.Decimal(36, 18) + quotedCryptoAmount Decimal? @db.Decimal(36, 18) fees Json? // structured FeeBreakdown; null means the provider could not price it (unpriced, never assumed 0) providerQuoteId String? rateLockExpiresAt DateTime? - settledRate Decimal? @db.Decimal(36, 18) - settledCryptoAmount Decimal? @db.Decimal(36, 18) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + settledRate Decimal? @db.Decimal(36, 18) + settledCryptoAmount Decimal? @db.Decimal(36, 18) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) transaction Transaction? @relation(fields: [transactionId], references: [id]) @@ -1356,18 +1357,18 @@ model UserEventSequence { } model UserWebhookEndpoint { - id String @id @default(uuid()) + id String @id @default(uuid()) userId String url String secretHash String events String[] topicScope String[] filterJson Json? - status String @default("ACTIVE") // ACTIVE | PAUSED | DISABLED_BAD_ENDPOINT - consecutiveFailures Int @default(0) + status String @default("ACTIVE") // ACTIVE | PAUSED | DISABLED_BAD_ENDPOINT + consecutiveFailures Int @default(0) lastDeliveryAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) deliveries UserWebhookDelivery[] @@ -1418,7 +1419,7 @@ model AlertAck { ruleId String userId String fireId String? - source String // API | WHATSAPP | TELEGRAM | EMAIL + source String // API | WHATSAPP | TELEGRAM | EMAIL note String? createdAt DateTime @default(now()) @@ -1443,4 +1444,3 @@ model EmailIdentity { @@map("email_identities") } - From f575c76562271c137491e5b78d352b5c28be5826 Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 15:15:02 +0100 Subject: [PATCH 5/7] fix(schema): ensure Prisma relation fields match between User and new models (#368, #367) --- prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index bfdb316..e46346c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -226,10 +226,10 @@ model User { portfolioAttributions PortfolioAttribution[] portfolioRiskAggregates PortfolioRiskAggregate[] userEvents UserEvent[] + userEventSequence UserEventSequence? userApiKeys UserApiKey[] userWebhookEndpoints UserWebhookEndpoint[] emailIdentity EmailIdentity? - UserEventSequence UserEventSequence[] @@map("users") } From 1c60cf04b7c3b1fdc5bf94038aaabd74b6e84f64 Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 15:18:10 +0100 Subject: [PATCH 6/7] ci: trigger GitHub Actions workflow build From 3eba7809b74a57912baa50e35af7d4fcdbd3010e Mon Sep 17 00:00:00 2001 From: AbdulHameedAnofi Date: Thu, 27 Aug 2026 15:21:36 +0100 Subject: [PATCH 7/7] fix(test): mock dotenv.config in network-config test to prevent .env file override --- tests/unit/stellar/network-config.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit/stellar/network-config.test.ts b/tests/unit/stellar/network-config.test.ts index 925a98c..f6fcecd 100644 --- a/tests/unit/stellar/network-config.test.ts +++ b/tests/unit/stellar/network-config.test.ts @@ -73,8 +73,12 @@ describe('Stellar Network Configuration', () => { it('should derive testnet RPC URL when not explicitly set', () => { process.env.STELLAR_NETWORK = 'testnet' - // CI's job env sets STELLAR_RPC_URL — clear it so derivation is exercised delete process.env.STELLAR_RPC_URL + const dotenv = require('dotenv') + jest.spyOn(dotenv, 'config').mockImplementation(() => { + delete process.env.STELLAR_RPC_URL + return {} as any + }) process.env.STELLAR_AGENT_SECRET_KEY = 'S' + 'A'.repeat(55) process.env.VAULT_CONTRACT_ID = 'C' + 'B'.repeat(55) process.env.USDC_TOKEN_ADDRESS = 'C' + 'C'.repeat(55)