From dff6401a6d06fcffe75aac522041d1f5ac04d903 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:01 -0400 Subject: [PATCH 01/22] ENG-297 feat(bridge): add core Bridge integration --- dev/config/base-config.yaml | 12 + docs/bridge-integration/API.md | 199 + docs/bridge-integration/ARCHITECTURE.md | 67 + docs/bridge-integration/FLOWS.md | 133 + docs/bridge-integration/WEBHOOKS.md | 65 + package.json | 9 +- src/app/accounts/mark-account-for-deletion.ts | 15 +- src/app/errors.ts | 2 + src/app/offers/Validator.ts | 11 +- src/app/wallets/get-balance-for-wallet.ts | 19 +- src/config/schema.ts | 35 + src/config/schema.types.d.ts | 28 +- src/config/yaml.ts | 42 +- src/domain/accounts/index.types.d.ts | 21 +- src/domain/primitives/bridge.ts | 23 + src/domain/shared/MoneyAmount.ts | 78 +- src/domain/shared/primitives.ts | 1 + src/graphql/error-map.ts | 59 +- src/graphql/public/mutations.ts | 8 + src/graphql/public/queries.ts | 12 +- .../mutation/bridge-add-external-account.ts | 36 + .../mutation/bridge-create-virtual-account.ts | 36 + .../root/mutation/bridge-initiate-kyc.ts | 36 + .../mutation/bridge-initiate-withdrawal.ts | 53 + .../root/query/bridge-external-accounts.ts | 26 + .../public/root/query/bridge-kyc-status.ts | 25 + .../root/query/bridge-virtual-account.ts | 26 + .../public/root/query/bridge-withdrawals.ts | 26 + src/graphql/public/types/index.ts | 2 + .../types/object/bridge-external-account.ts | 13 + .../public/types/object/bridge-kyc-link.ts | 11 + .../types/object/bridge-virtual-account.ts | 13 + .../public/types/object/bridge-withdrawal.ts | 14 + src/graphql/shared/types/object/usd-wallet.ts | 10 +- .../shared/types/object/usdt-wallet.ts | 140 + .../shared/types/scalar/wallet-currency.ts | 1 + src/servers/bridge-webhook-server.ts | 8 + src/servers/graphql-admin-server.ts | 37 +- src/servers/graphql-server.ts | 2 +- src/servers/plugins/complexity.ts | 45 + src/services/bridge/client.ts | 367 ++ src/services/bridge/errors.ts | 89 + src/services/bridge/index.ts | 585 ++ src/services/bridge/index.types.d.ts | 121 + src/services/bridge/webhook-server/index.ts | 44 + .../middleware/verify-signature.ts | 67 + .../bridge/webhook-server/routes/deposit.ts | 49 + .../bridge/webhook-server/routes/kyc.ts | 80 + .../bridge/webhook-server/routes/transfer.ts | 88 + src/services/ibex/client.ts | 307 +- src/services/ibex/types.ts | 48 +- src/services/ibex/webhook-server/index.ts | 8 +- .../webhook-server/routes/crypto-receive.ts | 104 + .../ibex/webhook-server/routes/index.ts | 3 +- src/services/mongoose/accounts.ts | 51 + src/services/mongoose/bridge-accounts.ts | 138 + src/services/mongoose/schema.ts | 91 +- src/services/mongoose/schema.types.d.ts | 5 + yarn.lock | 5686 +++++++++-------- 59 files changed, 6527 insertions(+), 2803 deletions(-) create mode 100644 docs/bridge-integration/API.md create mode 100644 docs/bridge-integration/ARCHITECTURE.md create mode 100644 docs/bridge-integration/FLOWS.md create mode 100644 docs/bridge-integration/WEBHOOKS.md create mode 100644 src/domain/primitives/bridge.ts create mode 100644 src/graphql/public/root/mutation/bridge-add-external-account.ts create mode 100644 src/graphql/public/root/mutation/bridge-create-virtual-account.ts create mode 100644 src/graphql/public/root/mutation/bridge-initiate-kyc.ts create mode 100644 src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts create mode 100644 src/graphql/public/root/query/bridge-external-accounts.ts create mode 100644 src/graphql/public/root/query/bridge-kyc-status.ts create mode 100644 src/graphql/public/root/query/bridge-virtual-account.ts create mode 100644 src/graphql/public/root/query/bridge-withdrawals.ts create mode 100644 src/graphql/public/types/object/bridge-external-account.ts create mode 100644 src/graphql/public/types/object/bridge-kyc-link.ts create mode 100644 src/graphql/public/types/object/bridge-virtual-account.ts create mode 100644 src/graphql/public/types/object/bridge-withdrawal.ts create mode 100644 src/graphql/shared/types/object/usdt-wallet.ts create mode 100644 src/servers/bridge-webhook-server.ts create mode 100644 src/servers/plugins/complexity.ts create mode 100644 src/services/bridge/client.ts create mode 100644 src/services/bridge/errors.ts create mode 100644 src/services/bridge/index.ts create mode 100644 src/services/bridge/index.types.d.ts create mode 100644 src/services/bridge/webhook-server/index.ts create mode 100644 src/services/bridge/webhook-server/middleware/verify-signature.ts create mode 100644 src/services/bridge/webhook-server/routes/deposit.ts create mode 100644 src/services/bridge/webhook-server/routes/kyc.ts create mode 100644 src/services/bridge/webhook-server/routes/transfer.ts create mode 100644 src/services/ibex/webhook-server/routes/crypto-receive.ts create mode 100644 src/services/mongoose/bridge-accounts.ts diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index eb7eaf3ce..da545d17e 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -12,6 +12,18 @@ ibex: port: 4008 secret: "not-so-secret" +bridge: + enabled: false + apiKey: "" + baseUrl: "https://api.bridge.xyz" + webhook: + port: 4009 + publicKeys: + kyc: "" + deposit: "" + transfer: "" + timestampSkewMs: 300000 + # See Foreign Exchange Rates: https://www.firstglobal-bank.com/ exchangeRates: USD: diff --git a/docs/bridge-integration/API.md b/docs/bridge-integration/API.md new file mode 100644 index 000000000..2bd24e5fb --- /dev/null +++ b/docs/bridge-integration/API.md @@ -0,0 +1,199 @@ +# Bridge.xyz GraphQL API Reference + +All Bridge-related operations require the user to be authenticated and have an **Account Level 2** or higher. + +## Mutations + +### `bridgeInitiateKyc` + +Starts the KYC process for the authenticated user. + +**Request:** +```graphql +mutation BridgeInitiateKyc { + bridgeInitiateKyc { + errors { + message + } + kycLink { + kycLink + tosLink + } + } +} +``` + +**Response:** +- `kycLink`: URL to the Bridge/Persona KYC flow. +- `tosLink`: URL to the Bridge Terms of Service. + +--- + +### `bridgeCreateVirtualAccount` + +Creates a virtual bank account for the user to receive USD deposits. Requires approved KYC. + +**Request:** +```graphql +mutation BridgeCreateVirtualAccount { + bridgeCreateVirtualAccount { + errors { + message + } + virtualAccount { + bridgeVirtualAccountId + bankName + routingNumber + accountNumberLast4 + } + } +} +``` + +**Response:** +- `bridgeVirtualAccountId`: Unique identifier for the virtual account. +- `bankName`: Name of the bank (e.g., "Bridge Bank"). +- `routingNumber`: ABA routing number. +- `accountNumberLast4`: Last 4 digits of the account number. + +--- + +### `bridgeAddExternalAccount` + +Returns a hosted URL for the user to link their external bank account (via Plaid/Bridge). + +**Request:** +```graphql +mutation BridgeAddExternalAccount { + bridgeAddExternalAccount { + errors { + message + } + externalAccount { + linkUrl + expiresAt + } + } +} +``` + +**Response:** +- `linkUrl`: URL to the bank linking flow. +- `expiresAt`: Expiration timestamp for the link. + +--- + +### `bridgeInitiateWithdrawal` + +Initiates a withdrawal from the user's USDT balance to a linked external bank account. + +**Request:** +```graphql +mutation BridgeInitiateWithdrawal($input: BridgeInitiateWithdrawalInput!) { + bridgeInitiateWithdrawal(input: $input) { + errors { + message + } + withdrawal { + transferId + amount + currency + state + } + } +} +``` + +**Input:** +- `amount`: String representation of the amount (e.g., "100.00"). +- `externalAccountId`: The ID of the linked bank account. + +**Response:** +- `transferId`: Unique identifier for the transfer. +- `state`: Current state of the transfer (e.g., "pending", "processing"). + +--- + +## Queries + +### `bridgeKycStatus` + +Returns the current KYC status for the user. + +**Request:** +```graphql +query BridgeKycStatus { + bridgeKycStatus +} +``` + +**Possible Values:** +- `"pending"`: KYC is in progress. +- `"approved"`: KYC is complete and approved. +- `"rejected"`: KYC was rejected. +- `null`: KYC has not been initiated. + +--- + +### `bridgeVirtualAccount` + +Returns the user's virtual account details if one exists. + +**Request:** +```graphql +query BridgeVirtualAccount { + bridgeVirtualAccount { + bridgeVirtualAccountId + bankName + routingNumber + accountNumberLast4 + } +} +``` + +--- + +### `bridgeExternalAccounts` + +Lists all linked external bank accounts. + +**Request:** +```graphql +query BridgeExternalAccounts { + bridgeExternalAccounts { + bridgeExternalAccountId + bankName + accountNumberLast4 + status + } +} +``` + +--- + +### `bridgeWithdrawals` + +Lists the user's withdrawal history. + +**Request:** +```graphql +query BridgeWithdrawals { + bridgeWithdrawals { + transferId + amount + currency + state + createdAt + } +} +``` + +## Error Codes + +| Code | Description | +| --- | --- | +| `BRIDGE_DISABLED` | Bridge integration is disabled in configuration. | +| `BRIDGE_ACCOUNT_LEVEL_ERROR` | User account level is below 2. | +| `BRIDGE_KYC_PENDING` | Operation requires approved KYC, but it is still pending. | +| `BRIDGE_KYC_REJECTED` | KYC was rejected. | +| `BRIDGE_CUSTOMER_NOT_FOUND` | Bridge customer record not found for the user. | diff --git a/docs/bridge-integration/ARCHITECTURE.md b/docs/bridge-integration/ARCHITECTURE.md new file mode 100644 index 000000000..0ec665227 --- /dev/null +++ b/docs/bridge-integration/ARCHITECTURE.md @@ -0,0 +1,67 @@ +# Bridge.xyz Integration Architecture + +## System Overview + +The Bridge.xyz integration enables USD on-ramp and off-ramp functionality for Flash users. It allows users to convert between USD (via bank transfers) and USDT (on the Tron network), which is then integrated into the Flash ecosystem via IBEX. + +## Component Architecture + +The integration consists of three main components: + +1. **Flash Backend**: The core service that orchestrates the flow between users, Bridge.xyz, and IBEX. It exposes a GraphQL API for the mobile app and handles webhooks from Bridge.xyz. +2. **Bridge.xyz API**: An external service that provides virtual bank accounts, KYC processing, and USD/USDT conversion. +3. **IBEX**: An external service used by Flash to manage Bitcoin and Lightning wallets, and in this context, to provide Tron USDT receive addresses and handle USDT deposits. + +### Component Diagram + +```ascii ++-------------+ GraphQL +----------------+ +| Mobile App | <-----------------> | Flash Backend | ++-------------+ +----------------+ + ^ ^ + | | + | API | API + v v + +------------+ +------------+ + | Bridge.xyz | | IBEX | + +------------+ +------------+ + | | + | USD/USDT | USDT + v v + +----------------------------+ + | Tron Network | + +----------------------------+ +``` + +## Data Flow + +### On-Ramp (USD -> USDT) + +1. **KYC**: User initiates KYC via Flash, which creates a Bridge customer and returns a KYC link (Persona). +2. **Virtual Account**: Once KYC is approved, Flash creates a Tron USDT address via IBEX and a Bridge virtual account pointing to that address. +3. **Deposit**: User sends USD to the virtual account. +4. **Conversion**: Bridge converts USD to USDT and sends it to the Tron address. +5. **Credit**: IBEX detects the USDT deposit and notifies Flash via webhook, which credits the user's wallet. + +### Off-Ramp (USDT -> USD) + +1. **Link Bank**: User links an external bank account via Bridge's hosted UI. +2. **Withdrawal**: User initiates a withdrawal in Flash. +3. **Transfer**: Flash creates a Bridge transfer from the user's Tron address to the linked bank account. +4. **Conversion**: Bridge converts USDT to USD and sends it to the bank via ACH. + +## Technology Stack + +- **Language**: TypeScript +- **Runtime**: Node.js +- **API**: GraphQL (Apollo Server) +- **Database**: MongoDB (Mongoose) for storing Bridge account mappings and transfer states. +- **Communication**: REST API (Bridge.xyz), Webhooks. + +## Security Model + +- **Account Level**: Bridge functionality is restricted to users with Account Level 2 or higher. +- **KYC**: All users must pass Bridge's KYC process (powered by Persona). +- **Webhook Verification**: All incoming webhooks from Bridge.xyz are verified using asymmetric RSA-SHA256 signatures. +- **Idempotency**: All critical API calls to Bridge include an `Idempotency-Key` to prevent duplicate transactions. +- **Data Isolation**: Bridge customer IDs and account details are mapped to Flash internal account IDs. diff --git a/docs/bridge-integration/FLOWS.md b/docs/bridge-integration/FLOWS.md new file mode 100644 index 000000000..9b76cffe8 --- /dev/null +++ b/docs/bridge-integration/FLOWS.md @@ -0,0 +1,133 @@ +# Bridge.xyz Integration Flows + +This document describes the step-by-step flows for USD on-ramp and off-ramp using Bridge.xyz and IBEX. + +## On-Ramp Flow (USD -> USDT) + +This flow allows users to deposit USD from their bank account and receive USDT in their Flash wallet. + +### Sequence Diagram + +```ascii +User Flash App Flash Backend Bridge.xyz IBEX + | | | | | + | 1. Start KYC | | | | + |----------------->| 2. bridgeInitKyc | | | + | |------------------>| 3. Create Customer | | + | | |-------------------->| | + | | | 4. Create KYC Link | | + | | |-------------------->| | + | | 5. KYC Link | | | + | |<------------------| | | + | 6. Complete KYC | | | | + |----------------->| | | | + | (Persona Flow) | | | | + | | | 7. kyc.approved | | + | | |<--------------------| | + | | | 8. Create Tron Addr | | + | | |---------------------------------------->| + | | | 9. Create Virt Acc | | + | | |-------------------->| | + | 10. View Bank Det| | | | + |<-----------------| | | | + | 11. Transfer USD | | | | + |----------------------------------------------------------->| | + | | | | 12. Convert USD | + | | | | 13. Send USDT | + | | | |------------------>| + | | | | | + | | | 14. Crypto Webhook | | + | | |<----------------------------------------| + | | 15. Notify User | | | + |<-----------------| | | | +``` + +### Steps + +1. **Initiate KYC**: User clicks "Deposit USD" in the app. +2. **GraphQL Mutation**: App calls `bridgeInitiateKyc`. +3. **Bridge Customer**: Flash creates a Bridge customer if one doesn't exist. +4. **KYC Link**: Flash requests a KYC link from Bridge. +5. **Redirect**: App opens the KYC link (Persona). +6. **Verification**: User completes identity verification. +7. **KYC Webhook**: Bridge sends `kyc.approved` webhook to Flash. +8. **Tron Address**: Flash requests a unique Tron USDT receive address from IBEX. +9. **Virtual Account**: Flash creates a Bridge virtual account linked to the Tron address. +10. **Display Details**: User sees bank name, routing number, and account number in the app. +11. **Bank Transfer**: User initiates a transfer from their banking app. +12. **Conversion**: Bridge receives USD and converts it to USDT. +13. **Settlement**: Bridge sends USDT to the user's Tron address. +14. **IBEX Webhook**: IBEX detects the incoming USDT and notifies Flash. +15. **Credit**: Flash credits the user's USDT wallet and sends a push notification. + +--- + +## Off-Ramp Flow (USDT -> USD) + +This flow allows users to withdraw USDT from their Flash wallet to their external bank account. + +### Sequence Diagram + +```ascii +User Flash App Flash Backend Bridge.xyz Bank + | (Check for KYC, if complete, skip to 12. | | + | 1. Start KYC | | | | + |----------------->| 2. bridgeInitKyc | | | + | |------------------>| 3. Create Customer | | + | | |-------------------->| | + | | | 4. Create KYC Link | | + | | |-------------------->| | + | | 5. KYC Link | | | + | |<------------------| | | + | 6. Complete KYC | | | | + |----------------->| | | | + | (Persona Flow) | | | | + | | | 7. kyc.approved | | + | | |<--------------------| | + | 8. Link Bank | | | | + |----------------->| 9. bridgeAddExtAcc| | | + | |------------------>| 10. Get Link URL | | + | | |-------------------->| | + | | 11. Link URL | | | + | |<------------------| | | + | 12. Auth Bank | | | | + |----------------->| | | | + | (Plaid Flow) | | | | + | | | 13. ext_acc.verified| | + | | |<--------------------| | + | 14. Withdraw | | | | + |----------------->| 15. bridgeInitWith| | | + | |------------------>| 16. Create Transfer | | + | | |-------------------->| | + | | 17. Pending | | | + |<-----------------| | | | + | | | | 18. Convert USDT | + | | | | 19. Send ACH | + | | | |------------------>| + | | | 20. trans.completed | | + | | |<--------------------| | + | 21. Funds Arrive | | | | + |<-------------------------------------------------------------------------------| +``` + +### Steps + +1. **Link Bank**: User chooses to add a bank account. +2. **GraphQL Mutation**: App calls `bridgeAddExternalAccount`. +3. **Link URL**: Flash requests a hosted link URL from Bridge. +4. **Redirect**: App opens the Bridge/Plaid flow. +5. **Authentication**: User logs into their bank and selects an account. +6. **Verification Webhook**: Bridge notifies Flash when the external account is verified. +7. **Initiate Withdrawal**: User enters amount and selects the linked bank account. +8. **GraphQL Mutation**: App calls `bridgeInitiateWithdrawal`. +9. **Bridge Transfer**: Flash creates a transfer in Bridge from the user's Tron address to the external account. +10. **Confirmation**: App shows the withdrawal as "Pending". +11. **Conversion**: Bridge converts USDT from the user's balance to USD. +12. **ACH Transfer**: Bridge sends USD to the user's bank via ACH. +13. **Transfer Webhook**: Bridge sends `transfer.completed` webhook to Flash. +14. **Completion**: User receives funds in their bank account (usually 1-3 business days). + +## Fee Structure + +- **Bridge Fees**: Bridge.xyz charges fees for conversion and transfers (see Bridge.xyz documentation for current rates). +- **Flash Fee**: Flash charges a **0.5%** service fee on all Bridge transactions, which is included in the total amount shown to the user. diff --git a/docs/bridge-integration/WEBHOOKS.md b/docs/bridge-integration/WEBHOOKS.md new file mode 100644 index 000000000..bd129d808 --- /dev/null +++ b/docs/bridge-integration/WEBHOOKS.md @@ -0,0 +1,65 @@ +# Bridge.xyz Webhook Handling + +Flash receives real-time updates from Bridge.xyz via webhooks. These webhooks are used to update KYC status, confirm deposits, and track withdrawal progress. + +## Webhook Endpoint + +The webhook server listens on the configured port (default: `3005`) and expects POST requests at the following endpoints: + +- `POST /bridge/webhooks/kyc` +- `POST /bridge/webhooks/deposit` +- `POST /bridge/webhooks/transfer` + +## Signature Verification + +All incoming webhooks from Bridge.xyz are signed using asymmetric RSA-SHA256. Flash verifies these signatures using the public keys provided by Bridge.xyz. + +### Verification Process + +1. Retrieve the signature from the `Bridge-Signature` header. +2. Retrieve the timestamp from the `Bridge-Timestamp` header. +3. Verify that the timestamp is within the allowed skew (default: 5 minutes) to prevent replay attacks. +4. Construct the signed payload by concatenating the timestamp and the raw request body: `timestamp + "." + rawBody`. +5. Verify the signature against the signed payload using the appropriate public key (KYC, Deposit, or Transfer). + +## Event Types + +### KYC Events + +#### `kyc.approved` +Sent when a user's KYC application is approved. +- **Action**: Update user's `bridgeKycStatus` to `approved`. + +#### `kyc.rejected` +Sent when a user's KYC application is rejected. +- **Action**: Update user's `bridgeKycStatus` to `rejected`. + +### Deposit Events + +#### `deposit.completed` +Sent when a USD deposit to a virtual account is successfully converted to USDT and sent to the destination Tron address. +- **Action**: This event is primarily for tracking. The actual crediting of the user's wallet is handled by the IBEX webhook when the USDT arrives. + +### Transfer Events + +#### `transfer.completed` +Sent when an off-ramp transfer (USDT -> USD) is successfully completed. +- **Action**: Update the withdrawal record status to `completed` and notify the user. + +#### `transfer.failed` +Sent when an off-ramp transfer fails. +- **Action**: Update the withdrawal record status to `failed`, record the reason, and notify the user. + +## Idempotency Handling + +Each webhook event includes a unique `id`. Flash ensures that each event is processed exactly once by: + +1. Checking if the event ID has already been processed in the database. +2. Using a distributed lock (Redis) during processing to prevent race conditions from duplicate deliveries. + +## Response Codes + +- `200 OK`: Event successfully processed or already processed. +- `400 Bad Request`: Invalid payload or missing headers. +- `401 Unauthorized`: Signature verification failed. +- `500 Internal Server Error`: Temporary failure; Bridge.xyz will retry the webhook. diff --git a/package.json b/package.json index b48d43be7..e109959dc 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "watch-main": ". ./.env && nodemon -V -e ts,graphql -w ./src --exec yarn run start-main", "start-main": ". ./.env && yarn run build && node --inspect -r ./lib/services/tracing.js ./lib/servers/graphql-main-server.js", "migrate-mongo-up": "migrate-mongo up -f './src/migrations/migrate-mongo-config.js'", - "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts" + "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts", + "bridge-webhook": "ts-node src/servers/bridge-webhook-server.ts" }, "engines": { "node": ">=20.18.1 <21" @@ -38,6 +39,7 @@ "@google-cloud/storage": "^7.1.0", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", + "@lnflash/bridge-mcp": "github:lnflash/bridge-mcp", "@opentelemetry/api": "^1.6.0", "@opentelemetry/core": "^1.17.0", "@opentelemetry/exporter-trace-otlp-http": "^0.43.0", @@ -82,7 +84,6 @@ "graphql": "^16.8.0", "graphql-middleware": "^6.1.33", "graphql-query-complexity": "^0.12.0", - "graphql-query-complexity-apollo-plugin": "^1.0.2", "graphql-redis-subscriptions": "^2.6.0", "graphql-relay": "^0.10.0", "graphql-shield": "^7.6.4", @@ -177,11 +178,11 @@ "eslint-plugin-jest": "^27.4.0", "eslint-plugin-prettier": "^5.0.0", "graphql-subscriptions": "^2.0.0", - "grpc-tools": "^1.12.4", "grpc_tools_node_protoc_ts": "^5.3.3", + "grpc-tools": "^1.12.4", "jest": "^29.7.0", - "jest-junit": "^16.0.0", "jest_workaround": "^0.79.19", + "jest-junit": "^16.0.0", "lodash.difference": "^4.5.0", "lodash.find": "^4.6.0", "madge": "^6.1.0", diff --git a/src/app/accounts/mark-account-for-deletion.ts b/src/app/accounts/mark-account-for-deletion.ts index d8207eeeb..6e3619988 100644 --- a/src/app/accounts/mark-account-for-deletion.ts +++ b/src/app/accounts/mark-account-for-deletion.ts @@ -25,17 +25,26 @@ export const markAccountForDeletion = async ({ if (wallets instanceof Error) return wallets for (const wallet of wallets) { - const balance = await getBalanceForWallet({ walletId: wallet.id }) + const balance = await getBalanceForWallet({ + walletId: wallet.id, + currency: wallet.currency, + }) if (balance instanceof Error) return balance - if (balance.isGreaterThan(USDAmount.ZERO) && cancelIfPositiveBalance) { + if ( + balance instanceof USDAmount && + balance.isGreaterThan(USDAmount.ZERO) && + cancelIfPositiveBalance + ) { return new AccountHasPositiveBalanceError( `The new phone is associated with an account with a non empty wallet. walletId: ${wallet.id}, balance: ${balance}, accountId: ${account.id}, cancelIfPositiveBalance: ${cancelIfPositiveBalance}`, ) } + const balanceDisplay = + balance instanceof USDAmount ? balance.asDollars() : balance.asNumber() addEventToCurrentSpan(`deleting_wallet`, { walletId: wallet.id, currency: wallet.currency, - balance: balance.asDollars(), + balance: balanceDisplay, }) } diff --git a/src/app/errors.ts b/src/app/errors.ts index 89f68e8ae..6e747daf6 100644 --- a/src/app/errors.ts +++ b/src/app/errors.ts @@ -27,6 +27,7 @@ import * as SvixErrors from "@services/svix/errors" import * as IbexErrors from "@services/ibex/errors" import * as ErpNextErrors from "@services/frappe/errors" +import * as BridgeErrors from "@services/bridge/errors" export const ApplicationErrors = { ...SharedErrors, @@ -59,4 +60,5 @@ export const ApplicationErrors = { // Flash Errors ...IbexErrors, ...ErpNextErrors, + ...BridgeErrors, } as const diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index 316e7531b..680e6538a 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -1,7 +1,7 @@ import { getBalanceForWallet } from "@app/wallets"; import { Cashout } from "@config"; import { AccountValidator, hasErpParty, isActiveAccount, walletBelongsToAccount } from "@domain/accounts"; -import { JMDAmount, USDAmount, ValidationError, ValidationFn, validator } from "@domain/shared"; +import { JMDAmount, USDAmount, USDTAmount, ValidationError, ValidationFn, validator } from "@domain/shared"; import { ValidationInputs } from "./types"; import ErpNext from "@services/frappe/ErpNext"; @@ -36,9 +36,14 @@ const isUsd = async (o: ValidationInputs) => { } const hasSufficientBalance = async (o: ValidationInputs): Promise => { - const balance = await getBalanceForWallet({ walletId: o.wallet.id }) + const balance = await getBalanceForWallet({ + walletId: o.wallet.id, + currency: o.wallet.currency, + }) if (balance instanceof Error) return new ValidationError(balance) + if (balance instanceof USDTAmount) + return new ValidationError("Cash out only supports withdrawals from USD wallets") else if (o.payment.amount.isGreaterThan(balance)) return new ValidationError("Transfer amount is greater than wallet balance.") else return true @@ -74,4 +79,4 @@ export const CashoutValidator = validator([ hasErpParty, verifyBankAccount, // TODO daily/weekly/monthly volume limits -]) \ No newline at end of file +]) diff --git a/src/app/wallets/get-balance-for-wallet.ts b/src/app/wallets/get-balance-for-wallet.ts index 7a7ce6128..30aaff7a4 100644 --- a/src/app/wallets/get-balance-for-wallet.ts +++ b/src/app/wallets/get-balance-for-wallet.ts @@ -1,15 +1,26 @@ import { UnknownLedgerError } from "@domain/ledger" -import { USDAmount } from "@domain/shared" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" import Ibex from "@services/ibex/client" import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" export const getBalanceForWallet = async ({ walletId, + currency, }: { walletId: WalletId -}): Promise => { - // return LedgerService().getWalletBalance(walletId) - try { + currency?: WalletCurrency +}): Promise => { + try { + if (currency === WalletCurrency.Usdt) { + const resp = await Ibex.getCryptoReceiveBalance(walletId) + if (resp instanceof IbexError) { + if (resp.httpCode === 404) return USDTAmount.ZERO + return resp + } + if (resp === undefined) return new UnexpectedIbexResponse("Balance not found") + return resp + } + const resp = await Ibex.getAccountDetails(walletId) if (resp instanceof IbexError) { if (resp.httpCode === 404) return USDAmount.ZERO diff --git a/src/config/schema.ts b/src/config/schema.ts index 4f75d2c97..18010350d 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -642,6 +642,32 @@ export const configSchema = { }, }, }, + bridge: { + type: "object", + properties: { + enabled: { type: "boolean" }, + apiKey: { type: "string" }, + baseUrl: { type: "string" }, + webhook: { + type: "object", + properties: { + port: { type: "integer" }, + publicKeys: { + type: "object", + properties: { + kyc: { type: "string" }, + deposit: { type: "string" }, + transfer: { type: "string" }, + }, + required: ["kyc", "deposit", "transfer"], + }, + timestampSkewMs: { type: "integer" }, + }, + required: ["port", "publicKeys", "timestampSkewMs"], + }, + }, + required: ["enabled", "apiKey", "baseUrl", "webhook"], + }, exchangeRates: { type: "object", }, @@ -659,6 +685,14 @@ export const configSchema = { properties: { url: { type: "string" }, credentials: { type: "object" }, + erpnext: { + type: "object", + properties: { + accounts: { + type: "object", + }, + }, + }, }, required: ["url", "credentials"], }, @@ -711,6 +745,7 @@ export const configSchema = { "exchangeRates", "cashout", "ibex", + "bridge", ], additionalProperties: false, } as const diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index e49908962..b12767c01 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -29,6 +29,25 @@ type IbexConfig = { webhook: WebhookServer } +type BridgeWebhookPublicKeys = { + kyc: string + deposit: string + transfer: string +} + +type BridgeWebhook = { + port: number + publicKeys: BridgeWebhookPublicKeys + timestampSkewMs: number +} + +type BridgeConfig = { + enabled: boolean + apiKey: string + baseUrl: string + webhook: BridgeWebhook +} + type CashoutEmail = { to: string from: string @@ -171,7 +190,8 @@ type YamlSchema = { skipFeeProbeConfig: { pubkey: string[]; chanId: string[] } smsAuthUnsupportedCountries: string[] whatsAppAuthUnsupportedCountries: string[] - ibex: IbexConfig, + ibex: IbexConfig + bridge: BridgeConfig exchangeRates: StaticRates cashout: { enabled: boolean @@ -211,12 +231,12 @@ type FrappeConfig = { type CurrencyCode = string type PriceSpread = { - bid: number, - ask: number, + bid: number + ask: number } type StaticRates = { [key: CurrencyCode]: { [key: CurrencyCode]: PriceSpread } -} \ No newline at end of file +} diff --git a/src/config/yaml.ts b/src/config/yaml.ts index 0ac041840..136300441 100644 --- a/src/config/yaml.ts +++ b/src/config/yaml.ts @@ -24,44 +24,43 @@ import mergeWith from "lodash.mergewith" import { configSchema } from "./schema" import { ConfigError } from "./error" -import yargs from "yargs"; +import yargs from "yargs" -const argv: any = yargs(process.argv.slice(2)) - .option("configPath", { +const argv: any = + // .help() + yargs(process.argv.slice(2)).option("configPath", { alias: "c", type: "array", description: "Paths to YAML configuration files", demandOption: true, - }) - // .help() - .argv; + }).argv // replaces array with override const merge = (defaultConfig: unknown, customConfig: unknown) => mergeWith(defaultConfig, customConfig, (a, b) => (Array.isArray(b) ? b : undefined)) export const mergeYamls = (filePaths: string[]): Record => { - const mergedConfig: Record = {}; + const mergedConfig: Record = {} filePaths.forEach((filePath) => { try { - const resolvedPath = path.resolve(filePath); - const fileContent = fs.readFileSync(resolvedPath, "utf8"); - const parsedConfig = yaml.load(fileContent) as Record; + const resolvedPath = path.resolve(filePath) + const fileContent = fs.readFileSync(resolvedPath, "utf8") + const parsedConfig = yaml.load(fileContent) as Record merge(mergedConfig, parsedConfig) - baseLogger.info(`Successfully loaded config from ${resolvedPath}`); + baseLogger.info(`Successfully loaded config from ${resolvedPath}`) } catch (err) { - baseLogger.warn({ err, filePath }, `Failed to load config from ${filePath}`); + baseLogger.warn({ err, filePath }, `Failed to load config from ${filePath}`) } - }); + }) - return mergedConfig; -}; + return mergedConfig +} -const paths = argv.configPath.map((p: string) => path.resolve(p)) -const yamlConfigInit = mergeYamls(paths) +const paths = argv.configPath.map((p: string) => path.resolve(p)) +const yamlConfigInit = mergeYamls(paths) // TODO: fix errors // const ajv = new Ajv({ allErrors: true, strict: "log" }) @@ -359,7 +358,7 @@ const { ask } = yamlConfig.exchangeRates["USD"]["JMD"] const sellRate = JMDAmount.dollars(ask) if (sellRate instanceof BigIntConversionError) throw sellRate export const ExchangeRates = { - jmd: { sell: sellRate } + jmd: { sell: sellRate }, } export const Cashout = { @@ -384,12 +383,13 @@ export const Cashout = { to: yamlConfig.cashout.email.to, from: yamlConfig.cashout.email.from, subject: yamlConfig.cashout.email.subject, - } - + }, } export const SendGridConfig = yamlConfig.sendgrid as SendGridConfig export const IbexConfig = yamlConfig.ibex as IbexConfig -export const FrappeConfig = yamlConfig.frappe as FrappeConfig \ No newline at end of file +export const BridgeConfig = yamlConfig.bridge as BridgeConfig + +export const FrappeConfig = yamlConfig.frappe as FrappeConfig diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index 1f4de02b8..c2c11bd2a 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -1,5 +1,7 @@ type AccountError = import("./errors").AccountError +type BridgeCustomerId = import("@domain/primitives/bridge").BridgeCustomerId + type CurrencyRatio = number & { readonly brand: unique symbol } type AccountLevel = (typeof import("./index").AccountLevel)[keyof typeof import("./index").AccountLevel] @@ -84,6 +86,10 @@ type Account = { // temp role?: string erpParty?: string // Lookup key to Customer in ERPNext. Required for Account level > 1 + // Bridge integration: + bridgeCustomerId?: BridgeCustomerId + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string } // deprecated @@ -152,7 +158,7 @@ type AccountLimitsVolumes = type AccountValidator = { isActive(): true | ValidationError - isLevel(accountLevel: number): true | ValidationError + isLevel(accountLevel: number): true | ValidationError validateWalletForAccount(wallet: Wallet): true | ValidationError } @@ -168,6 +174,19 @@ interface IAccountsRepository { // listBusinessesForMap(): Promise findByNpub(npub: Npub): Promise update(account: Account): Promise + + updateBridgeFields( + id: AccountId, + fields: { + bridgeCustomerId?: BridgeCustomerId + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string + }, + ): Promise + + findByBridgeTronAddress(address: string): Promise + + findByBridgeCustomerId(customerId: BridgeCustomerId): Promise } type AdminRole = "dealer" | "funder" | "bankowner" | "editor" diff --git a/src/domain/primitives/bridge.ts b/src/domain/primitives/bridge.ts new file mode 100644 index 000000000..00305d3d7 --- /dev/null +++ b/src/domain/primitives/bridge.ts @@ -0,0 +1,23 @@ +// Bridge domain primitives - branded ID types for type safety + +export type BridgeCustomerId = string & { readonly brand: unique symbol } +export type BridgeVirtualAccountId = string & { readonly brand: unique symbol } +export type BridgeExternalAccountId = string & { readonly brand: unique symbol } +export type BridgeTransferId = string & { readonly brand: unique symbol } + +// Helper functions to create branded IDs +export const toBridgeCustomerId = (id: string): BridgeCustomerId => { + return id as BridgeCustomerId +} + +export const toBridgeVirtualAccountId = (id: string): BridgeVirtualAccountId => { + return id as BridgeVirtualAccountId +} + +export const toBridgeExternalAccountId = (id: string): BridgeExternalAccountId => { + return id as BridgeExternalAccountId +} + +export const toBridgeTransferId = (id: string): BridgeTransferId => { + return id as BridgeTransferId +} diff --git a/src/domain/shared/MoneyAmount.ts b/src/domain/shared/MoneyAmount.ts index 694bc0dc1..8818c20aa 100644 --- a/src/domain/shared/MoneyAmount.ts +++ b/src/domain/shared/MoneyAmount.ts @@ -60,6 +60,7 @@ export abstract class MoneyAmount { static from(amount: number | string, currency: WalletCurrency): MoneyAmount | Error { if (currency === WalletCurrency.Usd) return USDAmount.cents(amount.toString()) else if (currency === WalletCurrency.Jmd) return JMDAmount.cents(amount.toString()) + else if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(amount.toString()) else return new UnsupportedCurrencyError(`Could not read currency: ${currency}`) } } @@ -75,7 +76,9 @@ export class USDAmount extends MoneyAmount { try { return new USDAmount(cents) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } } @@ -87,9 +90,10 @@ export class USDAmount extends MoneyAmount { if (cents instanceof BigIntConversionError) return cents // should never happen return new USDAmount(cents.money.multiply(dollarAmt).toFixed(2)) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } - } static ZERO = new USDAmount(0) @@ -102,12 +106,12 @@ export class USDAmount extends MoneyAmount { return this.money.divide(100).toFixed(precision) } - // const jmdLiability = { - // amount: BigInt(usdLiability.asCents()) * exchangeRate / 100n, - // currency: "JMD", - // } + // const jmdLiability = { + // amount: BigInt(usdLiability.asCents()) * exchangeRate / 100n, + // currency: "JMD", + // } // Rate is the ratio at which one currency can be exchanged for another. - // T:USD + // T:USD convertAtRate(rate: T): T { const converted = rate.money.multiply(this.money).divide(100) return rate.getInstance(converted) @@ -133,7 +137,9 @@ export class JMDAmount extends MoneyAmount { try { return new JMDAmount(c) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } } @@ -141,9 +147,10 @@ export class JMDAmount extends MoneyAmount { try { return new JMDAmount(BigInt(d) * 100n) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } - } asCents(precision: number = 0): string { @@ -182,3 +189,52 @@ export class BtcAmount extends MoneyAmount { return new BtcAmount(amount) as this } } + +export class USDTAmount extends MoneyAmount { + static currencyId: IbexCurrencyId = 4 as IbexCurrencyId + + private constructor(amount: Money | bigint | string | number) { + super(amount, WalletCurrency.Usdt) + } + + static smallestUnits(units: string | bigint): USDTAmount | BigIntConversionError { + try { + return new USDTAmount(units) + } catch (error) { + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) + } + } + + static fromNumber(d: number | string): USDTAmount | BigIntConversionError { + try { + const usdtAmt = new Money(d.toString(), "USDT", Round.HALF_TO_EVEN) + const multiplier = USDTAmount.smallestUnits(1_000_000n) + if (multiplier instanceof BigIntConversionError) return multiplier + return new USDTAmount(multiplier.money.multiply(usdtAmt).toFixed(0)) + } catch (error) { + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) + } + } + + static ZERO = new USDTAmount(0) + + asSmallestUnits(precision: number = 0): string { + return this.money.toFixed(precision) + } + + asNumber(precision: number = 6): string { + return this.money.divide(1_000_000).toFixed(precision) + } + + toIbex(): number { + return Number(this.asNumber(8)) + } + + getInstance(amount: Money): this { + return new USDTAmount(amount) as this + } +} diff --git a/src/domain/shared/primitives.ts b/src/domain/shared/primitives.ts index 4bc41e1d2..8a23e05c1 100644 --- a/src/domain/shared/primitives.ts +++ b/src/domain/shared/primitives.ts @@ -5,6 +5,7 @@ export const WalletCurrency = { Usd: "USD", Jmd: "JMD", Btc: "BTC", + Usdt: "USDT", } as const export const ExchangeCurrencyUnit = { diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 2af9f8ffc..67183ead0 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -471,10 +471,52 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "UnexpectedIbexResponse": return new IbexError(baseLogger) case "OfferNotFound": - return new NotFoundError({ - message: "Offer not available. Try again.", - logger: baseLogger + return new NotFoundError({ + message: "Offer not available. Try again.", + logger: baseLogger, }) + + case "BridgeDisabledError": + message = "Bridge integration is currently disabled" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeAccountLevelError": + message = "Bridge requires Pro account (Level 2+)" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeKycPendingError": + message = "KYC verification is pending" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeKycRejectedError": + message = "KYC verification was rejected" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeCustomerNotFoundError": + message = "Bridge customer not found" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeInsufficientFundsError": + message = "Insufficient funds for withdrawal" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeRateLimitError": + message = "Rate limit exceeded, please try again later" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeTimeoutError": + message = "Request timed out" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeWebhookValidationError": + message = "Invalid webhook signature" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeApiError": + case "BridgeError": + message = error.message || "Bridge API error" + return new UnknownClientError({ message, logger: baseLogger }) + // ---------- // Unhandled below here // ---------- @@ -745,18 +787,19 @@ export const mapError = (error: ApplicationError): CustomApolloError => { } // Move to CustomApolloError class? -export const apolloErrorResponse = (e: CustomApolloError): { errors: IError[] } => { +export const apolloErrorResponse = (e: CustomApolloError): { errors: IError[] } => { return { errors: [ { message: e.message, path: e.path, - code: e.extensions.code - } + code: e.extensions.code, + }, ] } } + export const mapAndParseErrorForGqlResponse = (err: ApplicationError): IError => { const mappedError = mapError(err) return { @@ -767,7 +810,7 @@ export const mapAndParseErrorForGqlResponse = (err: ApplicationError): IError => } export const mapToGqlErrorList = (err: ApplicationError): IError[] => { - if (err instanceof ValidationError && err.errors) return err.errors.map(mapAndParseErrorForGqlResponse) + if (err instanceof ValidationError && err.errors) + return err.errors.map(mapAndParseErrorForGqlResponse) else return [mapAndParseErrorForGqlResponse(err)] - } diff --git a/src/graphql/public/mutations.ts b/src/graphql/public/mutations.ts index 8e0ccc102..8469c5266 100644 --- a/src/graphql/public/mutations.ts +++ b/src/graphql/public/mutations.ts @@ -61,6 +61,10 @@ import RequestCashoutMutation from "./root/mutation/offers/request-cash-out" import InitiateCashoutMutation from "./root/mutation/offers/initiate-cash-out" import IdDocumentUploadUrlGenerateMutation from "./root/mutation/id-document-upload-url-generate" import UpdateExternalWalletMutation from "./root/mutation/update-external-wallet" +import BridgeInitiateKycMutation from "./root/mutation/bridge-initiate-kyc" +import BridgeCreateVirtualAccountMutation from "./root/mutation/bridge-create-virtual-account" +import BridgeAddExternalAccountMutation from "./root/mutation/bridge-add-external-account" +import BridgeInitiateWithdrawalMutation from "./root/mutation/bridge-initiate-withdrawal" // TODO: // const fields: { [key: string]: GraphQLFieldConfig } export const mutationFields = { @@ -115,6 +119,10 @@ export const mutationFields = { idDocumentUploadUrlGenerate: IdDocumentUploadUrlGenerateMutation, updateExternalWallet: UpdateExternalWalletMutation, + bridgeInitiateKyc: BridgeInitiateKycMutation, + bridgeCreateVirtualAccount: BridgeCreateVirtualAccountMutation, + bridgeAddExternalAccount: BridgeAddExternalAccountMutation, + bridgeInitiateWithdrawal: BridgeInitiateWithdrawalMutation, }, atWalletLevel: { diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index d30d84a32..2d95858b7 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -19,8 +19,12 @@ import LnInvoicePaymentStatusQuery from "@graphql/public/root/query/ln-invoice-p import NpubByUserNameQuery from "./root/query/username-npub-query" import IsFlashNpubQuery from "./root/query/is-flash-npub-query" import TransactionDetailsQuery from "./root/query/transaction-details" -import LatestAccountUpgradeRequestQuery from "./root/query/account-upgrade-request" +import AccountUpgradeRequestQuery from "./root/query/account-upgrade-request" import SupportedBanksQuery from "./root/query/supported-banks" +import BridgeKycStatusQuery from "./root/query/bridge-kyc-status" +import BridgeVirtualAccountQuery from "./root/query/bridge-virtual-account" +import BridgeExternalAccountsQuery from "./root/query/bridge-external-accounts" +import BridgeWithdrawalsQuery from "./root/query/bridge-withdrawals" export const queryFields = { unauthed: { @@ -44,7 +48,11 @@ export const queryFields = { atAccountLevel: { me: MeQuery, transactionDetails: TransactionDetailsQuery, - latestAccountUpgradeRequest: LatestAccountUpgradeRequestQuery, + accountUpgradeRequest: AccountUpgradeRequestQuery, + bridgeKycStatus: BridgeKycStatusQuery, + bridgeVirtualAccount: BridgeVirtualAccountQuery, + bridgeExternalAccounts: BridgeExternalAccountsQuery, + bridgeWithdrawals: BridgeWithdrawalsQuery, }, atWalletLevel: { onChainTxFee: OnChainTxFeeQuery, diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts new file mode 100644 index 000000000..10e4e279b --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -0,0 +1,36 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeAddExternalAccountPayload = GT.Object({ + name: "BridgeAddExternalAccountPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + externalAccount: { type: BridgeExternalAccount }, + }), +}) + +const bridgeAddExternalAccount = GT.Field({ + type: GT.NonNull(BridgeAddExternalAccountPayload), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.addExternalAccount(domainAccount.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { externalAccount: result, errors: [] } + }, +}) + +export default bridgeAddExternalAccount diff --git a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts new file mode 100644 index 000000000..55abd4998 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts @@ -0,0 +1,36 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeCreateVirtualAccountPayload = GT.Object({ + name: "BridgeCreateVirtualAccountPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + virtualAccount: { type: BridgeVirtualAccount }, + }), +}) + +const bridgeCreateVirtualAccount = GT.Field({ + type: GT.NonNull(BridgeCreateVirtualAccountPayload), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.createVirtualAccount(domainAccount.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { virtualAccount: result, errors: [] } + }, +}) + +export default bridgeCreateVirtualAccount diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts new file mode 100644 index 000000000..9e3234ee3 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -0,0 +1,36 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeKycLink from "@graphql/public/types/object/bridge-kyc-link" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeInitiateKycPayload = GT.Object({ + name: "BridgeInitiateKycPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + kycLink: { type: BridgeKycLink }, + }), +}) + +const bridgeInitiateKyc = GT.Field({ + type: GT.NonNull(BridgeInitiateKycPayload), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.initiateKyc(domainAccount.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { kycLink: result, errors: [] } + }, +}) + +export default bridgeInitiateKyc diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts new file mode 100644 index 000000000..738c10c83 --- /dev/null +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -0,0 +1,53 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" + +const BridgeInitiateWithdrawalInput = GT.Input({ + name: "BridgeInitiateWithdrawalInput", + fields: () => ({ + amount: { type: GT.NonNull(GT.String) }, + externalAccountId: { type: GT.NonNull(GT.ID) }, + }), +}) + +const BridgeInitiateWithdrawalPayload = GT.Object({ + name: "BridgeInitiateWithdrawalPayload", + fields: () => ({ + errors: { type: GT.List(GT.NonNull(Error)) }, + withdrawal: { type: BridgeWithdrawal }, + }), +}) + +const bridgeInitiateWithdrawal = GT.Field({ + type: GT.NonNull(BridgeInitiateWithdrawalPayload), + args: { + input: { type: GT.NonNull(BridgeInitiateWithdrawalInput) }, + }, + resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { + const { amount, externalAccountId } = args.input + + if (!BridgeConfig.enabled) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } + } + + if (!domainAccount || domainAccount.level < 2) { + return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } + } + + const result = await BridgeService.initiateWithdrawal( + domainAccount.id, + amount, + externalAccountId, + ) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { withdrawal: result, errors: [] } + }, +}) + +export default bridgeInitiateWithdrawal diff --git a/src/graphql/public/root/query/bridge-external-accounts.ts b/src/graphql/public/root/query/bridge-external-accounts.ts new file mode 100644 index 000000000..d58b9b41c --- /dev/null +++ b/src/graphql/public/root/query/bridge-external-accounts.ts @@ -0,0 +1,26 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeExternalAccounts = GT.Field({ + type: GT.List(BridgeExternalAccount), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getExternalAccounts(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeExternalAccounts diff --git a/src/graphql/public/root/query/bridge-kyc-status.ts b/src/graphql/public/root/query/bridge-kyc-status.ts new file mode 100644 index 000000000..fab9cc4b3 --- /dev/null +++ b/src/graphql/public/root/query/bridge-kyc-status.ts @@ -0,0 +1,25 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeKycStatus = GT.Field({ + type: GT.String, + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getKycStatus(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeKycStatus diff --git a/src/graphql/public/root/query/bridge-virtual-account.ts b/src/graphql/public/root/query/bridge-virtual-account.ts new file mode 100644 index 000000000..a563273dd --- /dev/null +++ b/src/graphql/public/root/query/bridge-virtual-account.ts @@ -0,0 +1,26 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeVirtualAccount = GT.Field({ + type: BridgeVirtualAccount, + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getVirtualAccount(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeVirtualAccount diff --git a/src/graphql/public/root/query/bridge-withdrawals.ts b/src/graphql/public/root/query/bridge-withdrawals.ts new file mode 100644 index 000000000..1995670e9 --- /dev/null +++ b/src/graphql/public/root/query/bridge-withdrawals.ts @@ -0,0 +1,26 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" +import { BridgeConfig } from "@config" +import BridgeService from "@services/bridge" +import { BridgeDisabledError } from "@services/bridge/errors" + +const bridgeWithdrawals = GT.Field({ + type: GT.List(BridgeWithdrawal), + resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + if (!BridgeConfig.enabled) { + throw mapAndParseErrorForGqlResponse(new BridgeDisabledError()) + } + + if (!domainAccount) return null + + const result = await BridgeService.getWithdrawals(domainAccount.id) + if (result instanceof Error) { + throw mapAndParseErrorForGqlResponse(result) + } + + return result + }, +}) + +export default bridgeWithdrawals diff --git a/src/graphql/public/types/index.ts b/src/graphql/public/types/index.ts index a134f1349..9d9acada0 100644 --- a/src/graphql/public/types/index.ts +++ b/src/graphql/public/types/index.ts @@ -4,6 +4,7 @@ import BtcWallet from "../../shared/types/object/btc-wallet" import GraphQLApplicationError from "../../shared/types/object/graphql-application-error" import UsdWallet from "../../shared/types/object/usd-wallet" +import UsdtWallet from "../../shared/types/object/usdt-wallet" import ConsumerAccount from "./object/consumer-account" import OneDayAccountLimit from "./object/one-day-account-limit" @@ -17,5 +18,6 @@ export const ALL_INTERFACE_TYPES = [ // BusinessAccount, BtcWallet, UsdWallet, + UsdtWallet, OneDayAccountLimit, ] diff --git a/src/graphql/public/types/object/bridge-external-account.ts b/src/graphql/public/types/object/bridge-external-account.ts new file mode 100644 index 000000000..890733978 --- /dev/null +++ b/src/graphql/public/types/object/bridge-external-account.ts @@ -0,0 +1,13 @@ +import { GT } from "@graphql/index" + +const BridgeExternalAccount = GT.Object({ + name: "BridgeExternalAccount", + fields: () => ({ + id: { type: GT.NonNullID }, + bankName: { type: GT.NonNull(GT.String) }, + accountNumberLast4: { type: GT.NonNull(GT.String) }, + status: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeExternalAccount diff --git a/src/graphql/public/types/object/bridge-kyc-link.ts b/src/graphql/public/types/object/bridge-kyc-link.ts new file mode 100644 index 000000000..9f0071dd9 --- /dev/null +++ b/src/graphql/public/types/object/bridge-kyc-link.ts @@ -0,0 +1,11 @@ +import { GT } from "@graphql/index" + +const BridgeKycLink = GT.Object({ + name: "BridgeKycLink", + fields: () => ({ + kycLink: { type: GT.NonNull(GT.String) }, + tosLink: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeKycLink diff --git a/src/graphql/public/types/object/bridge-virtual-account.ts b/src/graphql/public/types/object/bridge-virtual-account.ts new file mode 100644 index 000000000..eef1c4468 --- /dev/null +++ b/src/graphql/public/types/object/bridge-virtual-account.ts @@ -0,0 +1,13 @@ +import { GT } from "@graphql/index" + +const BridgeVirtualAccount = GT.Object({ + name: "BridgeVirtualAccount", + fields: () => ({ + id: { type: GT.NonNullID }, + bankName: { type: GT.NonNull(GT.String) }, + routingNumber: { type: GT.NonNull(GT.String) }, + accountNumberLast4: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeVirtualAccount diff --git a/src/graphql/public/types/object/bridge-withdrawal.ts b/src/graphql/public/types/object/bridge-withdrawal.ts new file mode 100644 index 000000000..f29ba250d --- /dev/null +++ b/src/graphql/public/types/object/bridge-withdrawal.ts @@ -0,0 +1,14 @@ +import { GT } from "@graphql/index" + +const BridgeWithdrawal = GT.Object({ + name: "BridgeWithdrawal", + fields: () => ({ + id: { type: GT.NonNullID }, + amount: { type: GT.NonNull(GT.String) }, + currency: { type: GT.NonNull(GT.String) }, + status: { type: GT.NonNull(GT.String) }, + createdAt: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeWithdrawal diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index af7e947f0..27096a000 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -10,7 +10,7 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract import { Wallets } from "@app" -import { WalletCurrency as WalletCurrencyDomain } from "@domain/shared" +import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" import { WalletType } from "@domain/wallets" import IWallet from "../abstract/wallet" @@ -51,10 +51,16 @@ const UsdWallet = GT.Object({ type: FractionalCentAmount, resolve: async (source) => { if (source.type === WalletType.External) return null - const balance = await Wallets.getBalanceForWallet({ walletId: source.id }) + const balance = await Wallets.getBalanceForWallet({ + walletId: source.id, + currency: source.currency, + }) if (balance instanceof Error) { throw mapError(balance) } + if (balance instanceof USDTAmount) { + return Number(balance.asSmallestUnits(8)) + } return Number(balance.asCents(8)) }, }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts new file mode 100644 index 000000000..d91a0e613 --- /dev/null +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -0,0 +1,140 @@ +import { GT } from "@graphql/index" +import { + connectionArgs, + connectionFromPaginatedArray, + checkedConnectionArgs, +} from "@graphql/connections" +import { normalizePaymentAmount } from "@graphql/shared/root/mutation" +import { mapError } from "@graphql/error-map" + +import { Wallets } from "@app" + +import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" + +import IWallet from "../abstract/wallet" + +import WalletCurrency from "../scalar/wallet-currency" +import SignedAmount from "../scalar/signed-amount" +import OnChainAddress from "../scalar/on-chain-address" +import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" + +import { TransactionConnection } from "./transaction" +import { baseLogger } from "@services/logger" +import Lnurl from "../scalar/lnurl" + +const UsdtWallet = GT.Object({ + name: "UsdtWallet", + description: + "A wallet belonging to an account which contains a USDT balance and a list of transactions.", + interfaces: () => [IWallet], + isTypeOf: (source) => source.currency === WalletCurrencyDomain.Usdt, + fields: () => ({ + id: { + type: GT.NonNullID, + }, + accountId: { + type: GT.NonNullID, + }, + walletCurrency: { + type: GT.NonNull(WalletCurrency), + resolve: (source) => source.currency, + }, + + lnurlp: { + type: Lnurl, + resolve: (source) => source.lnurlp, + }, + + balance: { + type: GT.NonNull(FractionalCentAmount), + resolve: async (source) => { + const balance = await Wallets.getBalanceForWallet({ + walletId: source.id, + currency: source.currency, + }) + if (balance instanceof Error) { + throw mapError(balance) + } + if (balance instanceof USDTAmount) { + return Number(balance.asSmallestUnits(8)) + } + return Number((balance as any).asCents(8)) + }, + }, + pendingIncomingBalance: { + type: GT.NonNull(SignedAmount), + description: "An unconfirmed incoming onchain balance.", + resolve: async (source) => { + const balanceSats = await Wallets.getPendingOnChainBalanceForWallets([source]) + if (balanceSats instanceof Error) { + throw mapError(balanceSats) + } + return normalizePaymentAmount(balanceSats[source.id]).amount + }, + }, + transactions: { + type: TransactionConnection, + args: connectionArgs, + resolve: async (source, args) => { + const paginationArgs = checkedConnectionArgs(args) + if (paginationArgs instanceof Error) { + throw paginationArgs + } + + const { result, error } = await Wallets.getTransactionsForWallets({ + wallets: [source], + paginationArgs, + }) + if (error instanceof Error) { + throw mapError(error) + } + + if (!result?.slice) throw error + + return connectionFromPaginatedArray( + result.slice, + result.total, + paginationArgs, + ) + }, + }, + transactionsByAddress: { + type: TransactionConnection, + args: { + ...connectionArgs, + address: { + type: GT.NonNull(OnChainAddress), + description: "Returns the items that include this address.", + }, + }, + resolve: async (source, args) => { + const paginationArgs = checkedConnectionArgs(args) + if (paginationArgs instanceof Error) { + throw paginationArgs + } + + const { address } = args + if (address instanceof Error) throw address + + const { result, error } = await Wallets.getTransactionsForWalletsByAddresses({ + wallets: [source], + addresses: [address], + paginationArgs, + }) + if (error instanceof Error) { + throw mapError(error) + } + + if (!result?.slice) throw error + + return connectionFromPaginatedArray( + result.slice, + result.total, + paginationArgs, + ) + }, + }, + }), +}) + +export default UsdtWallet diff --git a/src/graphql/shared/types/scalar/wallet-currency.ts b/src/graphql/shared/types/scalar/wallet-currency.ts index 65b9090b6..9cbaf314d 100644 --- a/src/graphql/shared/types/scalar/wallet-currency.ts +++ b/src/graphql/shared/types/scalar/wallet-currency.ts @@ -5,6 +5,7 @@ const WalletCurrency = GT.Enum({ values: { BTC: {}, USD: {}, + USDT: {}, }, }) diff --git a/src/servers/bridge-webhook-server.ts b/src/servers/bridge-webhook-server.ts new file mode 100644 index 000000000..b05763936 --- /dev/null +++ b/src/servers/bridge-webhook-server.ts @@ -0,0 +1,8 @@ +/** + * Bridge Webhook Server Entrypoint + * Starts the standalone Bridge webhook server + */ + +import { startBridgeWebhookServer } from "@services/bridge/webhook-server" + +startBridgeWebhookServer() diff --git a/src/servers/graphql-admin-server.ts b/src/servers/graphql-admin-server.ts index 1a220b07c..3a31cae69 100644 --- a/src/servers/graphql-admin-server.ts +++ b/src/servers/graphql-admin-server.ts @@ -15,31 +15,34 @@ import { GraphQLError, GraphQLSchema } from "graphql" import PinoHttp from "pino-http" import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "graphql-query-complexity-apollo-plugin" +import { createComplexityPlugin } from "./plugins/complexity" import { parseUnknownDomainErrorFromUnknown } from "@domain/shared" import healthzHandler from "./middlewares/healthz" import { idempotencyMiddleware } from "./middlewares/idempotency" import requestIp from "request-ip" -import jwt from 'jsonwebtoken' +import jwt from "jsonwebtoken" import { ErpNextRole, ErpNextRoles } from "@services/frappe/Roles" const graphqlLogger = baseLogger.child({ module: "graphql" }) interface JWTPayload { - userId: string; - roles: string[]; + userId: string + roles: string[] } // Parse the "Authorization" header to verify the JWT token and return its payload function parseAuthHeader(authHeader: string | undefined): JWTPayload { - if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new AuthenticationError({ message: 'Invalid authorization header', logger: graphqlLogger }); + if (!authHeader || !authHeader.startsWith("Bearer ")) { + throw new AuthenticationError({ + message: "Invalid authorization header", + logger: graphqlLogger, + }) } try { - const token = authHeader.slice(7); - return jwt.verify(token, ADMIN_CONFIG.ERPNEXT_JWT_SECRET as string) as JWTPayload; // process.env.ERPNEXT_JWT_SECRET + const token = authHeader.slice(7) + return jwt.verify(token, ADMIN_CONFIG.ERPNEXT_JWT_SECRET as string) as JWTPayload // process.env.ERPNEXT_JWT_SECRET } catch (error) { - throw new AuthenticationError({ message: 'Invalid Token', logger: graphqlLogger }); + throw new AuthenticationError({ message: "Invalid Token", logger: graphqlLogger }) } } @@ -62,7 +65,7 @@ export const hasRole = (role: ErpNextRole) => rule({ cache: "contextual" })(( // // } // addAttributesToCurrentSpanAndPropagate( // { - // [SemanticAttributes.HTTP_CLIENT_IP]: ip, +// [SemanticAttributes.HTTP_CLIENT_IP]: ip, // [SemanticAttributes.HTTP_USER_AGENT]: req.headers["user-agent"], // }, // next, @@ -99,15 +102,15 @@ const startAdminServer = async ({ cache: "bounded", plugins: apolloPlugins, context: (ctx: ExpressContext) => { - const { authorization } = ctx.req.headers; - const decodedJwt = parseAuthHeader(authorization); + const { authorization } = ctx.req.headers + const decodedJwt = parseAuthHeader(authorization) return { - logger: graphqlLogger, + logger: graphqlLogger, user: { id: decodedJwt.userId, roles: decodedJwt.roles, ip: requestIp.getClientIp(ctx.req), - } + }, } }, formatError: (err) => { @@ -201,9 +204,7 @@ const startAdminServer = async ({ ) console.log( - `in dev mode, ${type} server should be accessed through oathkeeper reverse proxy at ${ - "http://localhost:4002/admin/graphql" - }`, + `in dev mode, ${type} server should be accessed through oathkeeper reverse proxy at ${"http://localhost:4002/admin/graphql"}`, ) resolve({ app, httpServer, apolloServer }) @@ -259,4 +260,4 @@ if (require.main === module) { await startApolloServerForAdminSchema() }) .catch((err) => graphqlLogger.error(err, "server error")) -} \ No newline at end of file +} diff --git a/src/servers/graphql-server.ts b/src/servers/graphql-server.ts index 4e2dbc538..6ddc84da6 100644 --- a/src/servers/graphql-server.ts +++ b/src/servers/graphql-server.ts @@ -16,7 +16,7 @@ import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "graphql-query-complexity-apollo-plugin" +import { createComplexityPlugin } from "./plugins/complexity" import jwksRsa from "jwks-rsa" diff --git a/src/servers/plugins/complexity.ts b/src/servers/plugins/complexity.ts new file mode 100644 index 000000000..5bfc3ee1b --- /dev/null +++ b/src/servers/plugins/complexity.ts @@ -0,0 +1,45 @@ +import { GraphQLSchema } from "graphql" +import { getComplexity, ComplexityEstimator } from "graphql-query-complexity" +import type { + ApolloServerPlugin, + GraphQLRequestListener, +} from "apollo-server-plugin-base" + +interface ComplexityPluginOptions { + schema: GraphQLSchema + estimators: ComplexityEstimator[] + maximumComplexity: number + onComplete?: (complexity: number) => void +} + +export function createComplexityPlugin( + options: ComplexityPluginOptions, +): ApolloServerPlugin { + const { schema, estimators, maximumComplexity, onComplete } = options + + return { + async requestDidStart(): Promise { + return { + async didResolveOperation({ request, document }) { + const complexity = getComplexity({ + schema, + operationName: request.operationName ?? undefined, + query: document, + variables: request.variables ?? {}, + estimators, + }) + + if (onComplete) { + onComplete(complexity) + } + + if (complexity > maximumComplexity) { + throw new Error( + `Query complexity of ${complexity} exceeds maximum allowed complexity of ${maximumComplexity}`, + ) + } + }, + } + }, + } +} diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts new file mode 100644 index 000000000..f8e64dfd2 --- /dev/null +++ b/src/services/bridge/client.ts @@ -0,0 +1,367 @@ +/** + * Bridge.xyz API Client + * Ported from bridge-mcp and extended with Tron/USDT support + */ + +import { BridgeConfig } from "@config" +import { + BridgeCustomerId, + BridgeVirtualAccountId, + BridgeExternalAccountId, + BridgeTransferId, + toBridgeCustomerId, + toBridgeVirtualAccountId, + toBridgeExternalAccountId, + toBridgeTransferId, +} from "@domain/primitives/bridge" + +// ============ Error Handling ============ + +export class BridgeApiError extends Error { + constructor( + message: string, + public statusCode: number, + public response?: unknown, + ) { + super(message) + this.name = "BridgeApiError" + } +} + +// ============ Request/Response Types ============ + +export interface CreateIndividualCustomerRequest { + type: "individual" + first_name: string + last_name: string + email: string + phone?: string + address?: { + street_line_1: string + street_line_2?: string + city: string + state?: string + postal_code: string + country: string + } + birth_date?: string + tax_identification_number?: string +} + +export interface CreateBusinessCustomerRequest { + type: "business" + business_name: string + email: string + phone?: string + address?: { + street_line_1: string + street_line_2?: string + city: string + state?: string + postal_code: string + country: string + } + ein?: string +} + +export type CreateCustomerRequest = + | CreateIndividualCustomerRequest + | CreateBusinessCustomerRequest + +export interface Customer { + id: string + type: "individual" | "business" + kyc_status: string + tos_status: string + created_at: string + updated_at: string + first_name?: string + last_name?: string + email?: string + business_name?: string +} + +export interface KycLink { + kyc_link: string + tos_link: string + customer_id: string +} + +// Extended payment rails to include Tron +export type PaymentRail = + | "solana" + | "ethereum" + | "polygon" + | "base" + | "tron" + | "ach_push" + | "ach_pull" + | "wire" + | "sepa" + | "spei" + | "pix" + +// Extended currencies to include USDT +export type Currency = + | "usd" + | "eur" + | "mxn" + | "brl" + | "gbp" + | "usdc" + | "usdb" + | "eurc" + | "usdt" + +export interface CreateVirtualAccountRequest { + developer_fee_percent?: string + source: { + currency: "usd" | "eur" | "mxn" | "brl" | "gbp" + } + destination: { + currency: Currency + payment_rail: PaymentRail + address?: string + bridge_wallet_id?: string + } +} + +export interface VirtualAccount { + id: string + status: string + customer_id: string + developer_fee_percent?: string + source_deposit_instructions: { + currency: string + payment_rails: string[] + bank_name?: string + bank_address?: string + bank_beneficiary_name?: string + bank_account_number?: string + bank_routing_number?: string + iban?: string + bic?: string + } + destination: { + currency: string + payment_rail: string + address?: string + bridge_wallet_id?: string + } + created_at: string +} + +export interface ExternalAccount { + id: string + customer_id: string + account_type: string + currency: string + bank_name?: string + account_number_last_4?: string + routing_number?: string + iban?: string + created_at: string +} + +export interface ExternalAccountLinkUrl { + link_url: string + expires_at: string +} + +export interface ListResponse { + data: T[] + has_more: boolean + cursor?: string +} + +export interface CreateTransferRequest { + amount?: string + currency?: string + on_behalf_of: string + developer_fee?: string + developer_fee_percent?: string + source: { + payment_rail: PaymentRail | "bridge_wallet" + currency: string + from_address?: string + external_account_id?: string + bridge_wallet_id?: string + } + destination: { + payment_rail: PaymentRail | "bridge_wallet" | "ach" + currency: string + to_address?: string + external_account_id?: string + bridge_wallet_id?: string + wire_message?: string + } + dry_run?: boolean + features?: { + flexible_amount?: boolean + static_template?: boolean + allow_any_from_address?: boolean + } +} + +export interface Transfer { + id: string + client_reference_id?: string + amount: string + currency: string + on_behalf_of: string + developer_fee?: string + source: { + payment_rail: string + currency: string + from_address?: string + external_account_id?: string + bridge_wallet_id?: string + } + destination: { + payment_rail: string + currency: string + to_address?: string + external_account_id?: string + bridge_wallet_id?: string + } + state: string + source_deposit_instructions?: { + payment_rail: string + currency: string + amount?: string + bank_name?: string + bank_address?: string + bank_account_number?: string + bank_routing_number?: string + to_address?: string + } + receipt?: Record + created_at: string + updated_at: string +} + +// ============ Bridge Client ============ + +export class BridgeClient { + private apiKey: string + private baseUrl: string + + constructor() { + this.apiKey = BridgeConfig.apiKey + this.baseUrl = BridgeConfig.baseUrl || "https://api.bridge.xyz/v0" + } + + private async request( + method: string, + path: string, + body?: unknown, + idempotencyKey?: string, + ): Promise { + const url = `${this.baseUrl}${path}` + const headers: Record = { + "Api-Key": this.apiKey, + "Content-Type": "application/json", + } + + if (idempotencyKey) { + headers["Idempotency-Key"] = idempotencyKey + } + + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + }) + + const responseData = await response.json().catch(() => null) + + if (!response.ok) { + throw new BridgeApiError( + `Bridge API error: ${response.status} ${response.statusText}`, + response.status, + responseData, + ) + } + + return responseData as T + } + + // ============ Customers ============ + + async createCustomer( + data: CreateCustomerRequest, + idempotencyKey?: string, + ): Promise { + return this.request("POST", "/customers", data, idempotencyKey) + } + + async getCustomer(customerId: BridgeCustomerId): Promise { + return this.request("GET", `/customers/${customerId}`) + } + + // ============ KYC ============ + + async createKycLink(customerId: BridgeCustomerId): Promise { + return this.request("POST", "/kyc_links", { customer_id: customerId }) + } + + // ============ Virtual Accounts ============ + + async createVirtualAccount( + customerId: BridgeCustomerId, + data: CreateVirtualAccountRequest, + idempotencyKey?: string, + ): Promise { + return this.request( + "POST", + `/customers/${customerId}/virtual_accounts`, + data, + idempotencyKey, + ) + } + + // ============ External Accounts ============ + + async getExternalAccountLinkUrl( + customerId: BridgeCustomerId, + ): Promise { + return this.request( + "POST", + `/customers/${customerId}/external_accounts/link`, + ) + } + + async listExternalAccounts( + customerId: BridgeCustomerId, + ): Promise> { + return this.request>( + "GET", + `/customers/${customerId}/external_accounts`, + ) + } + + // ============ Transfers ============ + + async createTransfer( + customerId: BridgeCustomerId, + data: CreateTransferRequest, + idempotencyKey?: string, + ): Promise { + // Note: Bridge API expects on_behalf_of in the body, not in the path + const bodyWithCustomer = { + ...data, + on_behalf_of: customerId, + } + return this.request("POST", "/transfers", bodyWithCustomer, idempotencyKey) + } + + async getTransfer( + customerId: BridgeCustomerId, + transferId: BridgeTransferId, + ): Promise { + // Note: Bridge API uses /transfers/{id} not /customers/{id}/transfers/{id} + return this.request("GET", `/transfers/${transferId}`) + } +} + +export default new BridgeClient() diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts new file mode 100644 index 000000000..737869fba --- /dev/null +++ b/src/services/bridge/errors.ts @@ -0,0 +1,89 @@ +import { DomainError, ErrorLevel } from "@domain/shared" + +export class BridgeError extends DomainError { + readonly level: ErrorLevel = ErrorLevel.Warn +} + +export class BridgeApiError extends BridgeError { + readonly statusCode: number + readonly response?: unknown + + constructor(message: string, statusCode: number, response?: unknown) { + super(message) + this.statusCode = statusCode + this.response = response + } +} + +export class BridgeRateLimitError extends BridgeError { + constructor(message: string = "Rate limit exceeded, please try again later") { + super(message) + } +} + +export class BridgeTimeoutError extends BridgeError { + constructor(message: string = "Request timed out") { + super(message) + } +} + +export class BridgeCustomerNotFoundError extends BridgeError { + constructor(message: string = "Bridge customer not found") { + super(message) + } +} + +export class BridgeKycPendingError extends BridgeError { + constructor(message: string = "KYC verification is pending") { + super(message) + } +} + +export class BridgeKycRejectedError extends BridgeError { + constructor(message: string = "KYC verification was rejected") { + super(message) + } +} + +export class BridgeInsufficientFundsError extends BridgeError { + constructor(message: string = "Insufficient funds for withdrawal") { + super(message) + } +} + +export class BridgeAccountLevelError extends BridgeError { + constructor(message: string = "Bridge requires Pro account (Level 2+)") { + super(message) + } +} + +export class BridgeDisabledError extends BridgeError { + constructor(message: string = "Bridge integration is currently disabled") { + super(message) + } +} + +export class BridgeWebhookValidationError extends BridgeError { + constructor(message: string = "Invalid webhook signature") { + super(message) + } +} + +/** + * Maps HTTP status codes from Bridge API to domain error types + */ +export const mapBridgeHttpError = ( + statusCode: number, + response?: unknown, +): BridgeError => { + switch (statusCode) { + case 404: + return new BridgeCustomerNotFoundError() + case 429: + return new BridgeRateLimitError() + case 408: + return new BridgeTimeoutError() + default: + return new BridgeApiError("Bridge API error", statusCode, response) + } +} diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts new file mode 100644 index 000000000..fca80b89d --- /dev/null +++ b/src/services/bridge/index.ts @@ -0,0 +1,585 @@ +/** + * Bridge Service Layer + * Orchestrates Bridge API client, repository, and implements business logic + * for USD on/off-ramp functionality via Bridge.xyz + */ + +import { BridgeConfig } from "@config" +import BridgeClient, { + KycLink, + VirtualAccount, + ExternalAccountLinkUrl, + Transfer, +} from "./client" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { AccountsRepository } from "@services/mongoose/accounts" +import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" +import { baseLogger } from "@services/logger" +import { + BridgeDisabledError, + BridgeAccountLevelError, + BridgeKycPendingError, + BridgeKycRejectedError, + BridgeCustomerNotFoundError, +} from "./errors" +import { RepositoryError } from "@domain/errors" +import { toBridgeCustomerId, toBridgeExternalAccountId } from "@domain/primitives/bridge" + +// ============ Types ============ + +type InitiateKycResult = { + kycLink: string + tosLink: string +} + +type CreateVirtualAccountResult = { + virtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string +} + +type AddExternalAccountResult = { + linkUrl: string + expiresAt: string +} + +type InitiateWithdrawalResult = { + transferId: string + amount: string + currency: string + state: string +} + +type WithdrawalResult = { + transferId: string + amount: string + currency: string + state: string + createdAt: string +} + +type KycStatusResult = "pending" | "approved" | "rejected" | null + +type VirtualAccountResult = { + bridgeVirtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string +} | null + +type ExternalAccountResult = { + bridgeExternalAccountId: string + bankName: string + accountNumberLast4: string + status: "pending" | "verified" | "failed" +} + +// ============ Guards ============ + +const checkBridgeEnabled = (): true | BridgeDisabledError => { + if (!BridgeConfig.enabled) { + return new BridgeDisabledError() + } + return true +} + +const checkAccountLevel = async ( + accountId: AccountId, +): Promise => { + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + if (account.level < 2) { + return new BridgeAccountLevelError() + } + return account +} + +// ============ Service Methods ============ + +/** + * Initiates KYC process for an account + * - Creates Bridge customer if not exists + * - Returns KYC and TOS links + */ +const initiateKyc = async (accountId: AccountId): Promise => { + baseLogger.info({ accountId, operation: "initiateKyc" }, "Bridge operation started") + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + let customerId = account.bridgeCustomerId + + // Create customer if not exists + if (!customerId) { + // For now, create with minimal data - in production, gather from account profile + const customer = await BridgeClient.createCustomer({ + type: "individual", + first_name: account.username || "Flash", + last_name: "User", + email: `${account.id}@flash.app`, // Placeholder - should use real email + }) + + customerId = toBridgeCustomerId(customer.id) + + // Store customer ID + const updateResult = await AccountsRepository().updateBridgeFields(accountId, { + bridgeCustomerId: customerId, + bridgeKycStatus: "pending", + }) + if (updateResult instanceof Error) return updateResult + } + + // Create KYC link + const kycLink = await BridgeClient.createKycLink(customerId) + + const result: InitiateKycResult = { + kycLink: kycLink.kyc_link, + tosLink: kycLink.tos_link, + } + + baseLogger.info( + { accountId, operation: "initiateKyc", customerId }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "initiateKyc", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Creates a virtual account for receiving USD deposits + * - Requires approved KYC + * - Creates IBEX Tron USDT receive address + * - Creates Bridge virtual account pointing to Tron address + */ +const createVirtualAccount = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "createVirtualAccount" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + // Check KYC status + if (account.bridgeKycStatus === "pending") { + return new BridgeKycPendingError() + } + if (account.bridgeKycStatus === "rejected") { + return new BridgeKycRejectedError() + } + if (account.bridgeKycStatus !== "approved") { + return new BridgeKycPendingError("KYC not yet completed") + } + + // Get or create Tron address + let tronAddress = account.bridgeTronAddress + + if (!tronAddress) { + // TODO: Integrate with IBEX to create Tron USDT receive address + // For now, this is a placeholder - actual implementation requires: + // 1. Call Ibex.getCryptoReceiveOptions() to get Tron USDT option + // 2. Call Ibex.createCryptoReceiveInfo() to get Tron address + // This will be implemented when IBEX crypto receive methods are available + return new Error("IBEX Tron address creation not yet implemented") + } + + // Create Bridge virtual account + const virtualAccount = await BridgeClient.createVirtualAccount(customerId, { + source: { currency: "usd" }, + destination: { + currency: "usdt", + payment_rail: "tron", + address: tronAddress, + }, + }) + + // Store virtual account in repository + const repoResult = await BridgeAccountsRepo.createVirtualAccount({ + accountId: accountId as string, + bridgeVirtualAccountId: virtualAccount.id, + bankName: virtualAccount.source_deposit_instructions.bank_name || "", + routingNumber: virtualAccount.source_deposit_instructions.bank_routing_number || "", + accountNumberLast4: + virtualAccount.source_deposit_instructions.bank_account_number?.slice(-4) || "", + }) + if (repoResult instanceof Error) return repoResult + + const result: CreateVirtualAccountResult = { + virtualAccountId: virtualAccount.id, + bankName: virtualAccount.source_deposit_instructions.bank_name || "", + routingNumber: virtualAccount.source_deposit_instructions.bank_routing_number || "", + accountNumberLast4: + virtualAccount.source_deposit_instructions.bank_account_number?.slice(-4) || "", + } + + baseLogger.info( + { + accountId, + operation: "createVirtualAccount", + virtualAccountId: virtualAccount.id, + }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "createVirtualAccount", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns Bridge hosted bank linking URL for adding external accounts + */ +const addExternalAccount = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "addExternalAccount" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const linkUrl = await BridgeClient.getExternalAccountLinkUrl(customerId) + + const result: AddExternalAccountResult = { + linkUrl: linkUrl.link_url, + expiresAt: linkUrl.expires_at, + } + + baseLogger.info( + { accountId, operation: "addExternalAccount" }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "addExternalAccount", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Initiates a withdrawal from USDT to USD bank account + * - Orchestrates IBEX → Bridge transfer + */ +const initiateWithdrawal = async ( + accountId: AccountId, + amount: string, + externalAccountId: string, +): Promise => { + baseLogger.info( + { accountId, amount, externalAccountId, operation: "initiateWithdrawal" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const customerId = account.bridgeCustomerId + if (!customerId) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const tronAddress = account.bridgeTronAddress + if (!tronAddress) { + return new Error("Account has no Tron address. Create virtual account first.") + } + + // Verify external account exists + const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( + accountId as string, + ) + if (externalAccounts instanceof Error) return externalAccounts + + const targetAccount = externalAccounts.find( + (acc) => acc.bridgeExternalAccountId === externalAccountId, + ) + if (!targetAccount) { + return new Error("External account not found") + } + if (targetAccount.status !== "verified") { + return new Error("External account is not verified") + } + + // Create transfer via Bridge + const transfer = await BridgeClient.createTransfer(customerId, { + amount, + on_behalf_of: customerId, + source: { + payment_rail: "tron", + currency: "usdt", + from_address: tronAddress, + }, + destination: { + payment_rail: "ach", + currency: "usd", + external_account_id: externalAccountId, + }, + }) + + // Store withdrawal record + const withdrawalResult = await BridgeAccountsRepo.createWithdrawal({ + accountId: accountId as string, + bridgeTransferId: transfer.id, + amount: transfer.amount, + currency: transfer.currency, + externalAccountId, + status: "pending", + }) + if (withdrawalResult instanceof Error) return withdrawalResult + + const result: InitiateWithdrawalResult = { + transferId: transfer.id, + amount: transfer.amount, + currency: transfer.currency, + state: transfer.state, + } + + baseLogger.info( + { accountId, operation: "initiateWithdrawal", transferId: transfer.id }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "initiateWithdrawal", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns KYC status for an account + */ +const getKycStatus = async (accountId: AccountId): Promise => { + baseLogger.info({ accountId, operation: "getKycStatus" }, "Bridge operation started") + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + const result = account.bridgeKycStatus || null + + baseLogger.info( + { accountId, operation: "getKycStatus", status: result }, + "Bridge operation completed", + ) + + return result +} + +/** + * Returns virtual account details for an account + */ +const getVirtualAccount = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "getVirtualAccount" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const virtualAccount = await BridgeAccountsRepo.findVirtualAccountByAccountId( + accountId as string, + ) + + // Repository returns RepositoryError if not found + if (virtualAccount instanceof RepositoryError) { + baseLogger.info( + { accountId, operation: "getVirtualAccount", result: null }, + "Bridge operation completed - no virtual account", + ) + return null + } + + const result: VirtualAccountResult = { + bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId, + bankName: virtualAccount.bankName, + routingNumber: virtualAccount.routingNumber, + accountNumberLast4: virtualAccount.accountNumberLast4, + } + + baseLogger.info( + { + accountId, + operation: "getVirtualAccount", + virtualAccountId: result.bridgeVirtualAccountId, + }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "getVirtualAccount", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns list of linked external bank accounts + */ +const getExternalAccounts = async ( + accountId: AccountId, +): Promise => { + baseLogger.info( + { accountId, operation: "getExternalAccounts" }, + "Bridge operation started", + ) + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( + accountId as string, + ) + if (externalAccounts instanceof Error) return externalAccounts + + const result: ExternalAccountResult[] = externalAccounts.map((acc) => ({ + bridgeExternalAccountId: acc.bridgeExternalAccountId, + bankName: acc.bankName, + accountNumberLast4: acc.accountNumberLast4, + status: acc.status as "pending" | "verified" | "failed", + })) + + baseLogger.info( + { accountId, operation: "getExternalAccounts", count: result.length }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "getExternalAccounts", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +/** + * Returns list of withdrawals + */ +const getWithdrawals = async ( + accountId: AccountId, +): Promise => { + baseLogger.info({ accountId, operation: "getWithdrawals" }, "Bridge operation started") + + const enabledCheck = checkBridgeEnabled() + if (enabledCheck instanceof Error) return enabledCheck + + const account = await checkAccountLevel(accountId) + if (account instanceof Error) return account + + try { + const withdrawals = await BridgeAccountsRepo.findWithdrawalsByAccountId( + accountId as string, + ) + if (withdrawals instanceof Error) return withdrawals + + const result: WithdrawalResult[] = withdrawals.map((w) => ({ + transferId: w.bridgeTransferId, + amount: w.amount, + currency: w.currency, + state: w.status, + createdAt: w.createdAt.toISOString(), + })) + + baseLogger.info( + { accountId, operation: "getWithdrawals", count: result.length }, + "Bridge operation completed", + ) + + return result + } catch (error) { + baseLogger.error( + { accountId, operation: "getWithdrawals", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } +} + +// ============ Export with Tracing ============ + +export default wrapAsyncFunctionsToRunInSpan({ + namespace: "services.bridge", + fns: { + initiateKyc, + createVirtualAccount, + addExternalAccount, + initiateWithdrawal, + getKycStatus, + getVirtualAccount, + getExternalAccounts, + getWithdrawals, + }, +}) diff --git a/src/services/bridge/index.types.d.ts b/src/services/bridge/index.types.d.ts new file mode 100644 index 000000000..430e4b601 --- /dev/null +++ b/src/services/bridge/index.types.d.ts @@ -0,0 +1,121 @@ +import { + BridgeCustomerId, + BridgeVirtualAccountId, + BridgeExternalAccountId, + BridgeTransferId, +} from "@domain/primitives/bridge" + +// Bridge API Service Types - Response models from Bridge API + +interface BridgeCustomer { + readonly id: BridgeCustomerId + readonly externalId?: string + readonly email?: string + readonly name?: string + readonly createdAt: string + readonly updatedAt: string +} + +interface BridgeVirtualAccount { + readonly id: BridgeVirtualAccountId + readonly customerId: BridgeCustomerId + readonly accountNumber: string + readonly routingNumber: string + readonly bankName: string + readonly currency: string + readonly status: "active" | "inactive" | "closed" + readonly createdAt: string + readonly updatedAt: string +} + +interface BridgeExternalAccount { + readonly id: BridgeExternalAccountId + readonly customerId: BridgeCustomerId + readonly accountNumber: string + readonly routingNumber: string + readonly accountHolderName: string + readonly bankName: string + readonly accountType: "checking" | "savings" + readonly status: "pending" | "verified" | "failed" + readonly createdAt: string + readonly updatedAt: string +} + +interface BridgeTransfer { + readonly id: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly sourceAccountId: BridgeVirtualAccountId | BridgeExternalAccountId + readonly destinationAccountId: BridgeVirtualAccountId | BridgeExternalAccountId + readonly amount: number + readonly currency: string + readonly status: "pending" | "processing" | "completed" | "failed" | "cancelled" + readonly description?: string + readonly createdAt: string + readonly updatedAt: string + readonly completedAt?: string +} + +// Webhook Event Types + +interface BridgeWebhookEvent { + readonly id: string + readonly type: string + readonly timestamp: string + readonly data: Record +} + +interface BridgeKycApprovedEvent extends BridgeWebhookEvent { + readonly type: "kyc.approved" + readonly data: { + readonly customerId: BridgeCustomerId + readonly approvedAt: string + } +} + +interface BridgeKycRejectedEvent extends BridgeWebhookEvent { + readonly type: "kyc.rejected" + readonly data: { + readonly customerId: BridgeCustomerId + readonly reason: string + readonly rejectedAt: string + } +} + +interface BridgeDepositCompletedEvent extends BridgeWebhookEvent { + readonly type: "deposit.completed" + readonly data: { + readonly transferId: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly amount: number + readonly currency: string + readonly completedAt: string + } +} + +interface BridgeTransferCompletedEvent extends BridgeWebhookEvent { + readonly type: "transfer.completed" + readonly data: { + readonly transferId: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly amount: number + readonly currency: string + readonly completedAt: string + } +} + +interface BridgeTransferFailedEvent extends BridgeWebhookEvent { + readonly type: "transfer.failed" + readonly data: { + readonly transferId: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly reason: string + readonly failedAt: string + } +} + +type BridgeWebhookEventType = + | BridgeKycApprovedEvent + | BridgeKycRejectedEvent + | BridgeDepositCompletedEvent + | BridgeTransferCompletedEvent + | BridgeTransferFailedEvent diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts new file mode 100644 index 000000000..79b8778a5 --- /dev/null +++ b/src/services/bridge/webhook-server/index.ts @@ -0,0 +1,44 @@ +/** + * Bridge Webhook Server + * Standalone Express server for handling Bridge.xyz webhook events + * + * Runs on port configured in BridgeConfig.webhook.port (default: 4009) + * Routes: /kyc, /deposit, /transfer + */ + +import express from "express" +import { BridgeConfig } from "@config" +import { baseLogger } from "@services/logger" +import { verifyBridgeSignature } from "./middleware/verify-signature" +import { kycHandler } from "./routes/kyc" +import { depositHandler } from "./routes/deposit" +import { transferHandler } from "./routes/transfer" + +export const startBridgeWebhookServer = () => { + const app = express() + + // Middleware - MUST capture raw body for signature verification + app.use( + express.json({ + verify: (req: any, res, buf) => { + req.rawBody = buf.toString("utf8") + }, + }), + ) + + // Health check + app.get("/health", (req, res) => { + res.status(200).json({ status: "ok", service: "bridge-webhook" }) + }) + + // Webhook routes with signature verification + app.post("/kyc", verifyBridgeSignature("kyc"), kycHandler) + app.post("/deposit", verifyBridgeSignature("deposit"), depositHandler) + app.post("/transfer", verifyBridgeSignature("transfer"), transferHandler) + + // Start server + const port = BridgeConfig.webhook.port + app.listen(port, () => { + baseLogger.info({ port }, "Bridge webhook server started") + }) +} diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts new file mode 100644 index 000000000..b00961288 --- /dev/null +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -0,0 +1,67 @@ +/** + * Bridge Webhook Signature Verification Middleware + * + * Bridge uses asymmetric signature verification (RSA-SHA256), not HMAC. + * Header format: X-Webhook-Signature: t=,v0= + * Signature is computed over: . + */ + +import { Request, Response, NextFunction } from "express" +import crypto from "crypto" +import { BridgeConfig } from "@config" +import { baseLogger } from "@services/logger" + +export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer") => { + return (req: Request, res: Response, next: NextFunction) => { + const signature = req.headers["x-webhook-signature"] as string + + if (!signature) { + baseLogger.warn("Missing Bridge webhook signature") + return res.status(401).json({ error: "Missing signature" }) + } + + // Parse signature header: t=,v0= + const parts = signature.split(",") + const timestampPart = parts.find((p) => p.startsWith("t=")) + const signaturePart = parts.find((p) => p.startsWith("v0=")) + + if (!timestampPart || !signaturePart) { + baseLogger.warn("Invalid signature format") + return res.status(401).json({ error: "Invalid signature format" }) + } + + const timestamp = timestampPart.split("=")[1] + const sig = signaturePart.split("=")[1] + + // Check timestamp skew (default 5 minutes) + const now = Date.now() + const timestampMs = parseInt(timestamp, 10) + const skew = Math.abs(now - timestampMs) + + if (skew > BridgeConfig.webhook.timestampSkewMs) { + baseLogger.warn({ skew }, "Webhook timestamp too old") + return res.status(401).json({ error: "Timestamp too old" }) + } + + // Verify signature using Bridge public key + const publicKey = BridgeConfig.webhook.publicKeys[publicKeyType] + const rawBody = (req as any).rawBody || JSON.stringify(req.body) + const payload = `${timestamp}.${rawBody}` + + try { + const verifier = crypto.createVerify("RSA-SHA256") + verifier.update(payload) + const isValid = verifier.verify(publicKey, sig, "base64") + + if (!isValid) { + baseLogger.warn("Invalid Bridge webhook signature") + return res.status(401).json({ error: "Invalid signature" }) + } + + next() + } catch (error) { + baseLogger.error({ error }, "Error verifying Bridge webhook signature") + return res.status(500).json({ error: "Signature verification failed" }) + } + } +} diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts new file mode 100644 index 000000000..79d64253a --- /dev/null +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -0,0 +1,49 @@ +/** + * Bridge Deposit Webhook Handler + * Handles deposit.completed events from Bridge.xyz + * + * NOTE: This handler only logs the deposit event. + * The actual balance crediting happens when IBEX sends its crypto.received webhook. + */ + +import { Request, Response } from "express" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" + +export const depositHandler = async (req: Request, res: Response) => { + const { event, data } = req.body + const { transfer_id, amount, currency, tx_hash, customer_id } = data + + if (!transfer_id || !event) { + return res.status(400).json({ error: "Invalid payload" }) + } + + // Idempotency check using transfer_id as lock key + const lockKey = `bridge-deposit:${transfer_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + if (lockResult instanceof Error) { + baseLogger.info({ transfer_id }, "Duplicate Bridge deposit webhook") + return res.status(200).json({ status: "already_processed" }) + } + + try { + // Log deposit event + // The actual balance crediting happens via IBEX crypto webhook + baseLogger.info( + { + transfer_id, + amount, + currency, + tx_hash, + customer_id, + event, + }, + "Bridge deposit completed", + ) + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, transfer_id }, "Error processing Bridge deposit webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts new file mode 100644 index 000000000..b69dfda08 --- /dev/null +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -0,0 +1,80 @@ +/** + * Bridge KYC Webhook Handler + * Handles kyc.approved and kyc.rejected events from Bridge.xyz + */ + +import { Request, Response } from "express" +import { AccountsRepository } from "@services/mongoose/accounts" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" +import { toBridgeCustomerId } from "@domain/primitives/bridge" + +export const kycHandler = async (req: Request, res: Response) => { + const { event, data } = req.body + const { customer_id, kyc_status, reason } = data + + if (!customer_id || !event) { + return res.status(400).json({ error: "Invalid payload" }) + } + + // Idempotency check using customer_id + event as lock key + const lockKey = `bridge-kyc:${customer_id}:${event}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + if (lockResult instanceof Error) { + baseLogger.info({ customer_id, event }, "Duplicate Bridge KYC webhook") + return res.status(200).json({ status: "already_processed" }) + } + + try { + const bridgeCustomerId = toBridgeCustomerId(customer_id) + const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) + + if (account instanceof Error) { + baseLogger.error({ customer_id }, "Account not found for Bridge customer") + return res.status(404).json({ error: "Account not found" }) + } + + // Update KYC status based on event + if (event === "kyc.approved") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "approved", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info({ accountId: account.id, customer_id }, "Bridge KYC approved") + } else if (event === "kyc.rejected") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "rejected", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.warn( + { + accountId: account.id, + customer_id, + reason, + }, + "Bridge KYC rejected", + ) + } + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, customer_id }, "Error processing Bridge KYC webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts new file mode 100644 index 000000000..e08b8fa10 --- /dev/null +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -0,0 +1,88 @@ +/** + * Bridge Transfer Webhook Handler + * Handles transfer.completed and transfer.failed events from Bridge.xyz + */ + +import { Request, Response } from "express" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" +import { toBridgeTransferId } from "@domain/primitives/bridge" + +export const transferHandler = async (req: Request, res: Response) => { + const { event, data } = req.body + const { transfer_id, state, amount, currency } = data + + if (!transfer_id || !event) { + return res.status(400).json({ error: "Invalid payload" }) + } + + // Idempotency check using transfer_id as lock key + const lockKey = `bridge-transfer:${transfer_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + if (lockResult instanceof Error) { + baseLogger.info({ transfer_id }, "Duplicate Bridge transfer webhook") + return res.status(200).json({ status: "already_processed" }) + } + + try { + const bridgeTransferId = toBridgeTransferId(transfer_id) + + // Update withdrawal status based on event + if (event === "transfer.completed") { + const result = await BridgeAccountsRepo.updateWithdrawalStatus( + bridgeTransferId, + "completed", + ) + + if (result instanceof Error) { + baseLogger.error( + { transfer_id, error: result }, + "Failed to update withdrawal status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info( + { + transfer_id, + amount, + currency, + }, + "Bridge transfer completed", + ) + + // TODO: Send push notification to user + } else if (event === "transfer.failed") { + const result = await BridgeAccountsRepo.updateWithdrawalStatus( + bridgeTransferId, + "failed", + ) + + if (result instanceof Error) { + baseLogger.error( + { transfer_id, error: result }, + "Failed to update withdrawal status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.warn( + { + transfer_id, + state, + amount, + currency, + }, + "Bridge transfer failed", + ) + + // TODO: Send push notification to user + } + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, transfer_id }, "Error processing Bridge transfer webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 9f5a94d2c..5fc2d723c 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -1,105 +1,168 @@ import IbexClient, { GetFeeEstimateResponse200, IbexClientError } from "ibex-client" import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" -import { IbexConfig } from "@config"; -import { AddInvoiceBodyParam, AddInvoiceResponse201, CreateAccountResponse201, CreateLnurlPayBodyParam, CreateLnurlPayResponse201, DecodeLnurlMetadataParam, DecodeLnurlResponse200, EstimateFeeCopyMetadataParam, EstimateFeeCopyResponse200, GenerateBitcoinAddressBodyParam, GenerateBitcoinAddressResponse201, GetAccountDetailsMetadataParam, GetAccountDetailsResponse200, GetFeeEstimationMetadataParam, GetFeeEstimationResponse200, GetTransactionDetails1MetadataParam, GetTransactionDetails1Response200, GMetadataParam, GResponse200, InvoiceFromHashMetadataParam, InvoiceFromHashResponse200, PayInvoiceV2BodyParam, PayInvoiceV2Response200, PayToALnurlPayBodyParam, PayToALnurlPayResponse201, SendToAddressCopyBodyParam, SendToAddressCopyResponse200 } from "ibex-client"; -import { addAttributesToCurrentSpan, wrapAsyncFunctionsToRunInSpan } from "@services/tracing"; -import WebhookServer from "./webhook-server"; -import { Redis } from "./cache" -import { GetFeeEstimateArgs, IbexAccountDetails, IbexFeeEstimation, IbexInvoiceArgs, PayInvoiceArgs, SendOnchainArgs } from "./types"; -import { USDAmount } from "@domain/shared"; +import { IbexConfig } from "@config" +import { + AddInvoiceBodyParam, + AddInvoiceResponse201, + CreateAccountResponse201, + CreateLnurlPayBodyParam, + CreateLnurlPayResponse201, + DecodeLnurlMetadataParam, + DecodeLnurlResponse200, + EstimateFeeCopyMetadataParam, + EstimateFeeCopyResponse200, + GenerateBitcoinAddressBodyParam, + GenerateBitcoinAddressResponse201, + GetAccountDetailsMetadataParam, + GetAccountDetailsResponse200, + GetFeeEstimationMetadataParam, + GetFeeEstimationResponse200, + GetTransactionDetails1MetadataParam, + GetTransactionDetails1Response200, + GMetadataParam, + GResponse200, + InvoiceFromHashMetadataParam, + InvoiceFromHashResponse200, + PayInvoiceV2BodyParam, + PayInvoiceV2Response200, + PayToALnurlPayBodyParam, + PayToALnurlPayResponse201, + SendToAddressCopyBodyParam, + SendToAddressCopyResponse200, +} from "ibex-client" +import { + addAttributesToCurrentSpan, + wrapAsyncFunctionsToRunInSpan, +} from "@services/tracing" +import WebhookServer from "./webhook-server" +import { Redis } from "./cache" +import { + GetFeeEstimateArgs, + IbexAccountDetails, + IbexFeeEstimation, + IbexInvoiceArgs, + PayInvoiceArgs, + CryptoReceiveOption, + CryptoReceiveInfo, + CreateCryptoReceiveInfoRequest, +} from "./types" +import { USDAmount, USDTAmount } from "@domain/shared" +import { baseLogger } from "@services/logger" const Ibex = new IbexClient( - IbexConfig.url, - { email: IbexConfig.email, password: IbexConfig.password }, - Redis + IbexConfig.url, + { email: IbexConfig.email, password: IbexConfig.password }, + Redis, ) -const createAccount = async (name: string, currencyId: IbexCurrencyId): Promise => { +const createAccount = async ( + name: string, + currencyId: IbexCurrencyId, +): Promise => { return Ibex.createAccount({ name, currencyId }).then(errorHandler) } -const getAccountDetails = async (accountId: IbexAccountId): Promise => { +const getAccountDetails = async ( + accountId: IbexAccountId, +): Promise => { return Ibex.getAccountDetails({ accountId }) - .then(r => { + .then((r) => { if (r instanceof Error) return r else { - let balance = r.balance !== undefined ? USDAmount.dollars(r.balance.toString()) : undefined + let balance = + r.balance !== undefined ? USDAmount.dollars(r.balance.toString()) : undefined if (balance instanceof Error) balance = undefined return { id: r.id, userId: r.userId, name: r.name, - balance + balance, } } }) .then(errorHandler) } -const getAccountTransactions = async (params: GMetadataParam): Promise => { +const getAccountTransactions = async ( + params: GMetadataParam, +): Promise => { addAttributesToCurrentSpan({ "request.params": JSON.stringify(params) }) return Ibex.getAccountTransactions(params).then(errorHandler) } -const addInvoice = async (args: IbexInvoiceArgs): Promise => { - const body = { - ...args, - amount: args.amount?.toIbex(), - webhookUrl: WebhookServer.endpoints.onReceive.invoice, - webhookSecret: WebhookServer.secret, +const addInvoice = async ( + args: IbexInvoiceArgs, +): Promise => { + const body = { + ...args, + amount: args.amount?.toIbex(), + webhookUrl: WebhookServer.endpoints.onReceive.invoice, + webhookSecret: WebhookServer.secret, } as AddInvoiceBodyParam addAttributesToCurrentSpan({ "request.params": JSON.stringify(body) }) return Ibex.addInvoice(body).then(errorHandler) } -const getTransactionDetails = async (id: IbexTransactionId): Promise => { +const getTransactionDetails = async ( + id: IbexTransactionId, +): Promise => { return Ibex.getTransactionDetails({ transaction_id: id }).then(errorHandler) } -const generateBitcoinAddress = async (accountId: IbexAccountId): Promise => { +const generateBitcoinAddress = async ( + accountId: IbexAccountId, +): Promise => { return Ibex.generateBitcoinAddress({ accountId, webhookUrl: WebhookServer.endpoints.onReceive.onchain, - webhookSecret: WebhookServer.secret, + webhookSecret: WebhookServer.secret, }).then(errorHandler) } -const invoiceFromHash = async (invoice_hash: PaymentHash): Promise => { +const invoiceFromHash = async ( + invoice_hash: PaymentHash, +): Promise => { return Ibex.invoiceFromHash({ invoice_hash }).then(errorHandler) } // Only supports USD for now -const getLnFeeEstimation = async (args: GetFeeEstimateArgs): Promise => { +const getLnFeeEstimation = async ( + args: GetFeeEstimateArgs, +): Promise => { const currencyId = USDAmount.currencyId - // const amount = (args.send instanceof IbexCurrency) ? args.send.amount.toString() : undefined - + // const amount = (args.send instanceof IbexCurrency) ? args.send.amount.toString() : undefined + const resp = await Ibex.getFeeEstimation({ bolt11: args.invoice as string, - amount: args.send?.asDollars(8), + amount: args.send?.asDollars(8), currencyId: currencyId.toString(), }) if (resp instanceof Error) return new IbexError(resp) - else if (resp.amount === null || resp.amount === undefined) return new UnexpectedIbexResponse("Fee not found.") - else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) return new UnexpectedIbexResponse("invoiceAmount not found.") + else if (resp.amount === null || resp.amount === undefined) + return new UnexpectedIbexResponse("Fee not found.") + else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) + return new UnexpectedIbexResponse("invoiceAmount not found.") else { let fee = USDAmount.dollars(resp.amount) if (fee instanceof Error) return new ParseError(fee) let invoiceAmount = USDAmount.dollars(resp.invoiceAmount) if (invoiceAmount instanceof Error) return new ParseError(invoiceAmount) - return { - fee, + return { + fee, invoice: invoiceAmount, } } } -const payInvoice = async (args: PayInvoiceArgs): Promise => { - const bodyWithHooks = { - accountId: args.accountId, - bolt11: args.invoice, - amount: args.send?.toIbex(), - webhookUrl: WebhookServer.endpoints.onPay.invoice, - webhookSecret: WebhookServer.secret, +const payInvoice = async ( + args: PayInvoiceArgs, +): Promise => { + const bodyWithHooks = { + accountId: args.accountId, + bolt11: args.invoice, + amount: args.send?.toIbex(), + webhookUrl: WebhookServer.endpoints.onPay.invoice, + webhookSecret: WebhookServer.secret, } as PayInvoiceV2BodyParam addAttributesToCurrentSpan({ "request.params": JSON.stringify(bodyWithHooks) }) return Ibex.payInvoiceV2(bodyWithHooks).then(errorHandler) @@ -107,57 +170,125 @@ const payInvoice = async (args: PayInvoiceArgs): Promise => { - const body = { - accountId: args.accountId, - address: args.address, - amount: args.amount.toIbex(), - webhookUrl: WebhookServer.endpoints.onPay.onchain, - webhookSecret: WebhookServer.secret, - } as SendToAddressCopyBodyParam - addAttributesToCurrentSpan({ "request.params": JSON.stringify(body) }) - return Ibex.sendToAddressV2(body).then(errorHandler) -} - -const estimateOnchainFee = async (send: USDAmount, address: OnChainAddress): Promise => { - return Ibex.estimateFeeV2({ - amount: send.toIbex(), - "currency-id": USDAmount.currencyId.toString(), - address +const sendOnchain = async ( + body: SendToAddressCopyBodyParam, +): Promise => { + const bodyWithHooks = { + ...body, + webhookUrl: WebhookServer.endpoints.onPay.onchain, + webhookSecret: WebhookServer.secret, + } as SendToAddressCopyBodyParam + addAttributesToCurrentSpan({ "request.params": JSON.stringify(bodyWithHooks) }) + return Ibex.sendToAddressV2(bodyWithHooks).then(errorHandler) +} + +const estimateOnchainFee = async ( + send: USDAmount, + address: OnChainAddress, +): Promise => { + return Ibex.estimateFeeV2({ + "amount": send.toIbex(), + "currency-id": USDAmount.currencyId.toString(), + address, }).then(errorHandler) } - -const createLnurlPay = async (body: CreateLnurlPayBodyParam): Promise => { - const bodyWithHooks = { - ...body, - webhookUrl: WebhookServer.endpoints.onReceive.lnurl, - webhookSecret: WebhookServer.secret, + +const createLnurlPay = async ( + body: CreateLnurlPayBodyParam, +): Promise => { + const bodyWithHooks = { + ...body, + webhookUrl: WebhookServer.endpoints.onReceive.lnurl, + webhookSecret: WebhookServer.secret, } as CreateLnurlPayBodyParam addAttributesToCurrentSpan({ "request.params": JSON.stringify(bodyWithHooks) }) return Ibex.createLnurlPay(bodyWithHooks).then(errorHandler) } -const decodeLnurl = async (lnurl: DecodeLnurlMetadataParam): Promise => { +const decodeLnurl = async ( + lnurl: DecodeLnurlMetadataParam, +): Promise => { return Ibex.decodeLnurl(lnurl).then(errorHandler) } - -const payToLnurl = async (args: PayLnurlArgs): Promise => { + +const payToLnurl = async ( + args: PayLnurlArgs, +): Promise => { return Ibex.payToLnurl({ accountId: args.accountId, amount: args.send.amount, params: args.params, webhookUrl: WebhookServer.endpoints.onPay.lnurl, - webhookSecret: WebhookServer.secret, + webhookSecret: WebhookServer.secret, }).then(errorHandler) } +const getCryptoReceiveBalance = async ( + receiveInfoId: string, +): Promise => { + try { + const resp = await (Ibex as any).getCryptoReceiveBalance({ receiveInfoId }) + if (resp instanceof Error) return new IbexError(resp) + if (resp.balance === null || resp.balance === undefined) + return new UnexpectedIbexResponse("Balance not found") + const balance = USDTAmount.smallestUnits(resp.balance.toString()) + if (balance instanceof Error) return new IbexError(balance) + return balance + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} + +const getCryptoReceiveOptions = async (): Promise => { + try { + const resp = await (Ibex as any).getCryptoReceiveOptions() + if (resp instanceof Error) return new IbexError(resp) + return resp.options || [] + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} + +const createCryptoReceiveInfo = async ( + walletId: IbexAccountId, + optionId: string, +): Promise => { + try { + const resp = await (Ibex as any).createCryptoReceiveInfo({ + wallet_id: walletId, + option_id: optionId, + } as CreateCryptoReceiveInfoRequest) + if (resp instanceof Error) return new IbexError(resp) + if (!resp.address) return new UnexpectedIbexResponse("Address not found") + return resp + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} + +const getTronUsdtOption = async (): Promise => { + const options = await getCryptoReceiveOptions() + if (options instanceof IbexError) return options + + const tronUsdt = options.find( + (opt) => + opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "tron", + ) + + if (!tronUsdt) { + return new IbexError(new Error("Tron USDT option not found")) + } + + return tronUsdt.id +} + // const sendBetweenAccounts = async ( -// sender: IbexAccount, -// receiver: IbexAccount, +// sender: IbexAccount, +// receiver: IbexAccount, // transfer: USDollars, // memo: string = "Flash-to-Flash" // ): Promise => { -// const invoiceResp = await addInvoice({ +// const invoiceResp = await addInvoice({ // accountId: receiver.id, // memo, // amount: transfer, // convert cents to dollars for Ibex api @@ -173,20 +304,24 @@ const payToLnurl = async (args: PayLnurlArgs): Promise { const app = express() - // Middleware to parse JSON requests app.use(express.json()) - // Routes app.get("/health", (_: Request, resp: Response) => resp.send("Ibex server is running")) app.use(onReceive.router) app.use(onPay.router) + app.use(cryptoReceive.router) app.listen(IbexConfig.webhook.port, () => logger.info( `Listening for ibex events on port ${IbexConfig.webhook.port}. Can be reached at ${IbexConfig.webhook.uri}`, @@ -35,6 +34,9 @@ export default { lnurl: IbexConfig.webhook.uri + onPay.paths.lnurl, onchain: IbexConfig.webhook.uri + onPay.paths.onchain, }, + cryptoReceive: { + cryptoReceive: IbexConfig.webhook.uri + cryptoReceive.paths.cryptoReceive, + }, }, secret: IbexConfig.webhook.secret, } diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts new file mode 100644 index 000000000..7fe2283a5 --- /dev/null +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -0,0 +1,104 @@ +import express, { Request, Response } from "express" +import { AccountsRepository } from "@services/mongoose/accounts" +import { listWalletsByAccountId } from "@app/wallets" +import { WalletCurrency, USDTAmount } from "@domain/shared" +import { baseLogger } from "@services/logger" +import { LockService } from "@services/lock" +import { authenticate, logRequest } from "../middleware" + +const paths = { + cryptoReceive: "/crypto/receive", +} + +const router = express.Router() + +interface CryptoReceiveResult { + status: "success" | "error" + code?: string +} + +const cryptoReceiveHandler = async (req: Request, res: Response) => { + const { tx_hash, address, amount, currency, network } = req.body + + if (!tx_hash || !address || !amount || currency !== "USDT" || network !== "tron") { + baseLogger.warn( + { tx_hash, address, amount, currency, network }, + "Invalid crypto receive payload", + ) + return res.status(400).json({ error: "Invalid payload" }) + } + + const lockResult = await LockService().lockPaymentHash(tx_hash as any, async () => { + try { + const account = await AccountsRepository().findByBridgeTronAddress(address) + if (account instanceof Error) { + baseLogger.error({ address, tx_hash }, "Account not found for Tron address") + return { status: "error", code: "account_not_found" } as CryptoReceiveResult + } + + const wallets = await listWalletsByAccountId(account.id) + if (wallets instanceof Error) { + baseLogger.error( + { accountId: account.id, error: wallets }, + "Failed to list wallets", + ) + return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult + } + + const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) + if (!usdtWallet) { + baseLogger.error({ accountId: account.id }, "USDT wallet not found") + return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult + } + + const usdtAmount = USDTAmount.fromNumber(amount) + if (usdtAmount instanceof Error) { + baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") + return { status: "error", code: "invalid_amount" } as CryptoReceiveResult + } + + baseLogger.info( + { + accountId: account.id, + walletId: usdtWallet.id, + amount: usdtAmount.asNumber(), + tx_hash, + address, + }, + "USDT deposit received", + ) + + return { status: "success" } as CryptoReceiveResult + } catch (error) { + baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") + return { status: "error", code: "internal_error" } as CryptoReceiveResult + } + }) + + if (lockResult instanceof Error) { + baseLogger.warn( + { tx_hash, error: lockResult }, + "Lock acquisition failed or duplicate webhook", + ) + return res.status(200).json({ status: "already_processed" }) + } + + if (lockResult.status === "success") { + return res.status(200).json({ status: "success" }) + } + + const statusMap: Record = { + account_not_found: 404, + wallet_list_failed: 500, + usdt_wallet_not_found: 404, + invalid_amount: 400, + internal_error: 500, + } + + const statusCode = statusMap[lockResult.code || ""] || 500 + return res.status(statusCode).json({ error: lockResult.code }) +} + +router.post(paths.cryptoReceive, authenticate, logRequest, cryptoReceiveHandler) + +export { paths, router } diff --git a/src/services/ibex/webhook-server/routes/index.ts b/src/services/ibex/webhook-server/routes/index.ts index e41b6e40b..cb1759853 100644 --- a/src/services/ibex/webhook-server/routes/index.ts +++ b/src/services/ibex/webhook-server/routes/index.ts @@ -1,2 +1,3 @@ export * as onPay from "./on-pay" -export * as onReceive from "./on-receive" \ No newline at end of file +export * as onReceive from "./on-receive" +export * as cryptoReceive from "./crypto-receive" diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 32d55ec4b..7e658daa6 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -173,6 +173,51 @@ export const AccountsRepository = (): IAccountsRepository => { } } + const updateBridgeFields = async ( + id: AccountId, + fields: { + bridgeCustomerId?: BridgeCustomerId + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string + }, + ): Promise => { + try { + const result = await Account.findByIdAndUpdate( + toObjectId(id), + { $set: fields }, + { new: true }, + ) + if (!result) return new RepositoryError("Account not found") + return translateToAccount(result) + } catch (error) { + return parseRepositoryError(error) + } + } + + const findByBridgeTronAddress = async ( + address: string, + ): Promise => { + try { + const result = await Account.findOne({ bridgeTronAddress: address }) + if (!result) return new RepositoryError("Account not found for Tron address") + return translateToAccount(result) + } catch (error) { + return parseRepositoryError(error) + } + } + + const findByBridgeCustomerId = async ( + customerId: BridgeCustomerId, + ): Promise => { + try { + const result = await Account.findOne({ bridgeCustomerId: customerId }) + if (!result) return new RepositoryError("Account not found for Bridge customer ID") + return translateToAccount(result) + } catch (error) { + return parseRepositoryError(error) + } + } + return { persistNew, findByUserId, @@ -182,6 +227,9 @@ export const AccountsRepository = (): IAccountsRepository => { findByUsername, findByNpub, update, + updateBridgeFields, + findByBridgeTronAddress, + findByBridgeCustomerId, } } @@ -243,4 +291,7 @@ const translateToAccount = (result: AccountRecord): Account => ({ kratosUserId: result.kratosUserId as UserId, displayCurrency: (result.displayCurrency || UsdDisplayCurrency) as DisplayCurrency, + bridgeCustomerId: result.bridgeCustomerId as BridgeCustomerId | undefined, + bridgeKycStatus: result.bridgeKycStatus, + bridgeTronAddress: result.bridgeTronAddress, }) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts new file mode 100644 index 000000000..caeb7867b --- /dev/null +++ b/src/services/mongoose/bridge-accounts.ts @@ -0,0 +1,138 @@ +import { BridgeVirtualAccount, BridgeExternalAccount, BridgeWithdrawal } from "./schema" +import { + BridgeVirtualAccountId, + BridgeExternalAccountId, + BridgeTransferId, +} from "@domain/primitives/bridge" +import { RepositoryError } from "@domain/errors" + +// ============ Virtual Accounts ============ + +export const createVirtualAccount = async (data: { + accountId: string + bridgeVirtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string +}) => { + try { + const record = await BridgeVirtualAccount.create(data) + return record + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findVirtualAccountByAccountId = async (accountId: string) => { + try { + const record = await BridgeVirtualAccount.findOne({ accountId }) + return record || new RepositoryError("Virtual account not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findVirtualAccountByBridgeId = async (bridgeId: BridgeVirtualAccountId) => { + try { + const record = await BridgeVirtualAccount.findOne({ + bridgeVirtualAccountId: bridgeId, + }) + return record || new RepositoryError("Virtual account not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +// ============ External Accounts ============ + +export const createExternalAccount = async (data: { + accountId: string + bridgeExternalAccountId: string + bankName: string + accountNumberLast4: string + status?: "pending" | "verified" | "failed" +}) => { + try { + const record = await BridgeExternalAccount.create(data) + return record + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findExternalAccountsByAccountId = async (accountId: string) => { + try { + const records = await BridgeExternalAccount.find({ accountId }) + return records + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const updateExternalAccountStatus = async ( + bridgeId: BridgeExternalAccountId, + status: "pending" | "verified" | "failed", +) => { + try { + const record = await BridgeExternalAccount.findOneAndUpdate( + { bridgeExternalAccountId: bridgeId }, + { status }, + { new: true }, + ) + return record || new RepositoryError("External account not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +// ============ Withdrawals ============ + +export const createWithdrawal = async (data: { + accountId: string + bridgeTransferId: string + amount: string + currency: string + externalAccountId: string + status?: "pending" | "completed" | "failed" +}) => { + try { + const record = await BridgeWithdrawal.create(data) + return record + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findWithdrawalsByAccountId = async (accountId: string) => { + try { + const records = await BridgeWithdrawal.find({ accountId }).sort({ createdAt: -1 }) + return records + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const updateWithdrawalStatus = async ( + bridgeTransferId: BridgeTransferId, + status: "pending" | "completed" | "failed", +) => { + try { + const record = await BridgeWithdrawal.findOneAndUpdate( + { bridgeTransferId }, + { status, updatedAt: new Date() }, + { new: true }, + ) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const findWithdrawalByBridgeTransferId = async (transferId: BridgeTransferId) => { + try { + const record = await BridgeWithdrawal.findOne({ bridgeTransferId: transferId }) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index 6f490709b..d13cccada 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -17,6 +17,36 @@ import { WalletRecord } from "./wallets" const Schema = mongoose.Schema +// Bridge Record Interfaces +interface IBridgeVirtualAccountRecord { + accountId: string + bridgeVirtualAccountId: string + bankName: string + routingNumber: string + accountNumberLast4: string + createdAt: Date +} + +interface IBridgeExternalAccountRecord { + accountId: string + bridgeExternalAccountId: string + bankName: string + accountNumberLast4: string + status: "pending" | "verified" | "failed" + createdAt: Date +} + +interface IBridgeWithdrawalRecord { + accountId: string + bridgeTransferId: string + amount: string + currency: string + status: "pending" | "completed" | "failed" + externalAccountId: string + createdAt: Date + updatedAt: Date +} + const dbMetadataSchema = new Schema({ routingFeeLastEntry: Date, // TODO: rename to routingRevenueLastEntry }) @@ -295,6 +325,20 @@ const AccountSchema = new Schema( }, displayCurrency: String, // FIXME: should be an enum + + bridgeCustomerId: { + type: String, + required: false, + }, + bridgeKycStatus: { + type: String, + enum: ["pending", "approved", "rejected"], + required: false, + }, + bridgeTronAddress: { + type: String, + required: false, + }, }, { id: false }, ) @@ -304,6 +348,8 @@ AccountSchema.index({ coordinates: 1, }) +AccountSchema.index({ bridgeTronAddress: 1 }, { sparse: true }) + export const Account = mongoose.model("Account", AccountSchema) const QuizSchema = new Schema({ @@ -566,4 +612,47 @@ export const WalletOnChainPendingReceive = "WalletOnChainPendingReceive", WalletOnChainPendingReceiveSchema, ) - \ No newline at end of file + +const BridgeVirtualAccountSchema = new Schema({ + accountId: { type: String, required: true, index: true }, + bridgeVirtualAccountId: { type: String, required: true, unique: true }, + bankName: { type: String, required: true }, + routingNumber: { type: String, required: true }, + accountNumberLast4: { type: String, required: true }, + createdAt: { type: Date, default: Date.now }, +}) + +const BridgeExternalAccountSchema = new Schema({ + accountId: { type: String, required: true, index: true }, + bridgeExternalAccountId: { type: String, required: true, unique: true }, + bankName: { type: String, required: true }, + accountNumberLast4: { type: String, required: true }, + status: { type: String, enum: ["pending", "verified", "failed"], default: "pending" }, + createdAt: { type: Date, default: Date.now }, +}) + +const BridgeWithdrawalSchema = new Schema({ + accountId: { type: String, required: true, index: true }, + bridgeTransferId: { type: String, required: true, unique: true }, + amount: { type: String, required: true }, + currency: { type: String, required: true }, + status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, + externalAccountId: { type: String, required: true }, + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now }, +}) + +export const BridgeVirtualAccount = mongoose.model( + "BridgeVirtualAccount", + BridgeVirtualAccountSchema, +) + +export const BridgeExternalAccount = mongoose.model( + "BridgeExternalAccount", + BridgeExternalAccountSchema, +) + +export const BridgeWithdrawal = mongoose.model( + "BridgeWithdrawal", + BridgeWithdrawalSchema, +) diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 1a9849006..57016ec0a 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -94,6 +94,11 @@ interface AccountRecord { title?: string coordinates?: CoordinateObjectForUser + // Bridge integration: + bridgeCustomerId?: string + bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeTronAddress?: string + // mongoose in-built functions save: () => Promise } diff --git a/yarn.lock b/yarn.lock index 1b016c30d..ec1990392 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,49 +4,49 @@ "@anvilco/apollo-server-plugin-introspection-metadata@^2.2.3": version "2.2.3" - resolved "https://registry.yarnpkg.com/@anvilco/apollo-server-plugin-introspection-metadata/-/apollo-server-plugin-introspection-metadata-2.2.3.tgz#c913cefa665e77cd620df19842c1ce492694439e" + resolved "https://registry.npmjs.org/@anvilco/apollo-server-plugin-introspection-metadata/-/apollo-server-plugin-introspection-metadata-2.2.3.tgz" integrity sha512-qHhaXN8UuFii8Y2X/i9QSquOEuGaRCFJLEexLB06nNWIpMCvLCQtEB3zAuR4ljyoq+b9D/9TxoJ2Hsn1qtZ5KQ== dependencies: lodash "^4.17.21" "@apidevtools/swagger-methods@^3.0.2": version "3.0.2" - resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + resolved "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz" integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== -"@apollo/client@3.8.3": - version "3.8.3" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.8.3.tgz#7cd23307bbc788a0a9eda51e6a76f32db8282933" - integrity sha512-mK86JM6hCpMEBGDgdO9U8ZYS8r9lPjXE1tVGpJMdSFUsIcXpmEfHUAbbFpPtYmxn8Qa7XsYy0dwDaDhpf4UUPw== +"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0": + version "3.13.9" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.13.9.tgz" + integrity sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" - "@wry/context" "^0.7.3" + "@wry/caches" "^1.0.0" "@wry/equality" "^0.5.6" - "@wry/trie" "^0.4.3" + "@wry/trie" "^0.5.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.17.5" + optimism "^0.18.0" prop-types "^15.7.2" - response-iterator "^0.2.6" + rehackt "^0.1.0" symbol-observable "^4.0.0" ts-invariant "^0.10.3" tslib "^2.3.0" zen-observable-ts "^1.2.5" -"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0": - version "3.13.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.13.9.tgz#311e0df1734aaa5598e01a292bb7982df49cc062" - integrity sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ== +"@apollo/client@3.8.3": + version "3.8.3" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.8.3.tgz" + integrity sha512-mK86JM6hCpMEBGDgdO9U8ZYS8r9lPjXE1tVGpJMdSFUsIcXpmEfHUAbbFpPtYmxn8Qa7XsYy0dwDaDhpf4UUPw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" - "@wry/caches" "^1.0.0" + "@wry/context" "^0.7.3" "@wry/equality" "^0.5.6" - "@wry/trie" "^0.5.0" + "@wry/trie" "^0.4.3" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.18.0" + optimism "^0.17.5" prop-types "^15.7.2" - rehackt "^0.1.0" + response-iterator "^0.2.6" symbol-observable "^4.0.0" ts-invariant "^0.10.3" tslib "^2.3.0" @@ -54,7 +54,7 @@ "@apollo/protobufjs@1.2.6": version "1.2.6" - resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.6.tgz#d601e65211e06ae1432bf5993a1a0105f2862f27" + resolved "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.6.tgz" integrity sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw== dependencies: "@protobufjs/aspromise" "^1.1.2" @@ -73,7 +73,7 @@ "@apollo/protobufjs@1.2.7": version "1.2.7" - resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.7.tgz#3a8675512817e4a046a897e5f4f16415f16a7d8a" + resolved "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz" integrity sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg== dependencies: "@protobufjs/aspromise" "^1.1.2" @@ -91,7 +91,7 @@ "@apollo/rover@^0.18.1": version "0.18.1" - resolved "https://registry.yarnpkg.com/@apollo/rover/-/rover-0.18.1.tgz#b490fa74cb0ccb1e49d3ff3715629d8eea03db0b" + resolved "https://registry.npmjs.org/@apollo/rover/-/rover-0.18.1.tgz" integrity sha512-uhbi97JW8yOQWzyqAUgNEBnmihGjeav6W1Gln4KGpOHqIph4WxieDIzUvvccHuT7lf7S/fY7eDtPkpKhhcgRnQ== dependencies: axios-proxy-builder "^0.1.1" @@ -101,19 +101,19 @@ "@apollo/usage-reporting-protobuf@^4.0.0": version "4.1.1" - resolved "https://registry.yarnpkg.com/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz#407c3d18c7fbed7a264f3b9a3812620b93499de1" + resolved "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz" integrity sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA== dependencies: "@apollo/protobufjs" "1.2.7" "@apollo/utils.dropunuseddefinitions@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz#02b04006442eaf037f4c4624146b12775d70d929" + resolved "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz" integrity sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg== "@apollo/utils.keyvaluecache@^1.0.1": version "1.0.2" - resolved "https://registry.yarnpkg.com/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz#2bfe358c4d82f3a0950518451996758c52613f57" + resolved "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz" integrity sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg== dependencies: "@apollo/utils.logger" "^1.0.0" @@ -121,34 +121,34 @@ "@apollo/utils.logger@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@apollo/utils.logger/-/utils.logger-1.0.1.tgz#aea0d1bb7ceb237f506c6bbf38f10a555b99a695" + resolved "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz" integrity sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA== "@apollo/utils.printwithreducedwhitespace@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz#c466299a4766eef8577a2a64c8f27712e8bd7e30" + resolved "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz" integrity sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q== "@apollo/utils.removealiases@1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz#75f6d83098af1fcae2d3beb4f515ad4a8452a8c1" + resolved "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz" integrity sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A== "@apollo/utils.sortast@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz#93218c7008daf3e2a0725196085a33f5aab5ad07" + resolved "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz" integrity sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA== dependencies: lodash.sortby "^4.7.0" "@apollo/utils.stripsensitiveliterals@^1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz#4920651f36beee8e260e12031a0c5863ad0c7b28" + resolved "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz" integrity sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w== "@apollo/utils.usagereporting@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz#3c70b49e554771659576fe35381c7a4b321d27fd" + resolved "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz" integrity sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ== dependencies: "@apollo/usage-reporting-protobuf" "^4.0.0" @@ -160,19 +160,19 @@ "@apollographql/apollo-tools@^0.5.3": version "0.5.4" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz#cb3998c6cf12e494b90c733f44dd9935e2d8196c" + resolved "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz" integrity sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw== "@apollographql/graphql-playground-html@1.6.29": version "1.6.29" - resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz#a7a646614a255f62e10dcf64a7f68ead41dec453" + resolved "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz" integrity sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA== dependencies: xss "^1.0.8" "@aws-crypto/crc32@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz" integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== dependencies: "@aws-crypto/util" "^5.2.0" @@ -181,7 +181,7 @@ "@aws-crypto/crc32c@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" + resolved "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz" integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== dependencies: "@aws-crypto/util" "^5.2.0" @@ -190,7 +190,7 @@ "@aws-crypto/sha1-browser@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz#b0ee2d2821d3861f017e965ef3b4cb38e3b6a0f4" + resolved "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz" integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== dependencies: "@aws-crypto/supports-web-crypto" "^5.2.0" @@ -202,7 +202,7 @@ "@aws-crypto/sha256-browser@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz" integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== dependencies: "@aws-crypto/sha256-js" "^5.2.0" @@ -213,9 +213,9 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": +"@aws-crypto/sha256-js@^5.2.0", "@aws-crypto/sha256-js@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz" integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== dependencies: "@aws-crypto/util" "^5.2.0" @@ -224,14 +224,14 @@ "@aws-crypto/supports-web-crypto@^5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + resolved "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz" integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== dependencies: tslib "^2.6.2" -"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": +"@aws-crypto/util@^5.2.0", "@aws-crypto/util@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz" integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== dependencies: "@aws-sdk/types" "^3.222.0" @@ -240,7 +240,7 @@ "@aws-sdk/client-s3@^3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.968.0.tgz#b9b8b1825abc10788cc4fac8752b0306a6e9702d" + resolved "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.968.0.tgz" integrity sha512-YQARjiiucSkaSLS0HNyexOQzYM5pPRWSo+FNtq5JSuXwJQb8vs53JeZfk7yKb59G94Oh0BLAv1598XaEdtAFyA== dependencies: "@aws-crypto/sha1-browser" "5.2.0" @@ -301,7 +301,7 @@ "@aws-sdk/client-sso@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.968.0.tgz#7fc682533a1d218fb9dcf32f10d6a5416e0e7274" + resolved "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.968.0.tgz" integrity sha512-y+k23MvMzpn1WpeQ9sdEXg1Bbw7dfi0ZH2uwyBv78F/kz0mZOI+RJ1KJg8DgSD8XvdxB8gX5GQ8rzo0LnDothA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -345,7 +345,7 @@ "@aws-sdk/core@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.968.0.tgz#4590f3379325362b71578426fc43872337a7b802" + resolved "https://registry.npmjs.org/@aws-sdk/core/-/core-3.968.0.tgz" integrity sha512-u4lIpvGqMMHZN523/RxW70xNoVXHBXucIWZsxFKc373E6TWYEb16ddFhXTELioS5TU93qkd/6yDQZzI6AAhbkw== dependencies: "@aws-sdk/types" "3.968.0" @@ -364,7 +364,7 @@ "@aws-sdk/crc64-nvme@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/crc64-nvme/-/crc64-nvme-3.968.0.tgz#eba8f120d8ec5e1f6d071789efdaceab80bd4bc4" + resolved "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.968.0.tgz" integrity sha512-buylEu7i7I42uzfnQlu0oY35GAWcslU+Vyu9mlNszDKEDwsSyFDy1wg0wQ4vPyKDHlwsIm1srGa/MIaxZk1msg== dependencies: "@smithy/types" "^4.11.0" @@ -372,7 +372,7 @@ "@aws-sdk/credential-provider-env@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.968.0.tgz#8ed0bdce86285a91a389e35d9a421ba598121cc4" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.968.0.tgz" integrity sha512-G+zgXEniQxBHFtHo+0yImkYutvJZLvWqvkPUP8/cG+IaYg54OY7L/GPIAZJh0U3m0Uepao98NbL15zjM+uplqQ== dependencies: "@aws-sdk/core" "3.968.0" @@ -383,7 +383,7 @@ "@aws-sdk/credential-provider-http@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.968.0.tgz#79e7c87de4d03877745cd6a1f178855b25297bf0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.968.0.tgz" integrity sha512-79teHBx/EtsNRR3Bq8fQdmMHtUcYwvohm9EwXXFt2Jd3BEOBH872IjIlfKdAvdkM+jW1QeeWOZBAxXGPir7GcQ== dependencies: "@aws-sdk/core" "3.968.0" @@ -399,7 +399,7 @@ "@aws-sdk/credential-provider-ini@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.968.0.tgz#596fb1861d0d4e18575e44781ca893e2d8df6285" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.968.0.tgz" integrity sha512-9J9pcweoEN8yG7Qliux1zl9J3DT8X6OLcDN2RVXdTd5xzWBaYlupnUiJzoP6lvXdMnEmlDZaV7IMtoBdG7MY6g== dependencies: "@aws-sdk/core" "3.968.0" @@ -419,7 +419,7 @@ "@aws-sdk/credential-provider-login@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.968.0.tgz#8442e43899b4af23e6a7855c37071b6b2bd4a3b3" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.968.0.tgz" integrity sha512-YxBaR0IMuHPOVTG+73Ve0QfllweN+EdwBRnHFhUGnahMGAcTmcaRdotqwqWfiws+9ud44IFKjxXR3t8jaGpFnQ== dependencies: "@aws-sdk/core" "3.968.0" @@ -433,7 +433,7 @@ "@aws-sdk/credential-provider-node@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.968.0.tgz#8c3c336929d6c669cf282a93666596e93a4b7e0b" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.968.0.tgz" integrity sha512-wei6v0c9vDEam8pM5eWe9bt+5ixg8nL0q+DFPzI6iwdLUqmJsPoAzWPEyMkgp03iE02SS2fMqPWpmRjz/NVyUw== dependencies: "@aws-sdk/credential-provider-env" "3.968.0" @@ -451,7 +451,7 @@ "@aws-sdk/credential-provider-process@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.968.0.tgz#1fd585a3089d2838de22ec50a062621f811592bc" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.968.0.tgz" integrity sha512-my9M/ijRyEACoyeEWiC2sTVM3+eck5IWPGTPQrlYMKivy4LLlZchohtIopuqTom+JZzLZD508j1s9aDvl7BA0w== dependencies: "@aws-sdk/core" "3.968.0" @@ -463,7 +463,7 @@ "@aws-sdk/credential-provider-sso@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.968.0.tgz#1f52c88b3917b4b027460628280108adc8a89181" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.968.0.tgz" integrity sha512-XPYPcxfWIt5jBbofoP2xhAHlFYos0dzwbHsoE18Cera/XnaCEbsUpdROo30t0Kjdbv0EWMYLMPDi9G+vPRDnhQ== dependencies: "@aws-sdk/client-sso" "3.968.0" @@ -477,7 +477,7 @@ "@aws-sdk/credential-provider-web-identity@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.968.0.tgz#8df6527b432a55ebbadae728d1ace342bb948d80" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.968.0.tgz" integrity sha512-9HNAP6mx2jsBW4moWnRg5ycyZ0C1EbtMIegIHa93ga13B/8VZF9Y0iDnwW73yQYzCEt9UrDiFeRck/ChZup3rA== dependencies: "@aws-sdk/core" "3.968.0" @@ -490,7 +490,7 @@ "@aws-sdk/middleware-bucket-endpoint@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.968.0.tgz#4a2d82ec3f35349039f7bdfc33726a680d5de2b4" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.968.0.tgz" integrity sha512-KlA6D9wgyGF3KkKIRmmXxvKfzzGkibnnR6Kjp0NQAOi4jvKWuT/HKJX87sBJIrk8RWq+9Aq0SOY9LYqkdx9zJQ== dependencies: "@aws-sdk/types" "3.968.0" @@ -503,7 +503,7 @@ "@aws-sdk/middleware-expect-continue@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.968.0.tgz#11ab8d7346b1f027a723fb7c6b58a8a3f1d14815" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.968.0.tgz" integrity sha512-VCcDw21JCJywZH8+vpZCsVB9HV2BQ6BdF+cXww5nKnPNi+d05sHFczRHUQjfsEJiZ8Wb/a4M3mJuVrQ5gjiNUA== dependencies: "@aws-sdk/types" "3.968.0" @@ -513,7 +513,7 @@ "@aws-sdk/middleware-flexible-checksums@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.968.0.tgz#f13d2998225dc76093e7cf127f27f94861fc11e3" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.968.0.tgz" integrity sha512-5G4hpKS0XbU8s3WuuFP6qpB6kkFB45LQ2VomrS0FoyTXH9XUDYL1OmwraBe3t2N5LnpqOh1+RAJOyO8gRwO7xA== dependencies: "@aws-crypto/crc32" "5.2.0" @@ -533,7 +533,7 @@ "@aws-sdk/middleware-host-header@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.968.0.tgz#a8ba4841569df018f41d022efc5770bab2451bed" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.968.0.tgz" integrity sha512-ujlNT215VtE/2D2jEhFVcTuPPB36HJyLBM0ytnni/WPIjzq89iJrKR1tEhxpk8uct6A5NSQ6w9Y7g2Rw1rkSoQ== dependencies: "@aws-sdk/types" "3.968.0" @@ -543,7 +543,7 @@ "@aws-sdk/middleware-location-constraint@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.968.0.tgz#94f11537a71a28267ca00e9d04e803527d698b53" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.968.0.tgz" integrity sha512-+usAEX4rPmOofmLhZHgnRvW3idDnXdYnhaiOjfj2ynU05elTUkF2b4fyq+KhdjZQVbUpCewq4eKqgjGaGhIyyw== dependencies: "@aws-sdk/types" "3.968.0" @@ -552,7 +552,7 @@ "@aws-sdk/middleware-logger@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.968.0.tgz#c5a8fe6df51c3bce1053f2af16326cfeb846860d" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.968.0.tgz" integrity sha512-zvhhEPZgvaRDxzf27m2WmgaXoN7upFt/gvG7ofBN5zCBlkh3JtFamMh5KWYVQwMhc4eQBK3NjH0oIUKZSVztag== dependencies: "@aws-sdk/types" "3.968.0" @@ -561,7 +561,7 @@ "@aws-sdk/middleware-recursion-detection@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.968.0.tgz#4a63bbbe1c3e0beab2779323bb772370f962a639" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.968.0.tgz" integrity sha512-KygPiwpSAPGobgodK/oLb7OLiwK29pNJeNtP+GZ9pxpceDRqhN0Ub8Eo84dBbWq+jbzAqBYHzy+B1VsbQ/hLWA== dependencies: "@aws-sdk/types" "3.968.0" @@ -572,7 +572,7 @@ "@aws-sdk/middleware-sdk-s3@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.968.0.tgz#f1928b9f3ad9f9b9b66c83b2af4f257895827cf5" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.968.0.tgz" integrity sha512-fh2mQ/uwJ1Sth1q2dWAbeyky/SBPaqe1fjxvsNeEY6dtfi8PjW85zHpz1JoAhCKTRkrEdXYAqkqUwsUydLucyQ== dependencies: "@aws-sdk/core" "3.968.0" @@ -592,7 +592,7 @@ "@aws-sdk/middleware-ssec@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.968.0.tgz#f9d719af2a70d472be84d0f78c1feb3b5e450c71" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.968.0.tgz" integrity sha512-gbrhJ/JrKJ48SDPtlt5jPOadiPl2Rae0VLuNRyNg0ng7ygRO/0NjgKME4D1XINDjMOiZsOLNAcXmmwGFsVZsyw== dependencies: "@aws-sdk/types" "3.968.0" @@ -601,7 +601,7 @@ "@aws-sdk/middleware-user-agent@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.968.0.tgz#0d8f2ca6952e37b8d3868b0f44be5846e325b968" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.968.0.tgz" integrity sha512-4h5/B8FyxMjLxtXd5jbM2R69aO57qQiHoAJQTtkpuxmM7vhvjSxEQtMM9L1kuMXoMVNE7xM4886h0+gbmmxplg== dependencies: "@aws-sdk/core" "3.968.0" @@ -614,7 +614,7 @@ "@aws-sdk/nested-clients@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.968.0.tgz#035a3705132e25d0a888d2415505b3cb2b26d8d2" + resolved "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.968.0.tgz" integrity sha512-LLppm+8MzD3afD2IA/tYDp5AoVPOybK7MHQz5DVB4HsZ+fHvwYlvau2ZUK8nKwJSk5c1kWcxCZkyuJQjFu37ng== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -658,7 +658,7 @@ "@aws-sdk/region-config-resolver@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.968.0.tgz#c95ddabff115d7c2f18a42a0d7259d8ee8c03a07" + resolved "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.968.0.tgz" integrity sha512-BzrCpxEsAHbi+yDGtgXJ+/5AvLPjfhcT6DlL+Fc4g13J5Z0VwiO95Wem+Q4KK7WDZH7/sZ/1WFvfitjLTKZbEw== dependencies: "@aws-sdk/types" "3.968.0" @@ -669,7 +669,7 @@ "@aws-sdk/s3-request-presigner@^3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.968.0.tgz#53bfdb582455c48bd927a405720b7ce967c1faec" + resolved "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.968.0.tgz" integrity sha512-D6WLsQxJExpsuISL6/QB0RvHdIW/F7ohcyeB6SDUpEk7M0+s/oyo5exxFUV/2U20Kdd8TMGx/hamLu9xDm0HjQ== dependencies: "@aws-sdk/signature-v4-multi-region" "3.968.0" @@ -683,7 +683,7 @@ "@aws-sdk/signature-v4-multi-region@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.968.0.tgz#880d19f3287cdc7418f202ff11ded2c111f06aa0" + resolved "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.968.0.tgz" integrity sha512-kRBA1KK3LTHnfYJLPsESNF2WhQN6DyGc9MiM6qG8AdJwMPQkanF5hwtckV1ToO2KB5v1q+1PuvBvy6Npd2IV+w== dependencies: "@aws-sdk/middleware-sdk-s3" "3.968.0" @@ -695,7 +695,7 @@ "@aws-sdk/token-providers@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.968.0.tgz#7d24468d0984b88d8da5036ea6c8df976ce60a53" + resolved "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.968.0.tgz" integrity sha512-lXUZqB2qTFmZYNXPnVT0suSHGiuQAPrL2DhmhbjqOdR7+GKDHL5KbeKFvPisy7Y4neliJqT4Q1VPWa0nqYaiZg== dependencies: "@aws-sdk/core" "3.968.0" @@ -706,9 +706,9 @@ "@smithy/types" "^4.11.0" tslib "^2.6.2" -"@aws-sdk/types@3.968.0", "@aws-sdk/types@^3.222.0": +"@aws-sdk/types@^3.222.0", "@aws-sdk/types@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.968.0.tgz#0ccecb461d24ad69df50749b06e7987e97aaa3b1" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.968.0.tgz" integrity sha512-Wuumj/1cuiuXTMdHmvH88zbEl+5Pw++fOFQuMCF4yP0R+9k1lwX8rVst+oy99xaxtdluJZXrsccoZoA67ST1Ow== dependencies: "@smithy/types" "^4.11.0" @@ -716,14 +716,14 @@ "@aws-sdk/util-arn-parser@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.968.0.tgz#ca8d7d26ffafa340a9e441a40db886ddb587743f" + resolved "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.968.0.tgz" integrity sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw== dependencies: tslib "^2.6.2" "@aws-sdk/util-endpoints@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.968.0.tgz#23ce9474e270d7997f226e2a4de1ad46c3a8720f" + resolved "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.968.0.tgz" integrity sha512-9IdilgylS0crFSeI59vtr8qhDYMYYOvnvkl1dLp59+EmLH1IdXz7+4cR5oh5PkoqD7DRzc5Uzm2GnZhK6I0oVQ== dependencies: "@aws-sdk/types" "3.968.0" @@ -734,7 +734,7 @@ "@aws-sdk/util-format-url@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.968.0.tgz#6718bfa5bc2567da54f0e1f0bfef610b5053a64e" + resolved "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.968.0.tgz" integrity sha512-tJZuMKOJNCjrSh3+TS1brR4pW2W/O5mVkbKERJfO7BOhyV2+819QAoCX5a9Jy3AOHyMWddCpHmXaz6y2a3avWA== dependencies: "@aws-sdk/types" "3.968.0" @@ -744,14 +744,14 @@ "@aws-sdk/util-locate-window@^3.0.0": version "3.965.2" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz#e3fde1227b2f76b94e33650cb4bfa391738a26dc" + resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz" integrity sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q== dependencies: tslib "^2.6.2" "@aws-sdk/util-user-agent-browser@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.968.0.tgz#309d0985ba2fdf085b52ef06cae9445c84603908" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.968.0.tgz" integrity sha512-nRxjs8Jpq8ZHFsa/0uiww2f4+40D6Dt6bQmepAJHIE/D+atwPINDKsfamCjFnxrjKU3WBWpGYEf/QDO0XZsFMw== dependencies: "@aws-sdk/types" "3.968.0" @@ -761,7 +761,7 @@ "@aws-sdk/util-user-agent-node@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.968.0.tgz#88fd5fad127bc4c06decdd710fa7d97392585ebc" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.968.0.tgz" integrity sha512-oaIkPGraGhZgkDmxVhTIlakaUNWKO9aMN+uB6I+eS26MWi/lpMK66HTZeXEnaTrmt5/kl99YC0N37zScz58Tdg== dependencies: "@aws-sdk/middleware-user-agent" "3.968.0" @@ -772,7 +772,7 @@ "@aws-sdk/xml-builder@3.968.0": version "3.968.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.968.0.tgz#3ab47ac1483c79629038546c41b02b692505abe4" + resolved "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.968.0.tgz" integrity sha512-bZQKn41ebPh/uW9uWUE5oLuaBr44Gt78dkw2amu5zcwo1J/d8s6FdzZcRDmz0rHE2NHJWYkdQYeVQo7jhMziqA== dependencies: "@smithy/types" "^4.11.0" @@ -781,12 +781,12 @@ "@aws/lambda-invoke-store@^0.2.2": version "0.2.3" - resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz#f1137f56209ccc69c15f826242cbf37f828617dd" + resolved "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz" integrity sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw== "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5", "@babel/code-frame@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: "@babel/helper-validator-identifier" "^7.27.1" @@ -795,12 +795,12 @@ "@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz" integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.20", "@babel/core@^7.23.9", "@babel/core@^7.27.4": +"@babel/core@^7.0.0", "@babel/core@^7.0.0 || ^8.0.0-0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.22.20", "@babel/core@^7.23.9", "@babel/core@^7.27.4", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.8.0": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz" integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== dependencies: "@babel/code-frame" "^7.27.1" @@ -821,7 +821,7 @@ "@babel/generator@^7.28.5", "@babel/generator@^7.7.2": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz" integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== dependencies: "@babel/parser" "^7.28.5" @@ -832,14 +832,14 @@ "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": version "7.27.3" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz" integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg== dependencies: "@babel/types" "^7.27.3" "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz" integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== dependencies: "@babel/compat-data" "^7.27.2" @@ -850,7 +850,7 @@ "@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz" integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -863,7 +863,7 @@ "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz#7c1ddd64b2065c7f78034b25b43346a7e19ed997" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz" integrity sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -872,7 +872,7 @@ "@babel/helper-define-polyfill-provider@^0.6.5": version "0.6.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz#742ccf1cb003c07b48859fc9fa2c1bbe40e5f753" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz" integrity sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg== dependencies: "@babel/helper-compilation-targets" "^7.27.2" @@ -883,12 +883,12 @@ "@babel/helper-globals@^7.28.0": version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== "@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz" integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== dependencies: "@babel/traverse" "^7.28.5" @@ -896,7 +896,7 @@ "@babel/helper-module-imports@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== dependencies: "@babel/traverse" "^7.27.1" @@ -904,7 +904,7 @@ "@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz" integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== dependencies: "@babel/helper-module-imports" "^7.27.1" @@ -913,19 +913,19 @@ "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz" integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== dependencies: "@babel/types" "^7.27.1" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz" integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== "@babel/helper-remap-async-to-generator@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz#4601d5c7ce2eb2aea58328d43725523fcd362ce6" + resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz" integrity sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA== dependencies: "@babel/helper-annotate-as-pure" "^7.27.1" @@ -934,7 +934,7 @@ "@babel/helper-replace-supers@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz" integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== dependencies: "@babel/helper-member-expression-to-functions" "^7.27.1" @@ -943,7 +943,7 @@ "@babel/helper-skip-transparent-expression-wrappers@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz" integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== dependencies: "@babel/traverse" "^7.27.1" @@ -951,22 +951,22 @@ "@babel/helper-string-parser@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== "@babel/helper-wrap-function@^7.27.1": version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz#fe4872092bc1438ffd0ce579e6f699609f9d0a7a" + resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz" integrity sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g== dependencies: "@babel/template" "^7.27.2" @@ -975,7 +975,7 @@ "@babel/helpers@^7.28.4": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz" integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== dependencies: "@babel/template" "^7.27.2" @@ -983,14 +983,14 @@ "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz" integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: "@babel/types" "^7.28.5" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz#fbde57974707bbfa0376d34d425ff4fa6c732421" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz" integrity sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -998,21 +998,21 @@ "@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz#43f70a6d7efd52370eefbdf55ae03d91b293856d" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz" integrity sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz#beb623bd573b8b6f3047bd04c32506adc3e58a72" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz" integrity sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz#e134a5479eb2ba9c02714e8c1ebf1ec9076124fd" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz" integrity sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1021,7 +1021,7 @@ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.28.3": version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz#373f6e2de0016f73caf8f27004f61d167743742a" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz" integrity sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1029,138 +1029,138 @@ "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + 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== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-import-assertions@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz" integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz" integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + 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== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.7.2": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz" integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + 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== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + 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== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + 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== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + 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== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + 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== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + 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== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + 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== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz" integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz" integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.18.6" @@ -1168,14 +1168,14 @@ "@babel/plugin-transform-arrow-functions@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz" integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-async-generator-functions@^7.28.0": version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz#1276e6c7285ab2cd1eccb0bc7356b7a69ff842c2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz" integrity sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1184,7 +1184,7 @@ "@babel/plugin-transform-async-to-generator@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz" integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== dependencies: "@babel/helper-module-imports" "^7.27.1" @@ -1193,21 +1193,21 @@ "@babel/plugin-transform-block-scoped-functions@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz#558a9d6e24cf72802dd3b62a4b51e0d62c0f57f9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz" integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-block-scoping@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz#e0d3af63bd8c80de2e567e690a54e84d85eb16f6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz" integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-class-properties@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz" integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== dependencies: "@babel/helper-create-class-features-plugin" "^7.27.1" @@ -1215,7 +1215,7 @@ "@babel/plugin-transform-class-static-block@^7.28.3": version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz#d1b8e69b54c9993bc558203e1f49bfc979bfd852" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz" integrity sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg== dependencies: "@babel/helper-create-class-features-plugin" "^7.28.3" @@ -1223,7 +1223,7 @@ "@babel/plugin-transform-classes@^7.28.4": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz" integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -1235,7 +1235,7 @@ "@babel/plugin-transform-computed-properties@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz" integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1243,7 +1243,7 @@ "@babel/plugin-transform-destructuring@^7.28.0", "@babel/plugin-transform-destructuring@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz#b8402764df96179a2070bb7b501a1586cf8ad7a7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz" integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1251,7 +1251,7 @@ "@babel/plugin-transform-dotall-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz#aa6821de864c528b1fecf286f0a174e38e826f4d" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz" integrity sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1259,14 +1259,14 @@ "@babel/plugin-transform-duplicate-keys@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz#f1fbf628ece18e12e7b32b175940e68358f546d1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz" integrity sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz#5043854ca620a94149372e69030ff8cb6a9eb0ec" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz" integrity sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1274,14 +1274,14 @@ "@babel/plugin-transform-dynamic-import@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz#4c78f35552ac0e06aa1f6e3c573d67695e8af5a4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz" integrity sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-explicit-resource-management@^7.28.0": version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz#45be6211b778dbf4b9d54c4e8a2b42fa72e09a1a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz" integrity sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1289,21 +1289,21 @@ "@babel/plugin-transform-exponentiation-operator@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz#7cc90a8170e83532676cfa505278e147056e94fe" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz" integrity sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-export-namespace-from@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz#71ca69d3471edd6daa711cf4dfc3400415df9c23" + resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz" integrity sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-for-of@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz#bc24f7080e9ff721b63a70ac7b2564ca15b6c40a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz" integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1311,7 +1311,7 @@ "@babel/plugin-transform-function-name@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz#4d0bf307720e4dce6d7c30fcb1fd6ca77bdeb3a7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz" integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== dependencies: "@babel/helper-compilation-targets" "^7.27.1" @@ -1320,35 +1320,35 @@ "@babel/plugin-transform-json-strings@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz#a2e0ce6ef256376bd527f290da023983527a4f4c" + resolved "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz" integrity sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-literals@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz#baaefa4d10a1d4206f9dcdda50d7d5827bb70b24" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz" integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-logical-assignment-operators@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz#d028fd6db8c081dee4abebc812c2325e24a85b0e" + resolved "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz" integrity sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-member-expression-literals@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz#37b88ba594d852418e99536f5612f795f23aeaf9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz" integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-modules-amd@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz#a4145f9d87c2291fe2d05f994b65dba4e3e7196f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz" integrity sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA== dependencies: "@babel/helper-module-transforms" "^7.27.1" @@ -1356,7 +1356,7 @@ "@babel/plugin-transform-modules-commonjs@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz" integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== dependencies: "@babel/helper-module-transforms" "^7.27.1" @@ -1364,7 +1364,7 @@ "@babel/plugin-transform-modules-systemjs@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz#7439e592a92d7670dfcb95d0cbc04bd3e64801d2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz" integrity sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew== dependencies: "@babel/helper-module-transforms" "^7.28.3" @@ -1374,7 +1374,7 @@ "@babel/plugin-transform-modules-umd@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz#63f2cf4f6dc15debc12f694e44714863d34cd334" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz" integrity sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w== dependencies: "@babel/helper-module-transforms" "^7.27.1" @@ -1382,7 +1382,7 @@ "@babel/plugin-transform-named-capturing-groups-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz#f32b8f7818d8fc0cc46ee20a8ef75f071af976e1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz" integrity sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1390,28 +1390,28 @@ "@babel/plugin-transform-new-target@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz#259c43939728cad1706ac17351b7e6a7bea1abeb" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz" integrity sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-nullish-coalescing-operator@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" + resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz" integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-numeric-separator@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz#614e0b15cc800e5997dadd9bd6ea524ed6c819c6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz" integrity sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-object-rest-spread@^7.28.4": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz#9ee1ceca80b3e6c4bac9247b2149e36958f7f98d" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz" integrity sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew== dependencies: "@babel/helper-compilation-targets" "^7.27.2" @@ -1422,7 +1422,7 @@ "@babel/plugin-transform-object-super@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz#1c932cd27bf3874c43a5cac4f43ebf970c9871b5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz" integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1430,14 +1430,14 @@ "@babel/plugin-transform-optional-catch-binding@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz#84c7341ebde35ccd36b137e9e45866825072a30c" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz" integrity sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-optional-chaining@^7.27.1", "@babel/plugin-transform-optional-chaining@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz#8238c785f9d5c1c515a90bf196efb50d075a4b26" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz" integrity sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1445,14 +1445,14 @@ "@babel/plugin-transform-parameters@^7.27.7": version "7.27.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz#1fd2febb7c74e7d21cf3b05f7aebc907940af53a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz" integrity sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-private-methods@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz" integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA== dependencies: "@babel/helper-create-class-features-plugin" "^7.27.1" @@ -1460,7 +1460,7 @@ "@babel/plugin-transform-private-property-in-object@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz#4dbbef283b5b2f01a21e81e299f76e35f900fb11" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz" integrity sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ== dependencies: "@babel/helper-annotate-as-pure" "^7.27.1" @@ -1469,21 +1469,21 @@ "@babel/plugin-transform-property-literals@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz#07eafd618800591e88073a0af1b940d9a42c6424" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz" integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-regenerator@^7.28.4": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz#9d3fa3bebb48ddd0091ce5729139cd99c67cea51" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz" integrity sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-regexp-modifiers@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz#df9ba5577c974e3f1449888b70b76169998a6d09" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz" integrity sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1491,21 +1491,21 @@ "@babel/plugin-transform-reserved-words@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz#40fba4878ccbd1c56605a4479a3a891ac0274bb4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz" integrity sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-shorthand-properties@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz" integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-spread@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz" integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -1513,35 +1513,35 @@ "@babel/plugin-transform-sticky-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz#18984935d9d2296843a491d78a014939f7dcd280" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz" integrity sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-template-literals@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz#1a0eb35d8bb3e6efc06c9fd40eb0bcef548328b8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz" integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-typeof-symbol@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz#70e966bb492e03509cf37eafa6dcc3051f844369" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz" integrity sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-unicode-escapes@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz#3e3143f8438aef842de28816ece58780190cf806" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz" integrity sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-unicode-property-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz#bdfe2d3170c78c5691a3c3be934c8c0087525956" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz" integrity sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1549,7 +1549,7 @@ "@babel/plugin-transform-unicode-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz" integrity sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1557,7 +1557,7 @@ "@babel/plugin-transform-unicode-sets-regex@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz#6ab706d10f801b5c72da8bb2548561fa04193cd1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz" integrity sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.27.1" @@ -1565,7 +1565,7 @@ "@babel/preset-env@^7.22.20": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.28.5.tgz#82dd159d1563f219a1ce94324b3071eb89e280b0" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz" integrity sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg== dependencies: "@babel/compat-data" "^7.28.5" @@ -1641,7 +1641,7 @@ "@babel/preset-modules@0.1.6-no-external-plugins": version "0.1.6-no-external-plugins" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz" integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -1650,12 +1650,12 @@ "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== "@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3": version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz" integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== dependencies: "@babel/code-frame" "^7.27.1" @@ -1664,7 +1664,7 @@ "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz" integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== dependencies: "@babel/code-frame" "^7.27.1" @@ -1677,7 +1677,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz" integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== dependencies: "@babel/helper-string-parser" "^7.27.1" @@ -1685,24 +1685,24 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@colors/colors@1.5.0": version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: "@jridgewell/trace-mapping" "0.3.9" "@dependents/detective-less@^3.0.1": version "3.0.2" - resolved "https://registry.yarnpkg.com/@dependents/detective-less/-/detective-less-3.0.2.tgz#c6e46997010fe03a5dc98351a7e99a46d34f5832" + resolved "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-3.0.2.tgz" integrity sha512-1YUvQ+e0eeTWAHoN8Uz2x2U37jZs6IGutiIE5LXId7cxfUGhtZjzxE06FdUiuiRrW+UE0vNCdSNPH2lY4dQCOQ== dependencies: gonzales-pe "^4.3.0" @@ -1710,19 +1710,19 @@ "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.9.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz" integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": version "4.12.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== "@eslint/eslintrc@^2.1.4": version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz" integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" @@ -1737,39 +1737,39 @@ "@eslint/js@8.57.1": version "8.57.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== "@exodus/schemasafe@^1.0.0-rc.2": version "1.3.0" - resolved "https://registry.yarnpkg.com/@exodus/schemasafe/-/schemasafe-1.3.0.tgz#731656abe21e8e769a7f70a4d833e6312fe59b7f" + resolved "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz" integrity sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw== "@fastify/busboy@^1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-1.2.1.tgz#9c6db24a55f8b803b5222753b24fe3aea2ba9ca3" + resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz" integrity sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q== dependencies: text-decoding "^1.0.0" "@fastify/busboy@^2.0.0": version "2.1.1" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz" integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== "@firebase/app-types@0.9.0": version "0.9.0" - resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.0.tgz#35b5c568341e9e263b29b3d2ba0e9cfc9ec7f01e" + resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz" integrity sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q== "@firebase/auth-interop-types@0.2.1": version "0.2.1" - resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz#78884f24fa539e34a06c03612c75f222fcc33742" + resolved "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz" integrity sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg== "@firebase/component@0.6.4": version "0.6.4" - resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.6.4.tgz#8981a6818bd730a7554aa5e0516ffc9b1ae3f33d" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz" integrity sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA== dependencies: "@firebase/util" "1.9.3" @@ -1777,7 +1777,7 @@ "@firebase/database-compat@^0.3.4": version "0.3.4" - resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-0.3.4.tgz#4e57932f7a5ba761cd5ac946ab6b6ab3f660522c" + resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz" integrity sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg== dependencies: "@firebase/component" "0.6.4" @@ -1787,9 +1787,9 @@ "@firebase/util" "1.9.3" tslib "^2.1.0" -"@firebase/database-types@0.10.4", "@firebase/database-types@^0.10.4": +"@firebase/database-types@^0.10.4", "@firebase/database-types@0.10.4": version "0.10.4" - resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.10.4.tgz#47ba81113512dab637abace61cfb65f63d645ca7" + resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz" integrity sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ== dependencies: "@firebase/app-types" "0.9.0" @@ -1797,7 +1797,7 @@ "@firebase/database@0.14.4": version "0.14.4" - resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.14.4.tgz#9e7435a16a540ddfdeb5d99d45618e6ede179aa6" + resolved "https://registry.npmjs.org/@firebase/database/-/database-0.14.4.tgz" integrity sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ== dependencies: "@firebase/auth-interop-types" "0.2.1" @@ -1809,21 +1809,21 @@ "@firebase/logger@0.4.0": version "0.4.0" - resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.4.0.tgz#15ecc03c452525f9d47318ad9491b81d1810f113" + resolved "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz" integrity sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA== dependencies: tslib "^2.1.0" "@firebase/util@1.9.3": version "1.9.3" - resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.9.3.tgz#45458dd5cd02d90e55c656e84adf6f3decf4b7ed" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz" integrity sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA== dependencies: tslib "^2.1.0" "@google-cloud/firestore@^6.8.0": version "6.8.0" - resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-6.8.0.tgz#d8c852844c381afaf62592796606c10e178400b5" + resolved "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.8.0.tgz" integrity sha512-JRpk06SmZXLGz0pNx1x7yU3YhkUXheKgH5hbDZ4kMsdhtfV5qPLJLRI4wv69K0cZorIk+zTMOwptue7hizo0eA== dependencies: fast-deep-equal "^3.1.1" @@ -1833,7 +1833,7 @@ "@google-cloud/paginator@^3.0.7": version "3.0.7" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.7.tgz#fb6f8e24ec841f99defaebf62c75c2e744dd419b" + resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz" integrity sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ== dependencies: arrify "^2.0.0" @@ -1841,7 +1841,7 @@ "@google-cloud/paginator@^5.0.0": version "5.0.2" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-5.0.2.tgz#86ad773266ce9f3b82955a8f75e22cd012ccc889" + resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz" integrity sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg== dependencies: arrify "^2.0.0" @@ -1849,27 +1849,27 @@ "@google-cloud/projectify@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-3.0.0.tgz#302b25f55f674854dce65c2532d98919b118a408" + resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz" integrity sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA== "@google-cloud/projectify@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-4.0.0.tgz#d600e0433daf51b88c1fa95ac7f02e38e80a07be" + resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz" integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== -"@google-cloud/promisify@<4.1.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-4.0.0.tgz#a906e533ebdd0f754dca2509933334ce58b8c8b1" - integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== - "@google-cloud/promisify@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-3.0.1.tgz#8d724fb280f47d1ff99953aee0c1669b25238c2e" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz" integrity sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA== +"@google-cloud/promisify@<4.1.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz" + integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== + "@google-cloud/storage@^6.9.5": version "6.12.0" - resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-6.12.0.tgz#a5d3093cc075252dca5bd19a3cfda406ad3a9de1" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.12.0.tgz" integrity sha512-78nNAY7iiZ4O/BouWMWTD/oSF2YtYgYB3GZirn0To6eBOugjXVoK+GXgUXOl+HlqbAOyHxAVXOlsj3snfbQ1dw== dependencies: "@google-cloud/paginator" "^3.0.7" @@ -1893,7 +1893,7 @@ "@google-cloud/storage@^7.1.0": version "7.17.2" - resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-7.17.2.tgz#dd6ed7a60c5f917612dd665f292541cb2624b243" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.17.2.tgz" integrity sha512-6xN0KNO8L/LIA5zu3CJwHkJiB6n65eykBLOb0E+RooiHYgX8CSao6lvQiKT9TBk2gL5g33LL3fmhDodZnt56rw== dependencies: "@google-cloud/paginator" "^5.0.0" @@ -1914,7 +1914,7 @@ "@graphql-tools/batch-execute@8.5.1": version "8.5.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.1.tgz#fa3321d58c64041650be44250b1ebc3aab0ba7a9" + resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.5.1.tgz" integrity sha512-hRVDduX0UDEneVyEWtc2nu5H2PxpfSfM/riUlgZvo/a/nG475uyehxR5cFGvTEPEQUKY3vGIlqvtRigzqTfCew== dependencies: "@graphql-tools/utils" "8.9.0" @@ -1924,7 +1924,7 @@ "@graphql-tools/delegate@^8.8.1": version "8.8.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-8.8.1.tgz#0653a72f38947f38ab7917dfac50ebf6a6b883e9" + resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.8.1.tgz" integrity sha512-NDcg3GEQmdEHlnF7QS8b4lM1PSF+DKeFcIlLEfZFBvVq84791UtJcDj8734sIHLukmyuAxXMfA1qLd2l4lZqzA== dependencies: "@graphql-tools/batch-execute" "8.5.1" @@ -1936,7 +1936,7 @@ "@graphql-tools/jest-transform@^2.0.0": version "2.0.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/jest-transform/-/jest-transform-2.0.1.tgz#91a809bddd412e075758b941f8494ed0c9063ea4" + resolved "https://registry.npmjs.org/@graphql-tools/jest-transform/-/jest-transform-2.0.1.tgz" integrity sha512-Iz5zFASYUZuEbRFGW/BsWZYsa1hS7nGBWppdTAuHghYTuDuLpcTPoCeQ+4nOo9yQNP0czk5mUDdO2PXGRF1PCA== dependencies: "@graphql-tools/webpack-loader" "7.0.0" @@ -1946,24 +1946,16 @@ "@graphql-tools/load-files@^6.3.2": version "6.6.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.6.1.tgz#91ce18d910baf8678459486d8cccd474767bec0a" + resolved "https://registry.npmjs.org/@graphql-tools/load-files/-/load-files-6.6.1.tgz" integrity sha512-nd4GOjdD68bdJkHfRepILb0gGwF63mJI7uD4oJuuf2Kzeq8LorKa6WfyxUhdMuLmZhnx10zdAlWPfwv1NOAL4Q== dependencies: globby "11.1.0" tslib "^2.4.0" unixify "1.0.0" -"@graphql-tools/merge@8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.1.tgz#06121942ad28982a14635dbc87b5d488a041d722" - integrity sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg== - dependencies: - "@graphql-tools/utils" "8.9.0" - tslib "^2.4.0" - "@graphql-tools/merge@^8.1.2", "@graphql-tools/merge@^8.4.1": version "8.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz" integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== dependencies: "@graphql-tools/utils" "^9.2.1" @@ -1971,15 +1963,23 @@ "@graphql-tools/merge@^9.1.1": version "9.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.1.tgz#6a51775f5ba6bdef213def1fd277536f9468b44c" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.1.tgz" integrity sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w== dependencies: "@graphql-tools/utils" "^10.9.1" tslib "^2.4.0" +"@graphql-tools/merge@8.3.1": + version "8.3.1" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz" + integrity sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg== + dependencies: + "@graphql-tools/utils" "8.9.0" + tslib "^2.4.0" + "@graphql-tools/mock@^8.1.2": version "8.7.20" - resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-8.7.20.tgz#c83ae0f1940d194a3982120c9c85f3ac6b4f7f20" + resolved "https://registry.npmjs.org/@graphql-tools/mock/-/mock-8.7.20.tgz" integrity sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ== dependencies: "@graphql-tools/schema" "^9.0.18" @@ -1989,33 +1989,33 @@ "@graphql-tools/optimize@2.0.0": version "2.0.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-2.0.0.tgz#7a9779d180824511248a50c5a241eff6e7a2d906" + resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-2.0.0.tgz" integrity sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg== dependencies: tslib "^2.4.0" -"@graphql-tools/schema@8.5.1", "@graphql-tools/schema@^8.0.0", "@graphql-tools/schema@^8.5.1": - version "8.5.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.5.1.tgz#c2f2ff1448380919a330312399c9471db2580b58" - integrity sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg== - dependencies: - "@graphql-tools/merge" "8.3.1" - "@graphql-tools/utils" "8.9.0" - tslib "^2.4.0" - value-or-promise "1.0.11" - "@graphql-tools/schema@^10.0.25": version "10.0.25" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.25.tgz#73de08bc765f482cde334562a1bebe95a2e5a3c4" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.25.tgz" integrity sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw== dependencies: "@graphql-tools/merge" "^9.1.1" "@graphql-tools/utils" "^10.9.1" tslib "^2.4.0" -"@graphql-tools/schema@^9.0.1", "@graphql-tools/schema@^9.0.18": +"@graphql-tools/schema@^8.0.0", "@graphql-tools/schema@^8.5.1", "@graphql-tools/schema@8.5.1": + version "8.5.1" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz" + integrity sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg== + dependencies: + "@graphql-tools/merge" "8.3.1" + "@graphql-tools/utils" "8.9.0" + tslib "^2.4.0" + value-or-promise "1.0.11" + +"@graphql-tools/schema@^9.0.1": version "9.0.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz" integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== dependencies: "@graphql-tools/merge" "^8.4.1" @@ -2023,16 +2023,19 @@ tslib "^2.4.0" value-or-promise "^1.0.12" -"@graphql-tools/utils@8.9.0": - version "8.9.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.9.0.tgz#c6aa5f651c9c99e1aca55510af21b56ec296cdb7" - integrity sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg== +"@graphql-tools/schema@^9.0.18": + version "9.0.19" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz" + integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== dependencies: + "@graphql-tools/merge" "^8.4.1" + "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" + value-or-promise "^1.0.12" "@graphql-tools/utils@^10.9.1": version "10.9.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.9.1.tgz#ff4067256f2080db0c66f4475858f29a8da65ecf" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.9.1.tgz" integrity sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" @@ -2043,20 +2046,27 @@ "@graphql-tools/utils@^9.1.1", "@graphql-tools/utils@^9.2.1": version "9.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz" integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== dependencies: "@graphql-typed-document-node/core" "^3.1.1" tslib "^2.4.0" +"@graphql-tools/utils@8.9.0": + version "8.9.0" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz" + integrity sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg== + dependencies: + tslib "^2.4.0" + "@graphql-tools/webpack-loader-runtime@7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/webpack-loader-runtime/-/webpack-loader-runtime-7.0.0.tgz#22000c99dfe757c0af812e671b3d2032f70632ad" + resolved "https://registry.npmjs.org/@graphql-tools/webpack-loader-runtime/-/webpack-loader-runtime-7.0.0.tgz" integrity sha512-E5Flr2NbuTH+sfiMY2HacLbczJEDmqfKC0haOsc24wN0hV3rJvvPiqYIROgILgwVSJPQIQ4cOf/XcRsatrGDvQ== "@graphql-tools/webpack-loader@7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/webpack-loader/-/webpack-loader-7.0.0.tgz#a417b88882f3d94beffd1196ef58b940ab80bf50" + resolved "https://registry.npmjs.org/@graphql-tools/webpack-loader/-/webpack-loader-7.0.0.tgz" integrity sha512-Mf2ZkoVR5+J5pFu5P0LPjvHZCObIs61IuQR8sFanMQXQDaM/f2dMn1Ie8pQ42YfDgLDbvJ+4XIikicJH1oDIDw== dependencies: "@graphql-tools/optimize" "2.0.0" @@ -2065,20 +2075,12 @@ "@graphql-typed-document-node/core@^3.1.1": version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@grpc/grpc-js@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.4.tgz#6cf152869910c2ac3429eee08c1dbdc84e7bafea" - integrity sha512-oEnzYiDuEsBydZBtP84BkpduLsE1nSAO4KrhTLHRzNrIQE647fhchmosTQsJdCo8X9zBBt+l5+fNk+m/yCFJ/Q== - dependencies: - "@grpc/proto-loader" "^0.7.8" - "@types/node" ">=12.12.47" - "@grpc/grpc-js@^1.9.3": version "1.14.0" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.14.0.tgz#a3c47e7816ca2b4d5490cba9e06a3cf324e675ad" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz" integrity sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg== dependencies: "@grpc/proto-loader" "^0.8.0" @@ -2086,25 +2088,33 @@ "@grpc/grpc-js@~1.8.0": version "1.8.22" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.8.22.tgz#847930c9af46e14df05b57fc12325db140ceff1d" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.22.tgz" integrity sha512-oAjDdN7fzbUi+4hZjKG96MR6KTEubAeMpQEb+77qy+3r0Ua5xTFuie6JOLr4ZZgl5g+W5/uRTS2M1V8mVAFPuA== dependencies: "@grpc/proto-loader" "^0.7.0" "@types/node" ">=12.12.47" -"@grpc/proto-loader@0.7.10": - version "0.7.10" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.10.tgz#6bf26742b1b54d0a473067743da5d3189d06d720" - integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== +"@grpc/grpc-js@1.9.4": + version "1.9.4" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.4.tgz" + integrity sha512-oEnzYiDuEsBydZBtP84BkpduLsE1nSAO4KrhTLHRzNrIQE647fhchmosTQsJdCo8X9zBBt+l5+fNk+m/yCFJ/Q== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.9": + version "0.7.15" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz" + integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== dependencies: lodash.camelcase "^4.3.0" long "^5.0.0" - protobufjs "^7.2.4" + protobufjs "^7.2.5" yargs "^17.7.2" -"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.8", "@grpc/proto-loader@^0.7.9": +"@grpc/proto-loader@^0.7.8": version "0.7.15" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz#4cdfbf35a35461fc843abe8b9e2c0770b5095e60" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz" integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== dependencies: lodash.camelcase "^4.3.0" @@ -2114,7 +2124,7 @@ "@grpc/proto-loader@^0.8.0": version "0.8.0" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.8.0.tgz#b6c324dd909c458a0e4aa9bfd3d69cf78a4b9bd8" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz" integrity sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ== dependencies: lodash.camelcase "^4.3.0" @@ -2122,9 +2132,24 @@ protobufjs "^7.5.3" yargs "^17.7.2" +"@grpc/proto-loader@0.7.10": + version "0.7.10" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz" + integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" + +"@hono/node-server@^1.19.9": + version "1.19.9" + resolved "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz" + integrity sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw== + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz" integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== dependencies: "@humanwhocodes/object-schema" "^2.0.3" @@ -2133,39 +2158,39 @@ "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/momoa@^2.0.3": version "2.0.4" - resolved "https://registry.yarnpkg.com/@humanwhocodes/momoa/-/momoa-2.0.4.tgz#8b9e7a629651d15009c3587d07a222deeb829385" + resolved "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz" integrity sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA== "@humanwhocodes/object-schema@^2.0.3": version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== "@ioredis/commands@1.4.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.4.0.tgz#9f657d51cdd5d2fdb8889592aa4a355546151f25" + resolved "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz" integrity sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ== "@isaacs/balanced-match@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + resolved "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz" integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== "@isaacs/brace-expansion@^5.0.0": version "5.0.0" - resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + resolved "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz" integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== dependencies: "@isaacs/balanced-match" "^4.0.1" "@isaacs/cliui@^8.0.2": version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== dependencies: string-width "^5.1.2" @@ -2177,7 +2202,7 @@ "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -2188,12 +2213,12 @@ "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz" integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: "@jest/types" "^29.6.3" @@ -2205,7 +2230,7 @@ "@jest/core@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz" integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: "@jest/console" "^29.7.0" @@ -2239,14 +2264,14 @@ "@jest/create-cache-key-function@^30.0.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-30.2.0.tgz#86dbaf8cce43e8a0266180a5236b6f0b3be9d09b" + resolved "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-30.2.0.tgz" integrity sha512-44F4l4Enf+MirJN8X/NhdGkl71k5rBYiwdVlo4HxOwbu0sHV8QKrGEedb1VUU4K3W7fBKE0HGfbn7eZm0Ti3zg== dependencies: "@jest/types" "30.2.0" "@jest/environment@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz" integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: "@jest/fake-timers" "^29.7.0" @@ -2256,14 +2281,14 @@ "@jest/expect-utils@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz" integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: jest-get-type "^29.6.3" "@jest/expect@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz" integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: expect "^29.7.0" @@ -2271,7 +2296,7 @@ "@jest/fake-timers@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz" integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: "@jest/types" "^29.6.3" @@ -2283,7 +2308,7 @@ "@jest/globals@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz" integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: "@jest/environment" "^29.7.0" @@ -2293,7 +2318,7 @@ "@jest/pattern@30.0.1": version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f" + resolved "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz" integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== dependencies: "@types/node" "*" @@ -2301,7 +2326,7 @@ "@jest/reporters@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz" integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -2329,23 +2354,23 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473" - integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== - dependencies: - "@sinclair/typebox" "^0.34.0" - "@jest/schemas@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" +"@jest/schemas@30.0.5": + version "30.0.5" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz" + integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== + dependencies: + "@sinclair/typebox" "^0.34.0" + "@jest/source-map@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz" integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: "@jridgewell/trace-mapping" "^0.3.18" @@ -2354,7 +2379,7 @@ "@jest/test-result@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz" integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: "@jest/console" "^29.7.0" @@ -2364,7 +2389,7 @@ "@jest/test-sequencer@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz" integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: "@jest/test-result" "^29.7.0" @@ -2374,7 +2399,7 @@ "@jest/transform@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz" integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" @@ -2395,7 +2420,7 @@ "@jest/transform@^30.0.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.2.0.tgz#54bef1a4510dcbd58d5d4de4fe2980a63077ef2a" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz" integrity sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA== dependencies: "@babel/core" "^7.27.4" @@ -2414,22 +2439,9 @@ slash "^3.0.0" write-file-atomic "^5.0.1" -"@jest/types@30.2.0", "@jest/types@^30.0.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" - integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== - dependencies: - "@jest/pattern" "30.0.1" - "@jest/schemas" "30.0.5" - "@types/istanbul-lib-coverage" "^2.0.6" - "@types/istanbul-reports" "^3.0.4" - "@types/node" "*" - "@types/yargs" "^17.0.33" - chalk "^4.1.2" - "@jest/types@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: "@jest/schemas" "^29.6.3" @@ -2439,14 +2451,27 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^30.0.0", "@jest/types@30.2.0": + version "30.2.0" + resolved "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz" + integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== + dependencies: + "@jest/pattern" "30.0.1" + "@jest/schemas" "30.0.5" + "@types/istanbul-lib-coverage" "^2.0.6" + "@types/istanbul-reports" "^3.0.4" + "@types/node" "*" + "@types/yargs" "^17.0.33" + chalk "^4.1.2" + "@josephg/resolvable@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" + resolved "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz" integrity sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg== "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -2454,7 +2479,7 @@ "@jridgewell/remapping@^2.3.5": version "2.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -2462,50 +2487,57 @@ "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@js-sdsl/ordered-map@^4.4.2": version "4.4.2" - resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" + resolved "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz" integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== "@jsdevtools/ono@^7.1.3": version "7.1.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== "@jsdoc/salty@^0.2.1": version "0.2.9" - resolved "https://registry.yarnpkg.com/@jsdoc/salty/-/salty-0.2.9.tgz#4d8c147f7ca011532681ce86352a77a0178f1dec" + resolved "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz" integrity sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw== dependencies: lodash "^4.17.21" +"@lnflash/bridge-mcp@github:lnflash/bridge-mcp": + version "0.1.0" + resolved "git+ssh://git@github.com/lnflash/bridge-mcp.git#b2168e2b80d8935daf444732529bc51e757e6786" + dependencies: + "@modelcontextprotocol/sdk" "^1.0.0" + zod "^3.22.0" + "@mapbox/node-pre-gyp@^1.0.5": version "1.0.11" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz" integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== dependencies: detect-libc "^2.0.0" @@ -2520,7 +2552,7 @@ "@messageformat/core@^3.4.0": version "3.4.0" - resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.4.0.tgz#2814c23383dec7bddf535d54f2a03e410165ca9f" + resolved "https://registry.npmjs.org/@messageformat/core/-/core-3.4.0.tgz" integrity sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw== dependencies: "@messageformat/date-skeleton" "^1.0.0" @@ -2532,90 +2564,117 @@ "@messageformat/date-skeleton@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz#3bad068cbf5873d14592cfc7a73dd4d8615e2739" + resolved "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz" integrity sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A== "@messageformat/number-skeleton@^1.0.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz#e7c245c41a1b2722bc59dad68f4d454f761bc9b4" + resolved "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz" integrity sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg== "@messageformat/parser@^5.1.0": version "5.1.1" - resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.1.tgz#ca7d6c18e9f3f6b6bc984a465dac16da00106055" + resolved "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.1.tgz" integrity sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg== dependencies: moo "^0.5.1" "@messageformat/runtime@^3.0.1": version "3.0.1" - resolved "https://registry.yarnpkg.com/@messageformat/runtime/-/runtime-3.0.1.tgz#94d1f6c43265c28ef7aed98ecfcc0968c6c849ac" + resolved "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz" integrity sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg== dependencies: make-plural "^7.0.0" +"@modelcontextprotocol/sdk@^1.0.0": + version "1.25.3" + resolved "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz" + integrity sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ== + dependencies: + "@hono/node-server" "^1.19.9" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.0.1" + express-rate-limit "^7.5.0" + jose "^6.1.1" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.0" + "@mongodb-js/saslprep@^1.1.0", "@mongodb-js/saslprep@^1.3.0": version "1.3.2" - resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz#51e5cad2f24b8759702d9cc185da0a3ef3784bad" + resolved "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz" integrity sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg== dependencies: sparse-bitfield "^3.0.3" "@noble/ciphers@^0.5.1": version "0.5.3" - resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.5.3.tgz#48b536311587125e0d0c1535f73ec8375cd76b23" + resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz" integrity sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w== +"@noble/curves@~1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" + "@noble/curves@1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz" integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== dependencies: "@noble/hashes" "1.3.2" -"@noble/curves@~1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" - integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== - dependencies: - "@noble/hashes" "1.3.1" +"@noble/hashes@^1.2.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + +"@noble/hashes@~1.3.0": + version "1.3.3" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/hashes@~1.3.1": + version "1.3.3" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== "@noble/hashes@1.3.1": version "1.3.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== "@noble/hashes@1.3.2": version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@^1.2.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -2623,43 +2682,43 @@ "@one-ini/wasm@0.1.1": version "0.1.1" - resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" + resolved "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz" integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== -"@opentelemetry/api-logs@0.43.0": +"@opentelemetry/api-logs@>=0.39.1", "@opentelemetry/api-logs@0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.43.0.tgz#472dacbf91488ad6f6b9de39a6f5038b450bdbc6" + resolved "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.43.0.tgz" integrity sha512-0CXMOYPXgAdLM2OzVkiUfAL6QQwWVhnMfUXCqLsITY42FZ9TxAhZIHkoc4mfVxvPuXsBnRYGR8UQZX86p87z4A== dependencies: "@opentelemetry/api" "^1.0.0" -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.6.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" - integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== +"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.6.0", "@opentelemetry/api@>=1.0.0 <1.10.0", "@opentelemetry/api@>=1.0.0 <1.7.0", "@opentelemetry/api@>=1.3.0 <1.10.0", "@opentelemetry/api@>=1.3.0 <1.7.0", "@opentelemetry/api@>=1.4.0 <1.7.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.6.0.tgz" + integrity sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g== "@opentelemetry/context-async-hooks@1.30.1": version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz#4f76280691a742597fd0bf682982126857622948" + resolved "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz" integrity sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA== +"@opentelemetry/core@^1.17.0", "@opentelemetry/core@1.30.1": + version "1.30.1" + resolved "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz" + integrity sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.28.0" + "@opentelemetry/core@1.17.0": version "1.17.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.17.0.tgz#6a72425f5f953dc68b4c7c66d947c018173d30d2" + resolved "https://registry.npmjs.org/@opentelemetry/core/-/core-1.17.0.tgz" integrity sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg== dependencies: "@opentelemetry/semantic-conventions" "1.17.0" -"@opentelemetry/core@1.30.1", "@opentelemetry/core@^1.17.0": - version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.30.1.tgz#a0b468bb396358df801881709ea38299fc30ab27" - integrity sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ== - dependencies: - "@opentelemetry/semantic-conventions" "1.28.0" - "@opentelemetry/exporter-trace-otlp-http@^0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.43.0.tgz#4d0a3587a1d372d19c883d9a4720f6eeb7dc9671" + resolved "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.43.0.tgz" integrity sha512-X6RGl4RTWC13EBrFstAbTh4vKqVqf6afpvFcud9qYhvl2A53OZ5RTAQP+9MrAMhthiKQaftNsEDdB2/0Sq+Xkw== dependencies: "@opentelemetry/core" "1.17.0" @@ -2670,14 +2729,14 @@ "@opentelemetry/instrumentation-graphql@^0.35.1": version "0.35.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.35.2.tgz#67b0c173cff1191cfa66aa26f67c6752c365edf2" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.35.2.tgz" integrity sha512-lJv7BbHFK0ExwogdQMtVHfnWhCBMDQEz8KYvhShXfRPiSStU5aVwa3TmT0O00KiJFpATSKJNZMv1iZNHbF6z1g== dependencies: "@opentelemetry/instrumentation" "^0.44.0" "@opentelemetry/instrumentation-grpc@^0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.43.0.tgz#947f1b3430d28de2ab80ab8cc8b146489bbdf27b" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.43.0.tgz" integrity sha512-iiFDYCwSbI5qeW7m/b9CyPlqdt3accmuqDXuMuHq38WEgImP8AKeb0xpdVErRjQ93V4ATRaeM8Jiw2MAzvbBLw== dependencies: "@opentelemetry/instrumentation" "0.43.0" @@ -2685,7 +2744,7 @@ "@opentelemetry/instrumentation-http@^0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.43.0.tgz#c21cf8b407e26c912448b110f340ad1eb657a316" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.43.0.tgz" integrity sha512-Ho3IFQFuD0xmcVc0Uq9AvYvROSOuydn4XWRT/h/GO0VCwOeYz/WCwUJvRdS3m1B3AZ4iGJ0q/nhsATp2JX3/gA== dependencies: "@opentelemetry/core" "1.17.0" @@ -2695,7 +2754,7 @@ "@opentelemetry/instrumentation-ioredis@^0.35.1": version "0.35.3" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.35.3.tgz#2e94be10a0328b20652f85bc3ddfb49ede5365fa" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.35.3.tgz" integrity sha512-QIjSbC7JmbR3dVQtWg/PF5uOuawIyvD4KQIj5PJcYFfJQq0nZ+agiJ6KVjeK3rWPXbop6zGvU66d79r48j2rKA== dependencies: "@opentelemetry/instrumentation" "^0.45.1" @@ -2705,7 +2764,7 @@ "@opentelemetry/instrumentation-mongodb@^0.37.0": version "0.37.3" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.37.3.tgz#1a6690c4e8b7844b04db51a00ce9c75abd8300a6" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.37.3.tgz" integrity sha512-RraVhFVy+vD4nApxORSqnKTWJ+tQLn1w+ipAXwk2IWgcDDCurfaIg6xIYzowvmx7rSkP2phUF07kAcQXiPapMQ== dependencies: "@opentelemetry/instrumentation" "^0.45.1" @@ -2714,15 +2773,15 @@ "@opentelemetry/instrumentation-net@^0.32.1": version "0.32.5" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-net/-/instrumentation-net-0.32.5.tgz#09f84d7f19421179011ba2ec68b8d65e08c4b646" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.32.5.tgz" integrity sha512-omBLTXyJeCtBy1MzVi+IzUcpXiup90k0z3AGhNKbzro4RhpsdMpN9O+3zZCXCIHMuyifhy7z8w99wmvvFO/FCQ== dependencies: "@opentelemetry/instrumentation" "^0.46.0" "@opentelemetry/semantic-conventions" "^1.0.0" -"@opentelemetry/instrumentation@0.43.0", "@opentelemetry/instrumentation@^0.43.0": +"@opentelemetry/instrumentation@^0.43.0", "@opentelemetry/instrumentation@0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.43.0.tgz#749521415df03396f969bf42341fcb4acd2e9c7b" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.43.0.tgz" integrity sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ== dependencies: "@types/shimmer" "^1.0.2" @@ -2733,7 +2792,7 @@ "@opentelemetry/instrumentation@^0.44.0": version "0.44.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.44.0.tgz#194f16fc96671575b6bd73d3fadffb5aa4497e67" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.44.0.tgz" integrity sha512-B6OxJTRRCceAhhnPDBshyQO7K07/ltX3quOLu0icEvPK9QZ7r9P1y0RQX8O5DxB4vTv4URRkxkg+aFU/plNtQw== dependencies: "@types/shimmer" "^1.0.2" @@ -2744,7 +2803,7 @@ "@opentelemetry/instrumentation@^0.45.1": version "0.45.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.45.1.tgz#043ac90a889fae8a551c8d06e16be443984449af" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.45.1.tgz" integrity sha512-V1Cr0g8hSg35lpW3G/GYVZurrhHrQZJdmP68WyJ83f1FDn3iru+/Vnlto9kiOSm7PHhW+pZGdb9Fbv+mkQ31CA== dependencies: "@types/shimmer" "^1.0.2" @@ -2755,7 +2814,7 @@ "@opentelemetry/instrumentation@^0.46.0": version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.46.0.tgz#a8a252306f82e2eace489312798592a14eb9830e" + resolved "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.46.0.tgz" integrity sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw== dependencies: "@types/shimmer" "^1.0.2" @@ -2766,14 +2825,14 @@ "@opentelemetry/otlp-exporter-base@0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.43.0.tgz#3ac36e959897050ceba8838bb2af7145a9e73d2c" + resolved "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.43.0.tgz" integrity sha512-LXNtRFVuPRXB9q0qdvrLikQ3NtT9Jmv255Idryz3RJPhOh/Fa03sBASQoj3D55OH3xazmA90KFHfhJ/d8D8y4A== dependencies: "@opentelemetry/core" "1.17.0" "@opentelemetry/otlp-transformer@0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.43.0.tgz#d4b12ba52d5193a35840d9e1a5e646c4e7e06a5b" + resolved "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.43.0.tgz" integrity sha512-KXYmgzWdVBOD5NvPmGW1nEMJjyQ8gK3N8r6pi4HvmEhTp0v4T13qDSax4q0HfsqmbPJR355oqQSJUnu1dHNutw== dependencies: "@opentelemetry/api-logs" "0.43.0" @@ -2785,85 +2844,85 @@ "@opentelemetry/propagator-b3@1.30.1": version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz#b73321e5f30f062a9229887a4aa80c771107fdd2" + resolved "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz" integrity sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ== dependencies: "@opentelemetry/core" "1.30.1" "@opentelemetry/propagator-jaeger@1.30.1": version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz#c06c9dacbe818b80cfb13c4dbf0b57df1ad26b71" + resolved "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz" integrity sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg== dependencies: "@opentelemetry/core" "1.30.1" "@opentelemetry/redis-common@^0.36.1": version "0.36.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz#906ac8e4d804d4109f3ebd5c224ac988276fdc47" + resolved "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz" integrity sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g== +"@opentelemetry/resources@^1.17.0", "@opentelemetry/resources@1.30.1": + version "1.30.1" + resolved "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz" + integrity sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA== + dependencies: + "@opentelemetry/core" "1.30.1" + "@opentelemetry/semantic-conventions" "1.28.0" + "@opentelemetry/resources@1.17.0": version "1.17.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.17.0.tgz#ee29144cfd7d194c69698c8153dbadec7fe6819f" + resolved "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.17.0.tgz" integrity sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw== dependencies: "@opentelemetry/core" "1.17.0" "@opentelemetry/semantic-conventions" "1.17.0" -"@opentelemetry/resources@1.30.1", "@opentelemetry/resources@^1.17.0": - version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.30.1.tgz#a4eae17ebd96947fdc7a64f931ca4b71e18ce964" - integrity sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA== - dependencies: - "@opentelemetry/core" "1.30.1" - "@opentelemetry/semantic-conventions" "1.28.0" - "@opentelemetry/sdk-logs@0.43.0": version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.43.0.tgz#ad40803f0e8893d1839af3a32756d5f46f0f7bbe" + resolved "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.43.0.tgz" integrity sha512-JyJ2BBRKm37Mc4cSEhFmsMl5ASQn1dkGhEWzAAMSlhPtLRTv5PfvJwhR+Mboaic/eDLAlciwsgijq8IFlf6IgQ== dependencies: "@opentelemetry/core" "1.17.0" "@opentelemetry/resources" "1.17.0" +"@opentelemetry/sdk-metrics@^1.9.1": + version "1.30.1" + resolved "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz" + integrity sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog== + dependencies: + "@opentelemetry/core" "1.30.1" + "@opentelemetry/resources" "1.30.1" + "@opentelemetry/sdk-metrics@1.17.0": version "1.17.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.0.tgz#e51d39e0bb749780d17f9b1df12f0490438dec1a" + resolved "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.0.tgz" integrity sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w== dependencies: "@opentelemetry/core" "1.17.0" "@opentelemetry/resources" "1.17.0" lodash.merge "^4.6.2" -"@opentelemetry/sdk-metrics@^1.9.1": +"@opentelemetry/sdk-trace-base@^1.17.0", "@opentelemetry/sdk-trace-base@1.30.1": version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz#70e2bcd275b9df6e7e925e3fe53cfe71329b5fc8" - integrity sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog== + resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz" + integrity sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg== dependencies: "@opentelemetry/core" "1.30.1" "@opentelemetry/resources" "1.30.1" + "@opentelemetry/semantic-conventions" "1.28.0" "@opentelemetry/sdk-trace-base@1.17.0": version "1.17.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.0.tgz#05a21763c9efa72903c20b8930293cdde344b681" + resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.17.0.tgz" integrity sha512-2T5HA1/1iE36Q9eg6D4zYlC4Y4GcycI1J6NsHPKZY9oWfAxWsoYnRlkPfUqyY5XVtocCo/xHpnJvGNHwzT70oQ== dependencies: "@opentelemetry/core" "1.17.0" "@opentelemetry/resources" "1.17.0" "@opentelemetry/semantic-conventions" "1.17.0" -"@opentelemetry/sdk-trace-base@1.30.1", "@opentelemetry/sdk-trace-base@^1.17.0": - version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz#41a42234096dc98e8f454d24551fc80b816feb34" - integrity sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg== - dependencies: - "@opentelemetry/core" "1.30.1" - "@opentelemetry/resources" "1.30.1" - "@opentelemetry/semantic-conventions" "1.28.0" - "@opentelemetry/sdk-trace-node@^1.17.0": version "1.30.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz#bd7d68fcfb4d4ae76ea09810df9668b7dd09a2e5" + resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz" integrity sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ== dependencies: "@opentelemetry/context-async-hooks" "1.30.1" @@ -2873,43 +2932,41 @@ "@opentelemetry/sdk-trace-base" "1.30.1" semver "^7.5.2" +"@opentelemetry/semantic-conventions@^1.0.0", "@opentelemetry/semantic-conventions@^1.17.0": + version "1.37.0" + resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz" + integrity sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA== + "@opentelemetry/semantic-conventions@1.17.0": version "1.17.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.17.0.tgz#af10baa9f05ce1e64a14065fc138b5739bfb65f6" + resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.17.0.tgz" integrity sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA== "@opentelemetry/semantic-conventions@1.28.0": version "1.28.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz#337fb2bca0453d0726696e745f50064411f646d6" + resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz" integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA== -"@opentelemetry/semantic-conventions@^1.0.0", "@opentelemetry/semantic-conventions@^1.17.0": - version "1.37.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz#aa2b4fa0b910b66a050c5ddfcac1d262e91a321a" - integrity sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA== - "@ory/client@^1.2.6": - version "1.22.7" - resolved "https://registry.yarnpkg.com/@ory/client/-/client-1.22.7.tgz#f7941ff3254008b677a29ddfbe35d8cbbcb9238c" - integrity sha512-DOiZOXFAqG5d1AVgGhqkP3Hhvq9cf9b3EHJDXea6nSHGCg6isKbE/DFIYuAy220ymZ/J3IYYHG8k8CLje9VipA== + version "v1.22.7" dependencies: axios "^1.6.1" "@otplib/core@^12.0.1": version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/core/-/core-12.0.1.tgz#73720a8cedce211fe5b3f683cd5a9c098eaf0f8d" + resolved "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz" integrity sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA== "@otplib/plugin-crypto@^12.0.1": version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz#2b42c624227f4f9303c1c041fca399eddcbae25e" + resolved "https://registry.npmjs.org/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz" integrity sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g== dependencies: "@otplib/core" "^12.0.1" "@otplib/plugin-thirty-two@^12.0.1": version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz#5cc9b56e6e89f2a1fe4a2b38900ca4e11c87aa9e" + resolved "https://registry.npmjs.org/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz" integrity sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA== dependencies: "@otplib/core" "^12.0.1" @@ -2917,7 +2974,7 @@ "@otplib/preset-default@^12.0.1": version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/preset-default/-/preset-default-12.0.1.tgz#cb596553c08251e71b187ada4a2246ad2a3165ba" + resolved "https://registry.npmjs.org/@otplib/preset-default/-/preset-default-12.0.1.tgz" integrity sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ== dependencies: "@otplib/core" "^12.0.1" @@ -2926,81 +2983,21 @@ "@otplib/preset-v11@^12.0.1": version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/preset-v11/-/preset-v11-12.0.1.tgz#4c7266712e7230500b421ba89252963c838fc96d" + resolved "https://registry.npmjs.org/@otplib/preset-v11/-/preset-v11-12.0.1.tgz" integrity sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg== dependencies: "@otplib/core" "^12.0.1" "@otplib/plugin-crypto" "^12.0.1" "@otplib/plugin-thirty-two" "^12.0.1" -"@parcel/watcher-android-arm64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" - integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA== - "@parcel/watcher-darwin-arm64@2.5.1": version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67" + resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz" integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw== -"@parcel/watcher-darwin-x64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8" - integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg== - -"@parcel/watcher-freebsd-x64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b" - integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ== - -"@parcel/watcher-linux-arm-glibc@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1" - integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA== - -"@parcel/watcher-linux-arm-musl@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e" - integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q== - -"@parcel/watcher-linux-arm64-glibc@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30" - integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w== - -"@parcel/watcher-linux-arm64-musl@2.5.1": +"@parcel/watcher@^2.4.1": version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2" - integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg== - -"@parcel/watcher-linux-x64-glibc@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" - integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== - -"@parcel/watcher-linux-x64-musl@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee" - integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg== - -"@parcel/watcher-win32-arm64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243" - integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw== - -"@parcel/watcher-win32-ia32@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6" - integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ== - -"@parcel/watcher-win32-x64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947" - integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA== - -"@parcel/watcher@^2.4.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" + resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz" integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== dependencies: detect-libc "^1.0.3" @@ -3024,37 +3021,37 @@ "@pkgjs/parseargs@^0.11.0": version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@pkgr/core@^0.2.9": version "0.2.9" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: "@protobufjs/aspromise" "^1.1.1" @@ -3062,32 +3059,32 @@ "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== "@readme/better-ajv-errors@^2.0.0": version "2.4.0" - resolved "https://registry.yarnpkg.com/@readme/better-ajv-errors/-/better-ajv-errors-2.4.0.tgz#96d7361e0a02644f9d58871a2fa1e0c26b7b55bf" + resolved "https://registry.npmjs.org/@readme/better-ajv-errors/-/better-ajv-errors-2.4.0.tgz" integrity sha512-9WODaOAKSl/mU+MYNZ2aHCrkoRSvmQ+1YkLj589OEqqjOAhbn8j7Z+ilYoiTu/he6X63/clsxxAB4qny9/dDzg== dependencies: "@babel/code-frame" "^7.22.5" @@ -3099,17 +3096,17 @@ "@readme/data-urls@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@readme/data-urls/-/data-urls-1.0.1.tgz#2cfb4803cca06d3789778c1533b777748b6e2c99" + resolved "https://registry.npmjs.org/@readme/data-urls/-/data-urls-1.0.1.tgz" integrity sha512-FNP4ntG5rCgmrvQGoNH/Ljivc6jSWaaVeMuXneOyQ6oLuhm/NkysXJN3DnBrIsJUJbSae7qIs2QfPYnaropoHw== "@readme/http-status-codes@^7.2.0": version "7.2.0" - resolved "https://registry.yarnpkg.com/@readme/http-status-codes/-/http-status-codes-7.2.0.tgz#805d281346eb4c25d987d8b86e23b4dba116a96f" + resolved "https://registry.npmjs.org/@readme/http-status-codes/-/http-status-codes-7.2.0.tgz" integrity sha512-/dBh9qw3QhJYqlGwt2I+KUP/lQ6nytdCx3aq+GpMUhibLHF3O7fwoowNcTwlbnwtyJ+TJYTIIrp3oVUlRNx3fA== "@readme/json-schema-ref-parser@^1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@readme/json-schema-ref-parser/-/json-schema-ref-parser-1.2.0.tgz#8552cde8f8ecf455398c59aa6e2cf5ed2d0f3d31" + resolved "https://registry.npmjs.org/@readme/json-schema-ref-parser/-/json-schema-ref-parser-1.2.0.tgz" integrity sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA== dependencies: "@jsdevtools/ono" "^7.1.3" @@ -3119,12 +3116,12 @@ "@readme/oas-extensions@^17.0.1": version "17.0.1" - resolved "https://registry.yarnpkg.com/@readme/oas-extensions/-/oas-extensions-17.0.1.tgz#343cf6a36f44975a68a064798f1c698b90ff6bd6" + resolved "https://registry.npmjs.org/@readme/oas-extensions/-/oas-extensions-17.0.1.tgz" integrity sha512-PCU7WLz8TkbdxsiE4eQGvJYDYZQPiyLhXme3SvLboSmH+8G6AJPJ5OymzSAdlf5sXpSSoD2q3dTIou3Cb2DirQ== "@readme/oas-to-har@^20.0.2": version "20.1.1" - resolved "https://registry.yarnpkg.com/@readme/oas-to-har/-/oas-to-har-20.1.1.tgz#96e703ec181042f75019d5591102391eed5f1abc" + resolved "https://registry.npmjs.org/@readme/oas-to-har/-/oas-to-har-20.1.1.tgz" integrity sha512-rz8YpdZw+Jqrd8VQhQaYrzctkCAYdBldoQ5qDQyF9vGvq2lpA1yMvQPgKCJXfPGXH8Cm+NjLbunxnYabKQeKeA== dependencies: "@readme/data-urls" "^1.0.1" @@ -3135,7 +3132,7 @@ "@readme/openapi-parser@^2.4.0", "@readme/openapi-parser@^2.5.0": version "2.7.0" - resolved "https://registry.yarnpkg.com/@readme/openapi-parser/-/openapi-parser-2.7.0.tgz#727ef369b198ae85705350892eaad40c8a8cd40c" + resolved "https://registry.npmjs.org/@readme/openapi-parser/-/openapi-parser-2.7.0.tgz" integrity sha512-P8WSr8WTOxilnT89tcCRKWYsG/II4sAwt1a/DIWub8xTtkrG9cCBBy/IUcvc5X8oGWN82MwcTA3uEkDrXZd/7A== dependencies: "@apidevtools/swagger-methods" "^3.0.2" @@ -3149,12 +3146,12 @@ "@readme/openapi-schemas@^3.1.0": version "3.1.0" - resolved "https://registry.yarnpkg.com/@readme/openapi-schemas/-/openapi-schemas-3.1.0.tgz#5ff4b704af6a8b108f9d577fd87cf73e9e7b3178" + resolved "https://registry.npmjs.org/@readme/openapi-schemas/-/openapi-schemas-3.1.0.tgz" integrity sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw== "@readme/postman-to-openapi@^4.1.0": version "4.1.0" - resolved "https://registry.yarnpkg.com/@readme/postman-to-openapi/-/postman-to-openapi-4.1.0.tgz#ba40dd4374f74cf2112e23e031320ba2c3e0da44" + resolved "https://registry.npmjs.org/@readme/postman-to-openapi/-/postman-to-openapi-4.1.0.tgz" integrity sha512-VvV2Hzjskz01m8doSn7Ypt6cSZzgjnypVqXy1ipThbyYD6SGiM74VSePXykOODj/43Y2m6zeYedPk/ZLts/HvQ== dependencies: "@readme/http-status-codes" "^7.2.0" @@ -3166,27 +3163,27 @@ "@rtsao/scc@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@scure/base@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== - "@scure/base@^1.1.1": version "1.2.6" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz" integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== "@scure/base@~1.1.0": version "1.1.9" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz" integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== +"@scure/base@1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + "@scure/bip32@1.3.1": version "1.3.1" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.1.tgz#7248aea723667f98160f593d621c47e208ccbb10" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz" integrity sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A== dependencies: "@noble/curves" "~1.1.0" @@ -3195,7 +3192,7 @@ "@scure/bip39@1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz" integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== dependencies: "@noble/hashes" "~1.3.0" @@ -3203,7 +3200,7 @@ "@sendgrid/client@^8.1.5": version "8.1.6" - resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-8.1.6.tgz#b8e1a30e6e3d4b6e425d68e6c373047046a809ca" + resolved "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.6.tgz" integrity sha512-/BHu0hqwXNHr2aLhcXU7RmmlVqrdfrbY9KpaNj00KZHlVOVoRxRVrpOCabIB+91ISXJ6+mLM9vpaVUhK6TwBWA== dependencies: "@sendgrid/helpers" "^8.0.0" @@ -3211,14 +3208,14 @@ "@sendgrid/helpers@^8.0.0": version "8.0.0" - resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-8.0.0.tgz#f74bf9743bacafe4c8573be46166130c604c0fc1" + resolved "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz" integrity sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA== dependencies: deepmerge "^4.2.2" "@sendgrid/mail@^8.1.5": version "8.1.6" - resolved "https://registry.yarnpkg.com/@sendgrid/mail/-/mail-8.1.6.tgz#9c253c13d49867fdb6f7df1360643825236eef22" + resolved "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.6.tgz" integrity sha512-/ZqxUvKeEztU9drOoPC/8opEPOk+jLlB2q4+xpx6HVLq6aFu3pMpalkTpAQz8XfRfpLp8O25bh6pGPcHDCYpqg== dependencies: "@sendgrid/client" "^8.1.5" @@ -3226,31 +3223,31 @@ "@sinclair/typebox@^0.27.8": version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sinclair/typebox@^0.34.0": version "0.34.41" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.41.tgz#aa51a6c1946df2c5a11494a2cdb9318e026db16c" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz" integrity sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g== "@sinonjs/commons@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz" integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: "@sinonjs/commons" "^3.0.0" "@smithy/abort-controller@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.8.tgz#3bfd7a51acce88eaec9a65c3382542be9f3a053a" + resolved "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz" integrity sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw== dependencies: "@smithy/types" "^4.12.0" @@ -3258,7 +3255,7 @@ "@smithy/chunked-blob-reader-native@^4.2.1": version "4.2.1" - resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz#380266951d746b522b4ab2b16bfea6b451147b41" + resolved "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz" integrity sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ== dependencies: "@smithy/util-base64" "^4.3.0" @@ -3266,14 +3263,14 @@ "@smithy/chunked-blob-reader@^5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz#776fec5eaa5ab5fa70d0d0174b7402420b24559c" + resolved "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz" integrity sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA== dependencies: tslib "^2.6.2" "@smithy/config-resolver@^4.4.5", "@smithy/config-resolver@^4.4.6": version "4.4.6" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.6.tgz#bd7f65b3da93f37f1c97a399ade0124635c02297" + resolved "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz" integrity sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ== dependencies: "@smithy/node-config-provider" "^4.3.8" @@ -3285,7 +3282,7 @@ "@smithy/core@^3.20.3", "@smithy/core@^3.20.5": version "3.20.5" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.20.5.tgz#67cc50eefdec0f5f8dcdbe5b8caf7084185637d5" + resolved "https://registry.npmjs.org/@smithy/core/-/core-3.20.5.tgz" integrity sha512-0Tz77Td8ynHaowXfOdrD0F1IH4tgWGUhwmLwmpFyTbr+U9WHXNNp9u/k2VjBXGnSe7BwjBERRpXsokGTXzNjhA== dependencies: "@smithy/middleware-serde" "^4.2.9" @@ -3301,7 +3298,7 @@ "@smithy/credential-provider-imds@^4.2.7", "@smithy/credential-provider-imds@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz#b2f4bf759ab1c35c0dd00fa3470263c749ebf60f" + resolved "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz" integrity sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw== dependencies: "@smithy/node-config-provider" "^4.3.8" @@ -3312,7 +3309,7 @@ "@smithy/eventstream-codec@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz#2f431f4bac22e40aa6565189ea350c6fcb5efafd" + resolved "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz" integrity sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw== dependencies: "@aws-crypto/crc32" "5.2.0" @@ -3322,7 +3319,7 @@ "@smithy/eventstream-serde-browser@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz#04e2e1fad18e286d5595fbc0bff22e71251fca38" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz" integrity sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw== dependencies: "@smithy/eventstream-serde-universal" "^4.2.8" @@ -3331,7 +3328,7 @@ "@smithy/eventstream-serde-config-resolver@^4.3.7": version "4.3.8" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz#b913d23834c6ebf1646164893e1bec89dffe4f3b" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz" integrity sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ== dependencies: "@smithy/types" "^4.12.0" @@ -3339,7 +3336,7 @@ "@smithy/eventstream-serde-node@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz#5f2dfa2cbb30bf7564c8d8d82a9832e9313f5243" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz" integrity sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A== dependencies: "@smithy/eventstream-serde-universal" "^4.2.8" @@ -3348,7 +3345,7 @@ "@smithy/eventstream-serde-universal@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz#a62b389941c28a8c3ab44a0c8ba595447e0258a7" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz" integrity sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ== dependencies: "@smithy/eventstream-codec" "^4.2.8" @@ -3357,7 +3354,7 @@ "@smithy/fetch-http-handler@^5.3.8", "@smithy/fetch-http-handler@^5.3.9": version "5.3.9" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz#edfc9e90e0c7538c81e22e748d62c0066cc91d58" + resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz" integrity sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA== dependencies: "@smithy/protocol-http" "^5.3.8" @@ -3368,7 +3365,7 @@ "@smithy/hash-blob-browser@^4.2.8": version "4.2.9" - resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.9.tgz#4f8e19b12b5a1000b7292b30f5ee237d32216af3" + resolved "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.9.tgz" integrity sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg== dependencies: "@smithy/chunked-blob-reader" "^5.2.0" @@ -3378,7 +3375,7 @@ "@smithy/hash-node@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.8.tgz#c21eb055041716cd492dda3a109852a94b6d47bb" + resolved "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz" integrity sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA== dependencies: "@smithy/types" "^4.12.0" @@ -3388,7 +3385,7 @@ "@smithy/hash-stream-node@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-4.2.8.tgz#d541a31c714ac9c85ae9fec91559e81286707ddb" + resolved "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.8.tgz" integrity sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w== dependencies: "@smithy/types" "^4.12.0" @@ -3397,7 +3394,7 @@ "@smithy/invalid-dependency@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz#c578bc6d5540c877aaed5034b986b5f6bd896451" + resolved "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz" integrity sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ== dependencies: "@smithy/types" "^4.12.0" @@ -3405,21 +3402,21 @@ "@smithy/is-array-buffer@^2.2.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz" integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== dependencies: tslib "^2.6.2" "@smithy/is-array-buffer@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz#b0f874c43887d3ad44f472a0f3f961bcce0550c2" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz" integrity sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ== dependencies: tslib "^2.6.2" "@smithy/md5-js@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.2.8.tgz#d354dbf9aea7a580be97598a581e35eef324ce22" + resolved "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.8.tgz" integrity sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ== dependencies: "@smithy/types" "^4.12.0" @@ -3428,7 +3425,7 @@ "@smithy/middleware-content-length@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz#82c1df578fa70fe5800cf305b8788b9d2836a3e4" + resolved "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz" integrity sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A== dependencies: "@smithy/protocol-http" "^5.3.8" @@ -3437,7 +3434,7 @@ "@smithy/middleware-endpoint@^4.4.4", "@smithy/middleware-endpoint@^4.4.6": version "4.4.6" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.6.tgz#a6aea0b4486cf114923d688a7365732565aedf55" + resolved "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.6.tgz" integrity sha512-dpq3bHqbEOBqGBjRVHVFP3eUSPpX0BYtg1D5d5Irgk6orGGAuZfY22rC4sErhg+ZfY/Y0kPqm1XpAmDZg7DeuA== dependencies: "@smithy/core" "^3.20.5" @@ -3451,7 +3448,7 @@ "@smithy/middleware-retry@^4.4.20": version "4.4.22" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.22.tgz#2e8b681878f98ae0761dea5ad5c06eb05b93cb76" + resolved "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.22.tgz" integrity sha512-vwWDMaObSMjw6WCC/3Ae9G7uul5Sk95jr07CDk1gkIMpaDic0phPS1MpVAZ6+YkF7PAzRlpsDjxPwRlh/S11FQ== dependencies: "@smithy/node-config-provider" "^4.3.8" @@ -3466,7 +3463,7 @@ "@smithy/middleware-serde@^4.2.8", "@smithy/middleware-serde@^4.2.9": version "4.2.9" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz#fd9d9b02b265aef67c9a30f55c2a5038fc9ca791" + resolved "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz" integrity sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ== dependencies: "@smithy/protocol-http" "^5.3.8" @@ -3475,7 +3472,7 @@ "@smithy/middleware-stack@^4.2.7", "@smithy/middleware-stack@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz#4fa9cfaaa05f664c9bb15d45608f3cb4f6da2b76" + resolved "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz" integrity sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA== dependencies: "@smithy/types" "^4.12.0" @@ -3483,7 +3480,7 @@ "@smithy/node-config-provider@^4.3.7", "@smithy/node-config-provider@^4.3.8": version "4.3.8" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz#85a0683448262b2eb822f64c14278d4887526377" + resolved "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz" integrity sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg== dependencies: "@smithy/property-provider" "^4.2.8" @@ -3493,7 +3490,7 @@ "@smithy/node-http-handler@^4.4.7", "@smithy/node-http-handler@^4.4.8": version "4.4.8" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz#298cc148c812b9a79f0ebd75e82bdab9e6d0bbcd" + resolved "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz" integrity sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg== dependencies: "@smithy/abort-controller" "^4.2.8" @@ -3504,7 +3501,7 @@ "@smithy/property-provider@^4.2.7", "@smithy/property-provider@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.8.tgz#6e37b30923d2d31370c50ce303a4339020031472" + resolved "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz" integrity sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w== dependencies: "@smithy/types" "^4.12.0" @@ -3512,7 +3509,7 @@ "@smithy/protocol-http@^5.3.7", "@smithy/protocol-http@^5.3.8": version "5.3.8" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.8.tgz#0938f69a3c3673694c2f489a640fce468ce75006" + resolved "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz" integrity sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ== dependencies: "@smithy/types" "^4.12.0" @@ -3520,7 +3517,7 @@ "@smithy/querystring-builder@^4.2.7", "@smithy/querystring-builder@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz#2fa72d29eb1844a6a9933038bbbb14d6fe385e93" + resolved "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz" integrity sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw== dependencies: "@smithy/types" "^4.12.0" @@ -3529,7 +3526,7 @@ "@smithy/querystring-parser@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz#aa3f2456180ce70242e89018d0b1ebd4782a6347" + resolved "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz" integrity sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA== dependencies: "@smithy/types" "^4.12.0" @@ -3537,14 +3534,14 @@ "@smithy/service-error-classification@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz#6d89dbad4f4978d7b75a44af8c18c22455a16cdc" + resolved "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz" integrity sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ== dependencies: "@smithy/types" "^4.12.0" "@smithy/shared-ini-file-loader@^4.4.2", "@smithy/shared-ini-file-loader@^4.4.3": version "4.4.3" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz#6054215ecb3a6532b13aa49a9fbda640b63be50e" + resolved "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz" integrity sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg== dependencies: "@smithy/types" "^4.12.0" @@ -3552,7 +3549,7 @@ "@smithy/signature-v4@^5.3.7": version "5.3.8" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.8.tgz#796619b10b7cc9467d0625b0ebd263ae04fdfb76" + resolved "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz" integrity sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg== dependencies: "@smithy/is-array-buffer" "^4.2.0" @@ -3566,7 +3563,7 @@ "@smithy/smithy-client@^4.10.5", "@smithy/smithy-client@^4.10.7": version "4.10.7" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.10.7.tgz#cc0e07c7209d2a49d7d7a145abd4972f21b8a60b" + resolved "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.7.tgz" integrity sha512-Uznt0I9z3os3Z+8pbXrOSCTXCA6vrjyN7Ub+8l2pRDum44vLv8qw0qGVkJN0/tZBZotaEFHrDPKUoPNueTr5Vg== dependencies: "@smithy/core" "^3.20.5" @@ -3579,14 +3576,14 @@ "@smithy/types@^4.11.0", "@smithy/types@^4.12.0": version "4.12.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.12.0.tgz#55d2479080922bda516092dbf31916991d9c6fee" + resolved "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz" integrity sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw== dependencies: tslib "^2.6.2" "@smithy/url-parser@^4.2.7", "@smithy/url-parser@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.8.tgz#b44267cd704abe114abcd00580acdd9e4acc1177" + resolved "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz" integrity sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA== dependencies: "@smithy/querystring-parser" "^4.2.8" @@ -3595,7 +3592,7 @@ "@smithy/util-base64@^4.3.0": version "4.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.3.0.tgz#5e287b528793aa7363877c1a02cd880d2e76241d" + resolved "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz" integrity sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ== dependencies: "@smithy/util-buffer-from" "^4.2.0" @@ -3604,21 +3601,21 @@ "@smithy/util-body-length-browser@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz#04e9fc51ee7a3e7f648a4b4bcdf96c350cfa4d61" + resolved "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz" integrity sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg== dependencies: tslib "^2.6.2" "@smithy/util-body-length-node@^4.2.1": version "4.2.1" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz#79c8a5d18e010cce6c42d5cbaf6c1958523e6fec" + resolved "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz" integrity sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA== dependencies: tslib "^2.6.2" "@smithy/util-buffer-from@^2.2.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz" integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== dependencies: "@smithy/is-array-buffer" "^2.2.0" @@ -3626,7 +3623,7 @@ "@smithy/util-buffer-from@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz#7abd12c4991b546e7cee24d1e8b4bfaa35c68a9d" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz" integrity sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew== dependencies: "@smithy/is-array-buffer" "^4.2.0" @@ -3634,14 +3631,14 @@ "@smithy/util-config-provider@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz#2e4722937f8feda4dcb09672c59925a4e6286cfc" + resolved "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz" integrity sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q== dependencies: tslib "^2.6.2" "@smithy/util-defaults-mode-browser@^4.3.19": version "4.3.21" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.21.tgz#a575ea30d2b7758a66beeb8d14a6dc7cef496bfb" + resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.21.tgz" integrity sha512-DtmVJarzqtjghtGjCw/PFJolcJkP7GkZgy+hWTAN3YLXNH+IC82uMoMhFoC3ZtIz5mOgCm5+hOGi1wfhVYgrxw== dependencies: "@smithy/property-provider" "^4.2.8" @@ -3651,7 +3648,7 @@ "@smithy/util-defaults-mode-node@^4.2.22": version "4.2.24" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.24.tgz#5584bf82a6a92f263f3c1b9fe3654323fd287fea" + resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.24.tgz" integrity sha512-JelBDKPAVswVY666rezBvY6b0nF/v9TXjUbNwDNAyme7qqKYEX687wJv0uze8lBIZVbg30wlWnlYfVSjjpKYFA== dependencies: "@smithy/config-resolver" "^4.4.6" @@ -3664,7 +3661,7 @@ "@smithy/util-endpoints@^3.2.7", "@smithy/util-endpoints@^3.2.8": version "3.2.8" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz#5650bda2adac989ff2e562606088c5de3dcb1b36" + resolved "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz" integrity sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw== dependencies: "@smithy/node-config-provider" "^4.3.8" @@ -3673,14 +3670,14 @@ "@smithy/util-hex-encoding@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz#1c22ea3d1e2c3a81ff81c0a4f9c056a175068a7b" + resolved "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz" integrity sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw== dependencies: tslib "^2.6.2" "@smithy/util-middleware@^4.2.7", "@smithy/util-middleware@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.8.tgz#1da33f29a74c7ebd9e584813cb7e12881600a80a" + resolved "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz" integrity sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A== dependencies: "@smithy/types" "^4.12.0" @@ -3688,7 +3685,7 @@ "@smithy/util-retry@^4.2.7", "@smithy/util-retry@^4.2.8": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.8.tgz#23f3f47baf0681233fd0c37b259e60e268c73b11" + resolved "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz" integrity sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg== dependencies: "@smithy/service-error-classification" "^4.2.8" @@ -3697,7 +3694,7 @@ "@smithy/util-stream@^4.5.10", "@smithy/util-stream@^4.5.8": version "4.5.10" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.10.tgz#3a7b56f0bdc3833205f80fea67d8e76756ea055b" + resolved "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz" integrity sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g== dependencies: "@smithy/fetch-http-handler" "^5.3.9" @@ -3711,14 +3708,14 @@ "@smithy/util-uri-escape@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz#096a4cec537d108ac24a68a9c60bee73fc7e3a9e" + resolved "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz" integrity sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA== dependencies: tslib "^2.6.2" "@smithy/util-utf8@^2.0.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz" integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== dependencies: "@smithy/util-buffer-from" "^2.2.0" @@ -3726,7 +3723,7 @@ "@smithy/util-utf8@^4.2.0": version "4.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.2.0.tgz#8b19d1514f621c44a3a68151f3d43e51087fed9d" + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz" integrity sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw== dependencies: "@smithy/util-buffer-from" "^4.2.0" @@ -3734,7 +3731,7 @@ "@smithy/util-waiter@^4.2.7": version "4.2.8" - resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.2.8.tgz#35d7bd8b2be7a2ebc12d8c38a0818c501b73e928" + resolved "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.8.tgz" integrity sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg== dependencies: "@smithy/abort-controller" "^4.2.8" @@ -3743,69 +3740,24 @@ "@smithy/uuid@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@smithy/uuid/-/uuid-1.1.0.tgz#9fd09d3f91375eab94f478858123387df1cda987" + resolved "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz" integrity sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw== dependencies: tslib "^2.6.2" "@stablelib/base64@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/base64/-/base64-1.0.1.tgz#bdfc1c6d3a62d7a3b7bbc65b6cce1bb4561641be" + resolved "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz" integrity sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ== "@swc/core-darwin-arm64@1.3.85": version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.85.tgz#f92d3c2b7f85f7dc0707e4ec06e344c96b26b476" + resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.85.tgz" integrity sha512-jTikp+i4nO4Ofe6qGm4I3sFeebD1OvueBCHITux5tQKD6umN1c2z4CRGv6K49NIz/qEpUcdr6Qny6K+3yibVFQ== -"@swc/core-darwin-x64@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.85.tgz#ce623656ee427876423f07d898fbd7fa767d39bd" - integrity sha512-3uHYkjVU+2F+YbVYtq5rH0uCJIztFTALaS3mQEfQUZKXZ5/8jD5titTCRqFKtSlQg0CzaFZgsYsuqwYBmgN0mA== - -"@swc/core-linux-arm-gnueabihf@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.85.tgz#9eb958e3b2c4af0d208dc2d1e71270797605c71b" - integrity sha512-ouHzAHsFaEOkRuoTAOI/8n2m8BQAAnb4vr/xbMhhDOmix0lp5eNsW5Iac/EcJ2uG6B3n7P2K8oycj9SWkj+pfw== - -"@swc/core-linux-arm64-gnu@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.85.tgz#2932636d5414709c56b9a670ca804dc317201a41" - integrity sha512-/Z1CZOWiO+NqJEh1J20PIxQFHMH43upQJ1l7FJ5Z7+MyuYF8WkeJ7OSovau729pBR+38vvvccEJrMZIztfv7hQ== - -"@swc/core-linux-arm64-musl@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.85.tgz#3b312c57ac0b6861d87a7c0f7ff24e232eb792d5" - integrity sha512-gfh7CfKavi076dbMBTzfdawSGcYfZ4+1Q+8aRkSesqepKHcIWIJti8Cf3zB4a6CHNhJe+VN0Gb7DEfumydAm1w== - -"@swc/core-linux-x64-gnu@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.85.tgz#d9fd7ff6ba4e693274604926b82027cb4c2a3306" - integrity sha512-lWVqjHKzofb9q1qrBM4dLqO7CIisp08/xMS5Hz9DWex1gTc5F2b6yJO6Ceqwa256GMweJcdP6A5EvEFQAiZ5dg== - -"@swc/core-linux-x64-musl@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.85.tgz#0a80efac999fb125131385a8937227819549939f" - integrity sha512-EPJmlfqC05TUetnlErxNRyIp7Nc3B2w9abET6oQ/EgldeAeQnZ3M6svMViET/c2QSomgrU3rdP+Qcozkt62/4A== - -"@swc/core-win32-arm64-msvc@1.3.85": +"@swc/core@*", "@swc/core@^1.3.68", "@swc/core@>=1.2.50", "@swc/core@1.3.85": version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.85.tgz#9808ee0d35eeb315efa7b73edcca80001451c9f3" - integrity sha512-ibckJDZw8kNosciMexwk0z75ZyUhwtiFMV9rSBpup0opa7NNCUCoERCJ1e9LRyMdhsVUoLpZg/KZiHCdTw96hQ== - -"@swc/core-win32-ia32-msvc@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.85.tgz#9b85ba050f333199e88e54ba0dd7b72302af64be" - integrity sha512-hY4MpHGUVQHL1T2kgRXOigDho4DTIpVPYzJ4uyy8VQRbS7GzN5XtvdGP/fA4zp8+2BQjcig+6J7Y92SY15ouNQ== - -"@swc/core-win32-x64-msvc@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.85.tgz#5c7f3f366804b512cf26e1731d8714f0409f1204" - integrity sha512-ktxWOMFJ0iqKn6WUHtXqi4CS7xkyHmrRtjllGRuGqxmLmDX/HSOfuQ55Tm1KXKk5oHLacJkUbOSF2kBrpZ8dpg== - -"@swc/core@1.3.85": - version "1.3.85" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.85.tgz#36ce756854efa2320f601a2b3e1ca1096f8b3f16" + resolved "https://registry.npmjs.org/@swc/core/-/core-1.3.85.tgz" integrity sha512-qnoxp+2O0GtvRdYnXgR1v8J7iymGGYpx6f6yCK9KxipOZOjrlKILFANYlghQxZyPUfXwK++TFxfSlX4r9wK+kg== dependencies: "@swc/types" "^0.1.4" @@ -3823,12 +3775,12 @@ "@swc/counter@^0.1.3": version "0.1.3" - resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" + resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@swc/jest@^0.2.29": +"@swc/jest@^0.2.26", "@swc/jest@^0.2.29": version "0.2.39" - resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.39.tgz#482bee0adb0726fab1487a4f902a278ec563a6b7" + resolved "https://registry.npmjs.org/@swc/jest/-/jest-0.2.39.tgz" integrity sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA== dependencies: "@jest/create-cache-key-function" "^30.0.0" @@ -3837,24 +3789,24 @@ "@swc/types@^0.1.4": version "0.1.25" - resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.25.tgz#b517b2a60feb37dd933e542d93093719e4cf1078" + resolved "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz" integrity sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g== dependencies: "@swc/counter" "^0.1.3" "@t3-oss/env-core@^0.6.1": version "0.6.1" - resolved "https://registry.yarnpkg.com/@t3-oss/env-core/-/env-core-0.6.1.tgz#24d04c22f3e73732e69ac8ff8b13ed00732d20aa" + resolved "https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.6.1.tgz" integrity sha512-KQD7qEDJtkWIWWmTVjNvk0wnHpkvAQ6CRbUxbWMFNG/fiosBQDQvtRpBNu6USxBscJCoC4z6y7P9MN52/mLOzw== "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@ts-morph/common@~0.18.0": version "0.18.1" - resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" + resolved "https://registry.npmjs.org/@ts-morph/common/-/common-0.18.1.tgz" integrity sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA== dependencies: fast-glob "^3.2.12" @@ -3864,34 +3816,34 @@ "@tsconfig/node10@^1.0.7": version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz" integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/accepts@^1.3.5": version "1.3.7" - resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" + resolved "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz" integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ== dependencies: "@types/node" "*" "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.2": version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" @@ -3902,19 +3854,19 @@ "@types/babel__generator@*": version "7.27.0" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz" integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== dependencies: "@babel/types" "^7.0.0" "@types/babel__preset-env@^7.9.3": version "7.10.0" - resolved "https://registry.yarnpkg.com/@types/babel__preset-env/-/babel__preset-env-7.10.0.tgz#6db5aebb012af5da345c3508c79af41a803a0ef1" + resolved "https://registry.npmjs.org/@types/babel__preset-env/-/babel__preset-env-7.10.0.tgz" integrity sha512-LS8hRb/8TQir2f8W9/s5enDtrRS2F/6fsdkVw5ePHp6Q8SrSJHOGtWnP93ryaYMmg2du03vOsiGrl5mllz4uDA== "@types/babel__template@*": version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" @@ -3922,28 +3874,28 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz" integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== dependencies: "@babel/types" "^7.28.2" "@types/basic-auth@^1.1.4": version "1.1.8" - resolved "https://registry.yarnpkg.com/@types/basic-auth/-/basic-auth-1.1.8.tgz#ea235203c89e233faae66b99e03665747576e9e9" + resolved "https://registry.npmjs.org/@types/basic-auth/-/basic-auth-1.1.8.tgz" integrity sha512-dKcUeixGuZn8pBjcUrf1N7x5K6lWuKuwHHitM2IZ4vwZUDWEhhNtwCWiba8jTA9zn0GQQ+fTFkWpKx8pOU/enw== dependencies: "@types/node" "*" "@types/bn.js@^4.11.3": version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz" integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== dependencies: "@types/node" "*" "@types/body-parser@*": version "1.19.6" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz" integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== dependencies: "@types/connect" "*" @@ -3951,7 +3903,7 @@ "@types/body-parser@1.19.2": version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== dependencies: "@types/connect" "*" @@ -3959,53 +3911,53 @@ "@types/caseless@*": version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + resolved "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz" integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== "@types/concat-stream@^1.6.0": version "1.6.1" - resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74" + resolved "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz" integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA== dependencies: "@types/node" "*" "@types/connect@*": version "3.4.38" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz" integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" "@types/cookie-parser@^1.4.4": version "1.4.10" - resolved "https://registry.yarnpkg.com/@types/cookie-parser/-/cookie-parser-1.4.10.tgz#a045272a383a30597a01955d4f9c790018f214e4" + resolved "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz" integrity sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg== "@types/cookie@^0.5.2": version "0.5.4" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.4.tgz#7e70a20cd695bc48d46b08c2505874cd68b760e0" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.4.tgz" integrity sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA== "@types/cors@2.8.12": version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== "@types/dedent@^0.7.0": version "0.7.2" - resolved "https://registry.yarnpkg.com/@types/dedent/-/dedent-0.7.2.tgz#878b5286877ec68e68e89f16b1014411c8e10814" + resolved "https://registry.npmjs.org/@types/dedent/-/dedent-0.7.2.tgz" integrity sha512-kRiitIeUg1mPV9yH4VUJ/1uk2XjyANfeL8/7rH1tsjvHeO9PJLBHJIYsFWmAvmGj5u8rj+1TZx7PZzW2qLw3Lw== "@types/dotenv@^8.2.0": version "8.2.3" - resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-8.2.3.tgz#c97b3c5b2e97ff3873793a000999e86cd66ff354" + resolved "https://registry.npmjs.org/@types/dotenv/-/dotenv-8.2.3.tgz" integrity sha512-g2FXjlDX/cYuc5CiQvyU/6kkbP1JtmGzh0obW50zD7OKeILVL0NSpPWLXVfqoAGQjom2/SLLx9zHq0KXvD6mbw== dependencies: dotenv "*" -"@types/eslint@^8.44.2": +"@types/eslint@^8.44.2", "@types/eslint@>=8.0.0": version "8.56.12" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz" integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g== dependencies: "@types/estree" "*" @@ -4013,21 +3965,22 @@ "@types/estree@*": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@types/express-serve-static-core@4.17.31": - version "4.17.31" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" - integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== +"@types/express-serve-static-core@^4.17.18": + version "4.19.7" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz" + integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" + "@types/send" "*" -"@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.33": +"@types/express-serve-static-core@^4.17.33": version "4.19.7" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz#f1d306dcc03b1aafbfb6b4fe684cce8a31cffc10" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz" integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== dependencies: "@types/node" "*" @@ -4035,9 +3988,28 @@ "@types/range-parser" "*" "@types/send" "*" +"@types/express-serve-static-core@4.17.31": + version "4.17.31" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz" + integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.15", "@types/express@^4.17.20": + version "4.17.24" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.24.tgz" + integrity sha512-Mbrt4SRlXSTWryOnHAh2d4UQ/E7n9lZyGSi6KgX+4hkuL9soYbLOVXVhnk/ODp12YsGc95f4pOvqywJ6kngUwg== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/express@4.17.14": version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz" integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== dependencies: "@types/body-parser" "*" @@ -4047,7 +4019,7 @@ "@types/express@4.17.18": version "4.17.18" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz" integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== dependencies: "@types/body-parser" "*" @@ -4055,86 +4027,76 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/express@^4.17.15", "@types/express@^4.17.20": - version "4.17.24" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.24.tgz#7d4be463143e9f2f146ef805a619076ce4aaec26" - integrity sha512-Mbrt4SRlXSTWryOnHAh2d4UQ/E7n9lZyGSi6KgX+4hkuL9soYbLOVXVhnk/ODp12YsGc95f4pOvqywJ6kngUwg== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - "@types/form-data@0.0.33": version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + resolved "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz" integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw== dependencies: "@types/node" "*" "@types/glob@*": version "9.0.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-9.0.0.tgz#7b942fafe09c55671912b34f04e8e4676faf32b1" + resolved "https://registry.npmjs.org/@types/glob/-/glob-9.0.0.tgz" integrity sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA== dependencies: glob "*" "@types/google-protobuf@^3.15.6": version "3.15.12" - resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.12.tgz#eb2ba0eddd65712211a2b455dc6071d665ccf49b" + resolved "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz" integrity sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ== "@types/graceful-fs@^4.1.3": version "4.1.9" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz" integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" "@types/har-format@^1.2.8": version "1.2.16" - resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.16.tgz#b71ede8681400cc08b3685f061c31e416cf94944" + resolved "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz" integrity sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A== "@types/http-errors@*": version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz" integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== "@types/i18n@^0.13.5": version "0.13.12" - resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.13.12.tgz#b457715766c63d8ffdfc51dd4fc1f72728e9d38f" + resolved "https://registry.npmjs.org/@types/i18n/-/i18n-0.13.12.tgz" integrity sha512-iAd2QjKh+0ToBXocmCS3m38GskiaGzmSV1MTQz2GaOraqSqBiLf46J7u3EGINl+st+Uk4lO3OL7QyIjTJlrWIg== "@types/ioredis4@npm:@types/ioredis@^4.28.10": version "4.28.10" - resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff" + resolved "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz" integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ== dependencies: "@types/node" "*" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1", "@types/istanbul-lib-coverage@^2.0.6": version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + 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== "@types/istanbul-lib-report@*": version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + 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== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0", "@types/istanbul-reports@^3.0.4": version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@^29.5.5": version "29.5.14" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz" integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" @@ -4142,22 +4104,22 @@ "@types/js-yaml@^4.0.6": version "4.0.9" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" + resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz" integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== "@types/json-schema@*", "@types/json-schema@^7.0.11", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.9": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/jsonwebtoken@^9", "@types/jsonwebtoken@^9.0.3", "@types/jsonwebtoken@^9.0.4": version "9.0.10" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz#a7932a47177dcd4283b6146f3bd5c26d82647f09" + resolved "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz" integrity sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA== dependencies: "@types/ms" "*" @@ -4165,85 +4127,85 @@ "@types/linkify-it@^5": version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" + resolved "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz" integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== "@types/lodash.debounce@^4.0.7": version "4.0.9" - resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz#0f5f21c507bce7521b5e30e7a24440975ac860a5" + resolved "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz" integrity sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ== dependencies: "@types/lodash" "*" "@types/lodash.difference@^4.5.6": version "4.5.9" - resolved "https://registry.yarnpkg.com/@types/lodash.difference/-/lodash.difference-4.5.9.tgz#69e82a68557779abac489585f43159997cab874c" + resolved "https://registry.npmjs.org/@types/lodash.difference/-/lodash.difference-4.5.9.tgz" integrity sha512-MNlajcjtwzLpXk+cw38UkBvEXJNEPhULgS8A4EHwtUwT7f7yFH/SFKD0iw5Rfilwh60yJIgFo0vsMr7xsa5+aw== dependencies: "@types/lodash" "*" "@types/lodash.find@^4.6.6": version "4.6.9" - resolved "https://registry.yarnpkg.com/@types/lodash.find/-/lodash.find-4.6.9.tgz#9a1b7001a019b48b8978549f6b4fe24f8fa7734f" + resolved "https://registry.npmjs.org/@types/lodash.find/-/lodash.find-4.6.9.tgz" integrity sha512-v1qcNZs0ksl0+BO+aflReb8oIDHLc+rSc4n+RYZ3pMaxIFw2/mcW/wlTYFqrh+8dY1ZqRbX22ytFfUgs4sAXtw== dependencies: "@types/lodash" "*" "@types/lodash.groupby@^4.6.6": version "4.6.9" - resolved "https://registry.yarnpkg.com/@types/lodash.groupby/-/lodash.groupby-4.6.9.tgz#ea1aa9da1038ca50894d1fe1a3b5dabdf865d99c" + resolved "https://registry.npmjs.org/@types/lodash.groupby/-/lodash.groupby-4.6.9.tgz" integrity sha512-z2xtCX2ko7GrqORnnYea4+ksT7jZNAvaOcLd6mP9M7J09RHvJs06W8BGdQQAX8ARef09VQLdeRilSOcfHlDQJQ== dependencies: "@types/lodash" "*" "@types/lodash.map@^4.6.13": version "4.6.13" - resolved "https://registry.yarnpkg.com/@types/lodash.map/-/lodash.map-4.6.13.tgz#7d776611d4c0345e48cfdfe466d7b291b31d1d13" + resolved "https://registry.npmjs.org/@types/lodash.map/-/lodash.map-4.6.13.tgz" integrity sha512-kppRBzlpuvQQsr7R2nv/DDDZds8fglRFNAK70WUOkOC18KOcuQ22oQF9Kgy5Z2v/eDNkBm0ltrT6FThSkuWwow== dependencies: "@types/lodash" "*" "@types/lodash.mapvalues@^4.6.6": version "4.6.9" - resolved "https://registry.yarnpkg.com/@types/lodash.mapvalues/-/lodash.mapvalues-4.6.9.tgz#1edb4b1d299db332166b474221b06058b34030a7" + resolved "https://registry.npmjs.org/@types/lodash.mapvalues/-/lodash.mapvalues-4.6.9.tgz" integrity sha512-NyAIgUrI+nnr3VoJbiAlUfqBT2M/65mOCm+LerHgYE7lEyxXUAalZiMIL37GBnfg0QOMMBEPW4osdiMjsoEA4g== dependencies: "@types/lodash" "*" "@types/lodash.mergewith@^4.6.7": version "4.6.9" - resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.9.tgz#7093028a36de3cae4495d03b9d92c351cab1f8bf" + resolved "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.9.tgz" integrity sha512-fgkoCAOF47K7sxrQ7Mlud2TH023itugZs2bUg8h/KzT+BnZNrR2jAOmaokbLunHNnobXVWOezAeNn/lZqwxkcw== dependencies: "@types/lodash" "*" "@types/lodash.sortby@^4.7.6": version "4.7.9" - resolved "https://registry.yarnpkg.com/@types/lodash.sortby/-/lodash.sortby-4.7.9.tgz#e4af00e03daece7a939378ff64f0d44e638d7db6" + resolved "https://registry.npmjs.org/@types/lodash.sortby/-/lodash.sortby-4.7.9.tgz" integrity sha512-PDmjHnOlndLS59GofH0pnxIs+n9i4CWeXGErSB5JyNFHu2cmvW6mQOaUKjG8EDPkni14IgF8NsRW8bKvFzTm9A== dependencies: "@types/lodash" "*" "@types/lodash.sumby@^4.6.6": version "4.6.9" - resolved "https://registry.yarnpkg.com/@types/lodash.sumby/-/lodash.sumby-4.6.9.tgz#25cb59789b1373ade3c2505a957b721f4880a113" + resolved "https://registry.npmjs.org/@types/lodash.sumby/-/lodash.sumby-4.6.9.tgz" integrity sha512-rqtOxrmIWuYpxjq867H+mptnAfN1levAPwpiadV+wJgRBXBLUTJT5rDlDUPxMNkWtIdBTv8mE+U4u86rSVoc/Q== dependencies: "@types/lodash" "*" "@types/lodash@*", "@types/lodash@^4.14.175": version "4.17.20" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz" integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== "@types/long@^4.0.0": version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== -"@types/markdown-it@^14.1.1": +"@types/markdown-it@*", "@types/markdown-it@^14.1.1": version "14.1.2" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61" + resolved "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz" integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== dependencies: "@types/linkify-it" "^5" @@ -4251,12 +4213,12 @@ "@types/mdurl@^2": version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" + resolved "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz" integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== "@types/migrate-mongo@^10.0.0": version "10.0.5" - resolved "https://registry.yarnpkg.com/@types/migrate-mongo/-/migrate-mongo-10.0.5.tgz#adba2cce3678b047f551c98d2395039266b70fc8" + resolved "https://registry.npmjs.org/@types/migrate-mongo/-/migrate-mongo-10.0.5.tgz" integrity sha512-QudWxGV0QJGGyCUk0nScHY+3tf116jsWdx6DdlGdU1IpcOo0sC6abeZUKZUUBTFJjVuwfSsA59ECGa4pN43TDQ== dependencies: "@types/node" "*" @@ -4264,75 +4226,87 @@ "@types/mime@^1": version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== "@types/ms@*": version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" + resolved "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz" integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== "@types/node-jose@^1.1.10": version "1.1.13" - resolved "https://registry.yarnpkg.com/@types/node-jose/-/node-jose-1.1.13.tgz#aeb9f5894afa8ae6c13182ca99d92f4bcc22f70f" + resolved "https://registry.npmjs.org/@types/node-jose/-/node-jose-1.1.13.tgz" integrity sha512-QjMd4yhwy1EvSToQn0YI3cD29YhyfxFwj7NecuymjLys2/P0FwxWnkgBlFxCai6Y3aBCe7rbwmqwJJawxlgcXw== dependencies: "@types/node" "*" -"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "24.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.9.1.tgz#b7360b3c789089e57e192695a855aa4f6981a53c" - integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== +"@types/node@*", "@types/node@^20.6.2": + version "20.19.23" + resolved "https://registry.npmjs.org/@types/node/-/node-20.19.23.tgz" + integrity sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ== dependencies: - undici-types "~7.16.0" - -"@types/node@20.7.1": - version "20.7.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.1.tgz#06d732ead0bd5ad978ef0ea9cbdeb24dc8717514" - integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== + undici-types "~6.21.0" -"@types/node@^10.0.3", "@types/node@^10.1.0": +"@types/node@^10.0.3": version "10.17.60" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^20.6.2": - version "20.19.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.23.tgz#7de99389c814071cca78656a3243f224fed7453d" - integrity sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ== - dependencies: - undici-types "~6.21.0" +"@types/node@^10.1.0": + version "10.17.60" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^8.0.0": version "8.10.66" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" + resolved "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz" integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== +"@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "24.9.1" + resolved "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz" + integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== + dependencies: + undici-types "~7.16.0" + +"@types/node@>=13.7.0": + version "24.9.1" + resolved "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz" + integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== + dependencies: + undici-types "~7.16.0" + +"@types/node@20.7.1": + version "20.7.1" + resolved "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz" + integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== + "@types/nodemon@^1.19.2": version "1.19.6" - resolved "https://registry.yarnpkg.com/@types/nodemon/-/nodemon-1.19.6.tgz#1c14bac51dfd3d354e2b5046949f925a742412c4" + resolved "https://registry.npmjs.org/@types/nodemon/-/nodemon-1.19.6.tgz" integrity sha512-vjKuaQOLUA5EY2zkUmWG1ipXbKt9Wd+H/0SiIuHVeH4cHtt6509iRUGH9ZR0iqgUrtj3BrP9KqoTuV3ZCbQcYA== dependencies: "@types/node" "*" "@types/prop-types@*": version "15.7.15" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz" integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== "@types/qs@*", "@types/qs@^6.2.31": version "6.14.0" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz" integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== "@types/range-parser@*": version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react@^18.2.21": +"@types/react@*", "@types/react@^18.2.21": version "18.3.26" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.26.tgz#4c5970878d30db3d2a0bca1e4eb5f258e391bbeb" + resolved "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz" integrity sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA== dependencies: "@types/prop-types" "*" @@ -4340,34 +4314,34 @@ "@types/request-ip@^0.0.41": version "0.0.41" - resolved "https://registry.yarnpkg.com/@types/request-ip/-/request-ip-0.0.41.tgz#c22a3244df2573402989346062851b06b7a5ac4e" + resolved "https://registry.npmjs.org/@types/request-ip/-/request-ip-0.0.41.tgz" integrity sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg== dependencies: "@types/node" "*" -"@types/request@2.48.9": - version "2.48.9" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.9.tgz#43e123e93b10bd740a298e25430de6fab7e726c2" - integrity sha512-4mi2hYsvPAhe8RXjk5DKB09sAUzbK68T2XjORehHdWyxFoX2zUnfi1VQ5wU4Md28H/5+uB4DkxY9BS4B87N/0A== +"@types/request@^2.48.8": + version "2.48.13" + resolved "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz" + integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== dependencies: "@types/caseless" "*" "@types/node" "*" "@types/tough-cookie" "*" - form-data "^2.5.0" + form-data "^2.5.5" -"@types/request@^2.48.8": - version "2.48.13" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.13.tgz#abdf4256524e801ea8fdda54320f083edb5a6b80" - integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== +"@types/request@2.48.9": + version "2.48.9" + resolved "https://registry.npmjs.org/@types/request/-/request-2.48.9.tgz" + integrity sha512-4mi2hYsvPAhe8RXjk5DKB09sAUzbK68T2XjORehHdWyxFoX2zUnfi1VQ5wU4Md28H/5+uB4DkxY9BS4B87N/0A== dependencies: "@types/caseless" "*" "@types/node" "*" "@types/tough-cookie" "*" - form-data "^2.5.5" + form-data "^2.5.0" "@types/rimraf@^3.0.2": version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" + resolved "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ== dependencies: "@types/glob" "*" @@ -4375,19 +4349,19 @@ "@types/semver@^7.3.12", "@types/semver@^7.5.0": version "7.7.1" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz" integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== "@types/send@*": version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/send/-/send-1.2.1.tgz#6a784e45543c18c774c049bff6d3dbaf045c9c74" + resolved "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz" integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== dependencies: "@types/node" "*" "@types/send@<1": version "0.17.6" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.6.tgz#aeb5385be62ff58a52cd5459daa509ae91651d25" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz" integrity sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og== dependencies: "@types/mime" "^1" @@ -4395,7 +4369,7 @@ "@types/serve-static@*": version "1.15.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.10.tgz#768169145a778f8f5dfcb6360aead414a3994fee" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz" integrity sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw== dependencies: "@types/http-errors" "*" @@ -4404,51 +4378,51 @@ "@types/set-cookie-parser@^2.4.2": version "2.4.10" - resolved "https://registry.yarnpkg.com/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz#ad3a807d6d921db9720621ea3374c5d92020bcbc" + resolved "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz" integrity sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw== dependencies: "@types/node" "*" "@types/shimmer@^1.0.2": version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.2.0.tgz#9b706af96fa06416828842397a70dfbbf1c14ded" + resolved "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz" integrity sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg== "@types/stack-utils@^2.0.0": version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/strip-bom@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + resolved "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ== "@types/strip-json-comments@0.0.30": version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + resolved "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== "@types/tough-cookie@*": version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== "@types/webidl-conversions@*": version "7.0.3" - resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859" + resolved "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz" integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA== "@types/whatwg-url@^11.0.2": version "11.0.5" - resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.5.tgz#aaa2546e60f0c99209ca13360c32c78caf2c409f" + resolved "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz" integrity sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ== dependencies: "@types/webidl-conversions" "*" "@types/whatwg-url@^8.2.1": version "8.2.2" - resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63" + resolved "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz" integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA== dependencies: "@types/node" "*" @@ -4456,36 +4430,36 @@ "@types/ws@8.5.6": version "8.5.6" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.6.tgz#e9ad51f0ab79b9110c50916c9fcbddc36d373065" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.6.tgz" integrity sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.33", "@types/yargs@^17.0.8": version "17.0.34" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.34.tgz#1c2f9635b71d5401827373a01ce2e8a7670ea839" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz" integrity sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A== dependencies: "@types/yargs-parser" "*" "@types/yup@0.29.13": version "0.29.13" - resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.13.tgz#21b137ba60841307a3c8a1050d3bf4e63ad561e9" + resolved "https://registry.npmjs.org/@types/yup/-/yup-0.29.13.tgz" integrity sha512-qRyuv+P/1t1JK1rA+elmK1MmCL1BapEzKKfbEhDBV/LMMse4lmhZ/XbgETI39JveDJRpLjmToOI6uFtMW/WR2g== "@types/zen-observable@0.8.3": version "0.8.3" - resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" + resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz" integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== -"@typescript-eslint/eslint-plugin@^6.7.0": +"@typescript-eslint/eslint-plugin@^5.0.0 || ^6.0.0 || ^7.0.0", "@typescript-eslint/eslint-plugin@^6.7.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz" integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== dependencies: "@eslint-community/regexpp" "^4.5.1" @@ -4500,9 +4474,9 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^6.7.2": +"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.7.2": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz" integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== dependencies: "@typescript-eslint/scope-manager" "6.21.0" @@ -4513,7 +4487,7 @@ "@typescript-eslint/scope-manager@5.62.0": version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz" integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: "@typescript-eslint/types" "5.62.0" @@ -4521,7 +4495,7 @@ "@typescript-eslint/scope-manager@6.21.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz" integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== dependencies: "@typescript-eslint/types" "6.21.0" @@ -4529,7 +4503,7 @@ "@typescript-eslint/type-utils@6.21.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz" integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== dependencies: "@typescript-eslint/typescript-estree" "6.21.0" @@ -4539,22 +4513,48 @@ "@typescript-eslint/types@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz" integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== "@typescript-eslint/types@5.62.0": version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== "@typescript-eslint/types@6.21.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/typescript-estree@5.62.0", "@typescript-eslint/typescript-estree@^5.55.0": +"@typescript-eslint/typescript-estree@^4.33.0": + version "4.33.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@^5.55.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz" integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: "@typescript-eslint/types" "5.62.0" @@ -4567,7 +4567,7 @@ "@typescript-eslint/typescript-estree@6.21.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz" integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== dependencies: "@typescript-eslint/types" "6.21.0" @@ -4579,22 +4579,23 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@^4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== +"@typescript-eslint/utils@^5.10.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" "@typescript-eslint/utils@6.21.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz" integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" @@ -4605,23 +4606,9 @@ "@typescript-eslint/typescript-estree" "6.21.0" semver "^7.5.4" -"@typescript-eslint/utils@^5.10.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - "@typescript-eslint/visitor-keys@4.33.0": version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz" integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== dependencies: "@typescript-eslint/types" "4.33.0" @@ -4629,7 +4616,7 @@ "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz" integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: "@typescript-eslint/types" "5.62.0" @@ -4637,7 +4624,7 @@ "@typescript-eslint/visitor-keys@6.21.0": version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz" integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== dependencies: "@typescript-eslint/types" "6.21.0" @@ -4645,131 +4632,146 @@ "@ungap/structured-clone@^1.2.0", "@ungap/structured-clone@^1.3.0": version "1.3.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== "@whatwg-node/promise-helpers@^1.0.0": version "1.3.2" - resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz#3b54987ad6517ef6db5920c66a6f0dada606587d" + resolved "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz" integrity sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA== dependencies: tslib "^2.6.3" "@wry/caches@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" + resolved "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz" integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== dependencies: tslib "^2.3.0" "@wry/context@^0.7.0", "@wry/context@^0.7.3": version "0.7.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" + resolved "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz" integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== dependencies: tslib "^2.3.0" "@wry/equality@^0.5.6": version "0.5.7" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" + resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz" integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== dependencies: tslib "^2.3.0" "@wry/trie@^0.4.3": version "0.4.3" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" + resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz" integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== dependencies: tslib "^2.3.0" "@wry/trie@^0.5.0": version "0.5.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" + resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz" integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== dependencies: tslib "^2.3.0" "@xascode/tscpaths@0.1.4": version "0.1.4" - resolved "https://registry.yarnpkg.com/@xascode/tscpaths/-/tscpaths-0.1.4.tgz#ad9a1a1d6afc8d816868b0f78b697bab80a56795" + resolved "https://registry.npmjs.org/@xascode/tscpaths/-/tscpaths-0.1.4.tgz" integrity sha512-EHgnpIoTfZhgl7r0l9FLvz3h57NHyJQkEr0whqGgBdnvuoVA1IeXSRyuIUW1L/XaGlVbpkrbBZNXLakRsfZg4Q== dependencies: commander "^8.2.0" globby "^12.0.2" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - abbrev@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz" integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== +abbrev@1: + version "1.1.1" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: mime-types "~2.1.34" negotiator "0.6.3" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-import-assertions@^1.9.0: version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: version "8.3.4" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== adm-zip@0.5.10: version "0.5.10" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.10.tgz#4a51d5ab544b1f5ce51e1b9043139b639afff45b" + resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz" integrity sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ== +agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + agent-base@6: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" -agent-base@^7.1.2: - version "7.1.4" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - ajv-draft-04@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8" + resolved "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz" integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw== +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -4777,9 +4779,9 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.12.0: +ajv@^8.0.0, ajv@^8.12.0, ajv@^8.17.1, ajv@^8.5.0, "ajv@4.11.8 - 8": version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: fast-deep-equal "^3.1.3" @@ -4789,56 +4791,56 @@ ajv@^8.12.0: ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== ansi-styles@^6.1.0: version "6.2.3" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== any-promise@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== anymatch@^3.0.3, anymatch@^3.1.3, anymatch@~3.1.2: version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" @@ -4846,7 +4848,7 @@ anymatch@^3.0.3, anymatch@^3.1.3, anymatch@~3.1.2: api@^6.1.1, api@^6.1.2: version "6.1.3" - resolved "https://registry.yarnpkg.com/api/-/api-6.1.3.tgz#2b10836d386b4bf60ca9a22611c9da7895b9a44a" + resolved "https://registry.npmjs.org/api/-/api-6.1.3.tgz" integrity sha512-a9Bk/CVzQUvGIDG8HR1sVyQdPd5gxzygoQEnzBhYWvSJOW5fIMGkGq+lXItM2vWAneJqWgFMqbBklYw9qMsz6A== dependencies: "@readme/oas-to-har" "^20.0.2" @@ -4880,7 +4882,7 @@ api@^6.1.1, api@^6.1.2: apollo-datasource@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-3.3.2.tgz#5711f8b38d4b7b53fb788cb4dbd4a6a526ea74c8" + resolved "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.2.tgz" integrity sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg== dependencies: "@apollo/utils.keyvaluecache" "^1.0.1" @@ -4888,14 +4890,14 @@ apollo-datasource@^3.3.2: apollo-reporting-protobuf@^3.4.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz#6edd31f09d4a3704d9e808d1db30eca2229ded26" + resolved "https://registry.npmjs.org/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz" integrity sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog== dependencies: "@apollo/protobufjs" "1.2.6" apollo-server-core@^3.12.1, apollo-server-core@^3.13.0: version "3.13.0" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-3.13.0.tgz#ad6601fbb34cc97eedca27a9fb0b5738d11cd27d" + resolved "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.13.0.tgz" integrity sha512-v/g6DR6KuHn9DYSdtQijz8dLOkP78I5JSVJzPkARhDbhpH74QNwrQ2PP2URAPPEDJ2EeZNQDX8PvbYkAKqg+kg== dependencies: "@apollo/utils.keyvaluecache" "^1.0.1" @@ -4924,19 +4926,19 @@ apollo-server-core@^3.12.1, apollo-server-core@^3.13.0: apollo-server-env@^4.2.1: version "4.2.1" - resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-4.2.1.tgz#ea5b1944accdbdba311f179e4dfaeca482c20185" + resolved "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-4.2.1.tgz" integrity sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g== dependencies: node-fetch "^2.6.7" apollo-server-errors@^3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz#ba5c00cdaa33d4cbd09779f8cb6f47475d1cd655" + resolved "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz" integrity sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA== apollo-server-express@^3.12.1: version "3.13.0" - resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-3.13.0.tgz#0d8d9bbba3b8b8264912d215f63fd44e74d5f42a" + resolved "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.13.0.tgz" integrity sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g== dependencies: "@types/accepts" "^1.3.5" @@ -4953,14 +4955,14 @@ apollo-server-express@^3.12.1: apollo-server-plugin-base@^3.7.2: version "3.7.2" - resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz#c19cd137bc4c993ba2490ba2b571b0f3ce60a0cd" + resolved "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz" integrity sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw== dependencies: apollo-server-types "^3.8.0" apollo-server-types@^3.8.0: version "3.8.0" - resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-3.8.0.tgz#d976b6967878681f715fe2b9e4dad9ba86b1346f" + resolved "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.8.0.tgz" integrity sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A== dependencies: "@apollo/utils.keyvaluecache" "^1.0.1" @@ -4970,17 +4972,17 @@ apollo-server-types@^3.8.0: app-module-path@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + resolved "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz" integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== "aproba@^1.0.3 || ^2.0.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.1.0.tgz#75500a190313d95c64e871e7e4284c6ac219f0b1" + resolved "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz" integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew== are-we-there-yet@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz" integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== dependencies: delegates "^1.0.0" @@ -4988,24 +4990,24 @@ are-we-there-yet@^2.0.0: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz" integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== dependencies: call-bound "^1.0.3" @@ -5013,17 +5015,17 @@ array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: array-each@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + resolved "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz" integrity sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA== array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== array-includes@^3.1.9: version "3.1.9" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz" integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== dependencies: call-bind "^1.0.8" @@ -5037,22 +5039,22 @@ array-includes@^3.1.9: array-slice@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + resolved "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz" integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-union@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" + resolved "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz" integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== array.prototype.findlastindex@^1.2.6: version "1.2.6" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz" integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== dependencies: call-bind "^1.0.8" @@ -5065,7 +5067,7 @@ array.prototype.findlastindex@^1.2.6: array.prototype.flat@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz" integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== dependencies: call-bind "^1.0.8" @@ -5075,7 +5077,7 @@ array.prototype.flat@^1.3.3: array.prototype.flatmap@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz" integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== dependencies: call-bind "^1.0.8" @@ -5085,7 +5087,7 @@ array.prototype.flatmap@^1.3.3: arraybuffer.prototype.slice@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz" integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== dependencies: array-buffer-byte-length "^1.0.1" @@ -5098,104 +5100,104 @@ arraybuffer.prototype.slice@^1.0.4: arrify@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== asap@~2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== assert@1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + resolved "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz" integrity sha512-N+aAxov+CKVS3JuhDIQFr24XvZvwE96Wlhk9dytTg/GmwWoghdOvR8dspx8MVz71O+Y0pA3UPqHF68D6iy8UvQ== dependencies: util "0.10.3" ast-module-types@^2.7.1: version "2.7.1" - resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3" + resolved "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz" integrity sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw== ast-module-types@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-3.0.0.tgz#9a6d8a80f438b6b8fe4995699d700297f398bf81" + resolved "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz" integrity sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ== ast-module-types@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-4.0.0.tgz#17e1cadd5b5b108e7295b0cf0cff21ccc226b639" + resolved "https://registry.npmjs.org/ast-module-types/-/ast-module-types-4.0.0.tgz" integrity sha512-Kd0o8r6CDazJGCRzs8Ivpn0xj19oNKrULhoJFzhGjRsLpekF2zyZs9Ukz+JvZhWD6smszfepakTFhAaYpsI12g== async-function@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + resolved "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== async-limiter@~1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async-redis@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/async-redis/-/async-redis-2.0.0.tgz#478704e48e2ae7a84a7cfd55ab946f463fba9cab" + resolved "https://registry.npmjs.org/async-redis/-/async-redis-2.0.0.tgz" integrity sha512-cGv40n9h3gpAGMGnX1v5ncgHJ2C3TsRmK6Ekt282jVftiky3v0X13PSfnLoRix9uva7ctb2j9lwA3d2qc4KKTA== dependencies: redis "3.1.2" async-retry@^1.2.1, async-retry@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz" integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== dependencies: retry "0.13.1" -async@3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - async@^2.6.0: version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + resolved "https://registry.npmjs.org/async/-/async-2.6.4.tgz" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" async@^3.2.0, async@^3.2.3, async@~3.2.0: version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== +async@3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + asyncjs-util@1.2.12: version "1.2.12" - resolved "https://registry.yarnpkg.com/asyncjs-util/-/asyncjs-util-1.2.12.tgz#425e6aa8d52e19d2144db288c2687c71019cbb78" + resolved "https://registry.npmjs.org/asyncjs-util/-/asyncjs-util-1.2.12.tgz" integrity sha512-ZuiV9aMltl2Db9AB+4h0esHOc642ktBivOhyV3ZHQ4nHwSPH7KiLIxiPTubO4cyx6aJ1Cb5ASJVHUiTk+6DVQQ== dependencies: async "3.2.4" asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== atomic-sleep@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== available-typed-arrays@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: possible-typed-array-names "^1.0.0" axios-mock-adapter@^1.22.0: version "1.22.0" - resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz#0f3e6be0fc9b55baab06f2d49c0b71157e7c053d" + resolved "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz" integrity sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw== dependencies: fast-deep-equal "^3.1.3" @@ -5203,21 +5205,21 @@ axios-mock-adapter@^1.22.0: axios-proxy-builder@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/axios-proxy-builder/-/axios-proxy-builder-0.1.2.tgz#1149ffd916d0817c665c0bff2d50eeb10afce5bf" + resolved "https://registry.npmjs.org/axios-proxy-builder/-/axios-proxy-builder-0.1.2.tgz" integrity sha512-6uBVsBZzkB3tCC8iyx59mCjQckhB8+GQrI9Cop8eC7ybIsvs/KtnNgEBfRMSEa7GqK2VBGUzgjNYMdPIfotyPA== dependencies: tunnel "^0.0.6" axios@^0.26.1: version "0.26.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + resolved "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz" integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== dependencies: follow-redirects "^1.14.8" -axios@^1.12.0, axios@^1.3.4, axios@^1.5.0, axios@^1.6.0, axios@^1.6.1: +axios@^1.12.0, axios@^1.3.4, axios@^1.5.0, axios@^1.6.0, axios@^1.6.1, "axios@>= 0.17.0": version "1.12.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7" + resolved "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz" integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw== dependencies: follow-redirects "^1.15.6" @@ -5226,7 +5228,7 @@ axios@^1.12.0, axios@^1.3.4, axios@^1.5.0, axios@^1.6.0, axios@^1.6.1: babel-jest@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: "@jest/transform" "^29.7.0" @@ -5239,7 +5241,7 @@ babel-jest@^29.7.0: babel-plugin-istanbul@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -5250,7 +5252,7 @@ babel-plugin-istanbul@^6.1.1: babel-plugin-istanbul@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz" integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -5261,7 +5263,7 @@ babel-plugin-istanbul@^7.0.1: babel-plugin-jest-hoist@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz" integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" @@ -5271,7 +5273,7 @@ babel-plugin-jest-hoist@^29.6.3: babel-plugin-polyfill-corejs2@^0.4.14: version "0.4.14" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz#8101b82b769c568835611542488d463395c2ef8f" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz" integrity sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg== dependencies: "@babel/compat-data" "^7.27.7" @@ -5280,7 +5282,7 @@ babel-plugin-polyfill-corejs2@^0.4.14: babel-plugin-polyfill-corejs3@^0.13.0: version "0.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz#bb7f6aeef7addff17f7602a08a6d19a128c30164" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz" integrity sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A== dependencies: "@babel/helper-define-polyfill-provider" "^0.6.5" @@ -5288,14 +5290,14 @@ babel-plugin-polyfill-corejs3@^0.13.0: babel-plugin-polyfill-regenerator@^0.6.5: version "0.6.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz#32752e38ab6f6767b92650347bf26a31b16ae8c5" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz" integrity sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg== dependencies: "@babel/helper-define-polyfill-provider" "^0.6.5" babel-preset-current-node-syntax@^1.0.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" + 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== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -5316,7 +5318,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-jest@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz" integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: babel-plugin-jest-hoist "^29.6.3" @@ -5324,71 +5326,71 @@ babel-preset-jest@^29.6.3: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base-x@^3.0.2: version "3.0.11" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.11.tgz#40d80e2a1aeacba29792ccc6c5354806421287ff" + resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz" integrity sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA== dependencies: safe-buffer "^5.0.1" base-x@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.1.tgz#817fb7b57143c501f649805cb247617ad016a885" + resolved "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz" integrity sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw== base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base64url@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== baseline-browser-mapping@^2.8.19: version "2.8.20" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz#6766cf270f3668d20b6712b9c54cc911b87da714" + resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz" integrity sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ== basic-auth@^2.0.1, basic-auth@~2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + resolved "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz" integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== dependencies: safe-buffer "5.1.2" batch@0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== -bech32@2.0.0, bech32@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" - integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== - bech32@^1.1.2: version "1.1.4" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== +bech32@^2.0.0, bech32@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + bignumber.js@^9.0.0: version "9.3.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.1.tgz#759c5aaddf2ffdc4f154f7b493e1c8770f88c4d7" + resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz" integrity sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ== binary-extensions@^2.0.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== binary-install@^1.0.6: version "1.1.2" - resolved "https://registry.yarnpkg.com/binary-install/-/binary-install-1.1.2.tgz#06f059e5475e2a208d65ead8cb523d7845a83038" + resolved "https://registry.npmjs.org/binary-install/-/binary-install-1.1.2.tgz" integrity sha512-ZS2cqFHPZOy4wLxvzqfQvDjCOifn+7uCPqNmYRIBM/03+yllON+4fNnsD0VJdW0p97y+E+dTRNPStWNqMBq+9g== dependencies: axios "^0.26.1" @@ -5397,17 +5399,17 @@ binary-install@^1.0.6: bintrees@1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" + resolved "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz" integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== bip174@^2.1.0, bip174@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.1.tgz#ef3e968cf76de234a546962bcf572cc150982f9f" + resolved "https://registry.npmjs.org/bip174/-/bip174-2.1.1.tgz" integrity sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ== bip32@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/bip32/-/bip32-4.0.0.tgz#7fac3c05072188d2d355a4d6596b37188f06aa2f" + resolved "https://registry.npmjs.org/bip32/-/bip32-4.0.0.tgz" integrity sha512-aOGy88DDlVUhspIXJN+dVEtclhIsfAUppD43V0j40cPTld3pv/0X/MlrZSZ6jowIaQQzFwP8M6rFU2z2mVYjDQ== dependencies: "@noble/hashes" "^1.2.0" @@ -5417,53 +5419,53 @@ bip32@^4.0.0: bip66@1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + resolved "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz" integrity sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw== dependencies: safe-buffer "^5.0.1" bitcoin-cli-ts@^24.0.3: version "24.1.0" - resolved "https://registry.yarnpkg.com/bitcoin-cli-ts/-/bitcoin-cli-ts-24.1.0.tgz#34910062645351c589cb21432ff5149a41845dc1" + resolved "https://registry.npmjs.org/bitcoin-cli-ts/-/bitcoin-cli-ts-24.1.0.tgz" integrity sha512-MpbgZPFgyK6icA+L40HJ2/1wRmoCavkc4pSM9oF5kcpqggxKd+FLdL6aqR47bfGfpc7cxODJeu3+E7JioajykQ== dependencies: json-bigint "^1.0.0" json-rpc-2.0 "^1.6.0" undici "^5.22.1" -bitcoin-ops@1.4.1, bitcoin-ops@^1.3.0: +bitcoin-ops@^1.3.0, bitcoin-ops@1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz#e45de620398e22fd4ca6023de43974ff42240278" + resolved "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz" integrity sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow== -bitcoinjs-lib@6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz#88aed5a8d052e9faa04c6402d3f0865441f928d7" - integrity sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw== +bitcoinjs-lib@^6.0.0, bitcoinjs-lib@^6.1.4: + version "6.1.7" + resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz" + integrity sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg== dependencies: "@noble/hashes" "^1.2.0" bech32 "^2.0.0" - bip174 "^2.1.0" + bip174 "^2.1.1" bs58check "^3.0.1" typeforce "^1.11.3" varuint-bitcoin "^1.1.2" -bitcoinjs-lib@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-6.1.5.tgz#3b03509ae7ddd80a440f10fc38c4a97f0a028d8c" - integrity sha512-yuf6xs9QX/E8LWE2aMJPNd0IxGofwfuVOiYdNUESkc+2bHHVKjhJd8qewqapeoolh9fihzHGoDCB5Vkr57RZCQ== +bitcoinjs-lib@6.1.3: + version "6.1.3" + resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz" + integrity sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw== dependencies: "@noble/hashes" "^1.2.0" bech32 "^2.0.0" - bip174 "^2.1.1" + bip174 "^2.1.0" bs58check "^3.0.1" typeforce "^1.11.3" varuint-bitcoin "^1.1.2" -bitcoinjs-lib@^6.0.0, bitcoinjs-lib@^6.1.4: - version "6.1.7" - resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz#0f98dec1333d658574eefa455295668cfae38bb0" - integrity sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg== +bitcoinjs-lib@6.1.5: + version "6.1.5" + resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.5.tgz" + integrity sha512-yuf6xs9QX/E8LWE2aMJPNd0IxGofwfuVOiYdNUESkc+2bHHVKjhJd8qewqapeoolh9fihzHGoDCB5Vkr57RZCQ== dependencies: "@noble/hashes" "^1.2.0" bech32 "^2.0.0" @@ -5474,7 +5476,7 @@ bitcoinjs-lib@^6.0.0, bitcoinjs-lib@^6.1.4: bl@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" @@ -5483,22 +5485,60 @@ bl@^4.1.0: bluebird@^3.7.2: version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bn.js@^4.11.8: + version "4.12.2" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" + integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== + +bn.js@^4.11.9: + version "4.12.2" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" + integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== + bn.js@5.2.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -bn.js@^4.11.8, bn.js@^4.11.9: - version "4.12.2" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.2.tgz#3d8fed6796c24e177737f7cc5172ee04ef39ec99" - integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== +body-parser@^1.19.0, body-parser@^1.20.1, body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" body-parser@1.20.1: version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: bytes "3.1.2" @@ -5516,7 +5556,7 @@ body-parser@1.20.1: body-parser@1.20.2: version "1.20.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== dependencies: bytes "3.1.2" @@ -5532,27 +5572,9 @@ body-parser@1.20.2: type-is "~1.6.18" unpipe "1.0.0" -body-parser@1.20.3, body-parser@^1.19.0, body-parser@^1.20.1: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - body@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" + resolved "https://registry.npmjs.org/body/-/body-5.1.0.tgz" integrity sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ== dependencies: continuable-cache "^0.3.1" @@ -5562,19 +5584,19 @@ body@^5.1.0: bolt07@1.8.4: version "1.8.4" - resolved "https://registry.yarnpkg.com/bolt07/-/bolt07-1.8.4.tgz#1c8be355c5620bce66e9ef435f26edd58b9bf59a" + resolved "https://registry.npmjs.org/bolt07/-/bolt07-1.8.4.tgz" integrity sha512-UyZRSYmVE8K++Jg1BiJrUkxQak03aS/s7ESKDsBdBPzaTlk2E09Y0JYa9HhWN7MRn48Y2K1doOzkb1Hn6XixZw== dependencies: bn.js "5.2.1" bolt09@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/bolt09/-/bolt09-1.0.0.tgz#5587866b7ac541ac395cea76582a85c4ae75adfe" + resolved "https://registry.npmjs.org/bolt09/-/bolt09-1.0.0.tgz" integrity sha512-J8wh6mRTNnYJuC43iSJRvM2Te0RtO4+Cn0JCgF6q2xWXKWjZjdPV5AGamD8R8C39/Ei6L0I780aFvIZu+bATWw== bolt11@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/bolt11/-/bolt11-1.4.1.tgz#4363041b8c9f477b7f42c12d96e771fec39a00f1" + resolved "https://registry.npmjs.org/bolt11/-/bolt11-1.4.1.tgz" integrity sha512-jR0Y+MO+CK2at1Cg5mltLJ+6tdOwNKoTS/DJOBDdzVkQ+R9D6UgZMayTWOsuzY7OgV1gEqlyT5Tzk6t6r4XcNQ== dependencies: "@types/bn.js" "^4.11.3" @@ -5588,17 +5610,17 @@ bolt11@^1.4.1: boolbase@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== bowser@^2.11.0: version "2.13.1" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.13.1.tgz#5a4c652de1d002f847dd011819f5fc729f308a7e" + resolved "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz" integrity sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw== brace-expansion@^1.1.7: version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz" integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: balanced-match "^1.0.0" @@ -5606,26 +5628,26 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" braces@^3.0.3, braces@~3.0.2: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" brorand@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.24.0, browserslist@^4.26.3: +browserslist@^4.24.0, browserslist@^4.26.3, "browserslist@>= 4.21.0": version "4.27.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.27.0.tgz#755654744feae978fbb123718b2f139bc0fa6697" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz" integrity sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw== dependencies: baseline-browser-mapping "^2.8.19" @@ -5636,65 +5658,65 @@ browserslist@^4.24.0, browserslist@^4.26.3: bs58@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + resolved "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz" integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== dependencies: base-x "^3.0.2" bs58@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + resolved "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz" integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== dependencies: base-x "^4.0.0" +bs58check@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz" + integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^5.0.0" + bs58check@<3.0.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== dependencies: bs58 "^4.0.0" create-hash "^1.1.0" safe-buffer "^5.1.2" -bs58check@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-3.0.1.tgz#2094d13720a28593de1cba1d8c4e48602fdd841c" - integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== - dependencies: - "@noble/hashes" "^1.2.0" - bs58 "^5.0.0" - bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" -bson@^5.4.0, bson@^5.5.0: +bson@^5.5.0: version "5.5.1" - resolved "https://registry.yarnpkg.com/bson/-/bson-5.5.1.tgz#f5849d405711a7f23acdda9a442375df858e6833" + resolved "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz" integrity sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g== bson@^6.10.4: version "6.10.4" - resolved "https://registry.yarnpkg.com/bson/-/bson-6.10.4.tgz#d530733bb5bb16fb25c162e01a3344fab332fd2b" + resolved "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz" integrity sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng== buffer-equal-constant-time@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -5702,25 +5724,25 @@ buffer@^5.5.0: buffer@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" ieee754 "^1.2.1" +bytes@^3.1.2, bytes@~3.1.2, bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + bytes@1: version "1.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" + resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" integrity sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ== -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: es-errors "^1.3.0" @@ -5728,7 +5750,7 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply- call-bind@^1.0.7, call-bind@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz" integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: call-bind-apply-helpers "^1.0.0" @@ -5738,7 +5760,7 @@ call-bind@^1.0.7, call-bind@^1.0.8: call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== dependencies: call-bind-apply-helpers "^1.0.2" @@ -5746,51 +5768,51 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: call-me-maybe@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" + resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz" integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001751: version "1.0.30001751" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz#dacd5d9f4baeea841641640139d2b2a4df4226ad" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz" integrity sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw== caseless@^0.12.0, caseless@~0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== catharsis@^0.9.0: version "0.9.0" - resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.9.0.tgz#40382a168be0e6da308c277d3a2b3eb40c7d2121" + resolved "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz" integrity sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A== dependencies: lodash "^4.17.15" cbor@9.0.1: version "9.0.1" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.1.tgz#b16e393d4948d44758cd54ac6151379d443b37ae" + resolved "https://registry.npmjs.org/cbor/-/cbor-9.0.1.tgz" integrity sha512-/TQOWyamDxvVIv+DY9cOLNuABkoyz8K/F3QE56539pGVYohx0+MEA1f4lChFTX79dBTBS7R1PF6ovH7G+VtBfQ== dependencies: nofilter "^3.1.0" chalk@^1.1.1: version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== dependencies: ansi-styles "^2.2.1" @@ -5801,7 +5823,7 @@ chalk@^1.1.1: chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2, chalk@~4.1.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -5809,12 +5831,12 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2, chalk@~4.1.0: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== cheerio-select@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz" integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== dependencies: boolbase "^1.0.0" @@ -5826,7 +5848,7 @@ cheerio-select@^2.1.0: cheerio@^1.0.0-rc.10: version "1.1.2" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.1.2.tgz#26af77e89336c81c63ea83197f868b4cbd351369" + resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz" integrity sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg== dependencies: cheerio-select "^2.1.0" @@ -5843,7 +5865,7 @@ cheerio@^1.0.0-rc.10: chokidar@^3.5.1, chokidar@^3.5.2: version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" @@ -5858,29 +5880,29 @@ chokidar@^3.5.1, chokidar@^3.5.2: chokidar@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz" integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== dependencies: readdirp "^4.0.1" chownr@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^3.2.0: version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== ci-info@^4.2.0: version "4.3.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz" integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== cipher-base@^1.0.1: version "1.0.7" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.7.tgz#bd094bfef42634ccfd9e13b9fc73274997111e39" + resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz" integrity sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA== dependencies: inherits "^2.0.4" @@ -5889,31 +5911,31 @@ cipher-base@^1.0.1: cjs-module-lexer@^1.0.0, cjs-module-lexer@^1.2.2: version "1.4.3" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz" integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== clean-css@^5.3.2: version "5.3.3" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz" integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== dependencies: source-map "~0.6.0" cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-spinners@^2.5.0: version "2.9.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== cli-table3@^0.6.1: version "0.6.5" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz" integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== dependencies: string-width "^4.2.0" @@ -5922,127 +5944,132 @@ cli-table3@^0.6.1: cliui@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone@2.x: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== - clone@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== +clone@2.x: + version "2.1.2" + resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== + cluster-key-slot@^1.1.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== co@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== code-block-writer@^11.0.3: version "11.0.3" - resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.3.tgz#9eec2993edfb79bfae845fbc093758c0a0b73b76" + resolved "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz" integrity sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw== coffeescript@^2.6.1: version "2.7.0" - resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-2.7.0.tgz#a43ec03be6885d6d1454850ea70b9409c391279c" + resolved "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz" integrity sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A== collect-v8-coverage@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz" integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-support@^1.1.2: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colorette@2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - colorette@^2.0.7: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colorette@2.0.19: + version "2.0.19" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + colors@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + resolved "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz" integrity sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^10.0.0: version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== commander@^2.16.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== commander@^8.2.0: version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -commander@^9.1.0, commander@^9.5.0: +commander@^9.1.0: version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + +commander@^9.5.0: + version "9.5.0" + resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== compressible@^2.0.12: version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compute-gcd@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/compute-gcd/-/compute-gcd-1.2.1.tgz#34d639f3825625e1357ce81f0e456a6249d8c77f" + resolved "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz" integrity sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg== dependencies: validate.io-array "^1.0.3" @@ -6051,7 +6078,7 @@ compute-gcd@^1.2.1: compute-lcm@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/compute-lcm/-/compute-lcm-1.1.2.tgz#9107c66b9dca28cefb22b4ab4545caac4034af23" + resolved "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz" integrity sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ== dependencies: compute-gcd "^1.2.1" @@ -6061,12 +6088,12 @@ compute-lcm@^1.1.2: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^1.6.0, concat-stream@^1.6.2: version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" @@ -6076,7 +6103,7 @@ concat-stream@^1.6.0, concat-stream@^1.6.2: config-chain@^1.1.13: version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" @@ -6084,12 +6111,12 @@ config-chain@^1.1.13: connect-livereload@^0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/connect-livereload/-/connect-livereload-0.6.1.tgz#1ac0c8bb9d9cfd5b28b629987a56a9239db9baaa" + resolved "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz" integrity sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g== connect@^3.7.0: version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz" integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== dependencies: debug "2.6.9" @@ -6099,81 +6126,96 @@ connect@^3.7.0: console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== console.table@^0.10.0: version "0.10.0" - resolved "https://registry.yarnpkg.com/console.table/-/console.table-0.10.0.tgz#0917025588875befd70cf2eff4bef2c6e2d75d04" + resolved "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz" integrity sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g== dependencies: easy-table "1.1.0" +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + content-disposition@0.5.4: version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" -content-type@~1.0.4, content-type@~1.0.5: +content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== continuable-cache@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" + resolved "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz" integrity sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA== convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie-parser@^1.4.6: version "1.4.7" - resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.7.tgz#e2125635dfd766888ffe90d60c286404fa0e7b26" + resolved "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz" integrity sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw== dependencies: cookie "0.7.2" cookie-signature "1.0.6" +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0, cookie@^0.5.0: +cookie@^0.5.0, cookie@0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + cookie@0.7.1: version "0.7.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== cookie@0.7.2: version "0.7.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== core-js-compat@^3.43.0: version "3.46.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.46.0.tgz#0c87126a19a1af00371e12b02a2b088a40f3c6f7" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz" integrity sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law== dependencies: browserslist "^4.26.3" core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@2.8.5, cors@^2.8.5: +cors@^2.8.5, cors@2.8.5: version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -6181,7 +6223,7 @@ cors@2.8.5, cors@^2.8.5: create-hash@^1.1.0, create-hash@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== dependencies: cipher-base "^1.0.1" @@ -6192,7 +6234,7 @@ create-hash@^1.1.0, create-hash@^1.2.0: create-jest@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz" integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== dependencies: "@jest/types" "^29.6.3" @@ -6205,19 +6247,19 @@ create-jest@^29.7.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-inspect@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" + resolved "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz" integrity sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A== dependencies: tslib "^2.4.0" -cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -6226,7 +6268,7 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: css-select@^5.1.0: version "5.2.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.2.2.tgz#01b6e8d163637bb2dd6c982ca4ed65863682786e" + resolved "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz" integrity sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw== dependencies: boolbase "^1.0.0" @@ -6237,27 +6279,27 @@ css-select@^5.1.0: css-what@^6.1.0: version "6.2.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz" integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA== cssfilter@0.0.10: version "0.0.10" - resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" + resolved "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz" integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== csstype@^3.0.2: version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== csv-writer@^1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/csv-writer/-/csv-writer-1.6.0.tgz#d0cea44b6b4d7d3baa2ecc6f3f7209233514bcf9" + resolved "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz" integrity sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g== -d@1, d@^1.0.1, d@^1.0.2: +d@^1.0.1, d@^1.0.2, d@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + resolved "https://registry.npmjs.org/d/-/d-1.0.2.tgz" integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== dependencies: es5-ext "^0.10.64" @@ -6265,7 +6307,7 @@ d@1, d@^1.0.1, d@^1.0.2: data-view-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz" integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== dependencies: call-bound "^1.0.3" @@ -6274,7 +6316,7 @@ data-view-buffer@^1.0.2: data-view-byte-length@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz" integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== dependencies: call-bound "^1.0.3" @@ -6283,26 +6325,26 @@ data-view-byte-length@^1.0.2: data-view-byte-offset@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz" integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== dependencies: call-bound "^1.0.2" es-errors "^1.3.0" is-data-view "^1.0.1" -dataloader@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.1.0.tgz#c69c538235e85e7ac6c6c444bae8ecabf5de9df7" - integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== - dataloader@^2.2.2: version "2.2.3" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.3.tgz#42d10b4913515f5b37c6acedcb4960d6ae1b1517" + resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz" integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== +dataloader@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz" + integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== + datauri@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/datauri/-/datauri-4.1.0.tgz#19e6e4fadc38ff8cea724d8ff59dcaad832520e4" + resolved "https://registry.npmjs.org/datauri/-/datauri-4.1.0.tgz" integrity sha512-y17kh32+I82G+ED9MNWFkZiP/Cq/vO1hN9+tSZsT9C9qn3NrvcBnh7crSepg0AQPge1hXx2Ca44s1FRdv0gFWA== dependencies: image-size "1.0.0" @@ -6310,84 +6352,91 @@ datauri@^4.1.0: date-fns@^2.28.0: version "2.30.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== dependencies: "@babel/runtime" "^7.21.0" dateformat@^4.6.3: version "4.6.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz" integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== dateformat@~3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.11.9: version "1.11.18" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.18.tgz#835fa712aac52ab9dec8b1494098774ed7070a11" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz" integrity sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA== -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: - ms "2.0.0" + ms "^2.1.1" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" -debug@4, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.1, debug@^4.4.3: +debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3, debug@4, debug@4.x: version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@4.3.4: version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - dedent@^1.0.0, dedent@^1.5.1: version "1.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" + resolved "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz" integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== deep-extend@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -6396,7 +6445,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: define-properties@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: define-data-property "^1.0.1" @@ -6405,37 +6454,37 @@ define-properties@^1.2.1: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== delegates@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== denque@^1.5.0: version "1.5.1" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" + resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== denque@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + resolved "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@2.0.0, depd@~2.0.0: +depd@^2.0.0, depd@~2.0.0, depd@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== depd@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== dependency-tree@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-9.0.0.tgz#9288dd6daf35f6510c1ea30d9894b75369aa50a2" + resolved "https://registry.npmjs.org/dependency-tree/-/dependency-tree-9.0.0.tgz" integrity sha512-osYHZJ1fBSon3lNLw70amAXsQ+RGzXsPvk9HbBgTLbp/bQBmpH5mOmsUvqXU+YEWVU0ZLewsmzOET/8jWswjDQ== dependencies: commander "^2.20.3" @@ -6446,32 +6495,32 @@ dependency-tree@^9.0.0: destroy@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-file@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + resolved "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz" integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q== detect-libc@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== detect-libc@^2.0.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== detective-amd@^3.1.0: version "3.1.2" - resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.1.2.tgz#bf55eb5291c218b76d6224a3d07932ef13a9a357" + resolved "https://registry.npmjs.org/detective-amd/-/detective-amd-3.1.2.tgz" integrity sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ== dependencies: ast-module-types "^3.0.0" @@ -6481,7 +6530,7 @@ detective-amd@^3.1.0: detective-amd@^4.0.1, detective-amd@^4.1.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-4.2.0.tgz#21c43465669f06cf894eef047a27e6e72ba6bc13" + resolved "https://registry.npmjs.org/detective-amd/-/detective-amd-4.2.0.tgz" integrity sha512-RbuEJHz78A8nW7CklkqTzd8lDCN42En53dgEIsya0DilpkwslamSZDasLg8dJyxbw46OxhSQeY+C2btdSkCvQQ== dependencies: ast-module-types "^4.0.0" @@ -6491,7 +6540,7 @@ detective-amd@^4.0.1, detective-amd@^4.1.0: detective-cjs@^3.1.1: version "3.1.3" - resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-3.1.3.tgz#50e107d67b37f459b0ec02966ceb7e20a73f268b" + resolved "https://registry.npmjs.org/detective-cjs/-/detective-cjs-3.1.3.tgz" integrity sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ== dependencies: ast-module-types "^3.0.0" @@ -6499,7 +6548,7 @@ detective-cjs@^3.1.1: detective-cjs@^4.0.0, detective-cjs@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-4.1.0.tgz#56b1558ca0910933c7fc47c740b957f0280ff302" + resolved "https://registry.npmjs.org/detective-cjs/-/detective-cjs-4.1.0.tgz" integrity sha512-QxzMwt5MfPLwS7mG30zvnmOvHLx5vyVvjsAV6gQOyuMoBR5G1DhS1eJZ4P10AlH+HSnk93mTcrg3l39+24XCtg== dependencies: ast-module-types "^4.0.0" @@ -6507,21 +6556,21 @@ detective-cjs@^4.0.0, detective-cjs@^4.1.0: detective-es6@^2.2.1: version "2.2.2" - resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-2.2.2.tgz#ee5f880981d9fecae9a694007029a2f6f26d8d28" + resolved "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz" integrity sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw== dependencies: node-source-walk "^4.0.0" detective-es6@^3.0.0, detective-es6@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-3.0.1.tgz#53a15fbb2f298c4a106d9fe7427da8a57162dde6" + resolved "https://registry.npmjs.org/detective-es6/-/detective-es6-3.0.1.tgz" integrity sha512-evPeYIEdK1jK3Oji5p0hX4sPV/1vK+o4ihcWZkMQE6voypSW/cIBiynOLxQk5KOOQbdP8oOAsYqouMTYO5l1sw== dependencies: node-source-walk "^5.0.0" detective-less@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/detective-less/-/detective-less-1.0.2.tgz#a68af9ca5f69d74b7d0aa190218b211d83b4f7e3" + resolved "https://registry.npmjs.org/detective-less/-/detective-less-1.0.2.tgz" integrity sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA== dependencies: debug "^4.0.0" @@ -6530,7 +6579,7 @@ detective-less@^1.0.2: detective-postcss@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-4.0.0.tgz#24e69b465e5fefe7a6afd05f7e894e34595dbf51" + resolved "https://registry.npmjs.org/detective-postcss/-/detective-postcss-4.0.0.tgz" integrity sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A== dependencies: debug "^4.1.1" @@ -6540,7 +6589,7 @@ detective-postcss@^4.0.0: detective-postcss@^6.1.0, detective-postcss@^6.1.1: version "6.1.3" - resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-6.1.3.tgz#51a2d4419327ad85d0af071c7054c79fafca7e73" + resolved "https://registry.npmjs.org/detective-postcss/-/detective-postcss-6.1.3.tgz" integrity sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw== dependencies: is-url "^1.2.4" @@ -6549,7 +6598,7 @@ detective-postcss@^6.1.0, detective-postcss@^6.1.1: detective-sass@^3.0.1: version "3.0.2" - resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-3.0.2.tgz#e0f35aac79a4d2f6409c284d95b8f7ecd5973afd" + resolved "https://registry.npmjs.org/detective-sass/-/detective-sass-3.0.2.tgz" integrity sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g== dependencies: gonzales-pe "^4.3.0" @@ -6557,7 +6606,7 @@ detective-sass@^3.0.1: detective-sass@^4.0.1, detective-sass@^4.1.1: version "4.1.3" - resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-4.1.3.tgz#6cdcc27ae8a90d15704e0ba83683048f77f10b75" + resolved "https://registry.npmjs.org/detective-sass/-/detective-sass-4.1.3.tgz" integrity sha512-xGRbwGaGte57gvEqM8B9GDiURY3El/H49vA6g9wFkxq9zalmTlTAuqWu+BsH0iwonGPruLt55tZZDEZqPc6lag== dependencies: gonzales-pe "^4.3.0" @@ -6565,7 +6614,7 @@ detective-sass@^4.0.1, detective-sass@^4.1.1: detective-scss@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-2.0.2.tgz#7d2a642616d44bf677963484fa8754d9558b8235" + resolved "https://registry.npmjs.org/detective-scss/-/detective-scss-2.0.2.tgz" integrity sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg== dependencies: gonzales-pe "^4.3.0" @@ -6573,7 +6622,7 @@ detective-scss@^2.0.1: detective-scss@^3.0.0, detective-scss@^3.0.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-3.1.1.tgz#b49f05cadfb0837b04e23baba292581b7c7f65e1" + resolved "https://registry.npmjs.org/detective-scss/-/detective-scss-3.1.1.tgz" integrity sha512-FWkfru1jZBhUeuBsOeGKXKAVDrzYFSQFK2o2tuG/nCCFQ0U/EcXC157MNAcR5mmj+mCeneZzlkBOFJTesDjrww== dependencies: gonzales-pe "^4.3.0" @@ -6581,22 +6630,22 @@ detective-scss@^3.0.0, detective-scss@^3.0.1: detective-stylus@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.3.tgz#20a702936c9fd7d4203fd7a903314b5dd43ac713" + resolved "https://registry.npmjs.org/detective-stylus/-/detective-stylus-1.0.3.tgz" integrity sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q== detective-stylus@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-2.0.1.tgz#d528dfa7ef3c4eb2fbc9a7249d54906ec4e05d09" + resolved "https://registry.npmjs.org/detective-stylus/-/detective-stylus-2.0.1.tgz" integrity sha512-/Tvs1pWLg8eYwwV6kZQY5IslGaYqc/GACxjcaGudiNtN5nKCH6o2WnJK3j0gA3huCnoQcbv8X7oz/c1lnvE3zQ== detective-stylus@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-3.0.0.tgz#c869795a7d6df7043ab6aee8b1a6f3dd66764232" + resolved "https://registry.npmjs.org/detective-stylus/-/detective-stylus-3.0.0.tgz" integrity sha512-1xYTzbrduExqMYmte7Qk99IRA3Aa6oV7PYzd+3yDcQXkmENvyGF/arripri6lxRDdNYEb4fZFuHtNRAXbz3iAA== detective-typescript@^7.0.0: version "7.0.2" - resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-7.0.2.tgz#c6e00b4c28764741ef719662250e6b014a5f3c8e" + resolved "https://registry.npmjs.org/detective-typescript/-/detective-typescript-7.0.2.tgz" integrity sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA== dependencies: "@typescript-eslint/typescript-estree" "^4.33.0" @@ -6606,7 +6655,7 @@ detective-typescript@^7.0.0: detective-typescript@^9.0.0, detective-typescript@^9.1.1: version "9.1.1" - resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-9.1.1.tgz#b99c0122cbb35b39de2c5f58447f1e93ac28c6d5" + resolved "https://registry.npmjs.org/detective-typescript/-/detective-typescript-9.1.1.tgz" integrity sha512-Uc1yVutTF0RRm1YJ3g//i1Cn2vx1kwHj15cnzQP6ff5koNzQ0idc1zAC73ryaWEulA0ElRXFTq6wOqe8vUQ3MA== dependencies: "@typescript-eslint/typescript-estree" "^5.55.0" @@ -6616,38 +6665,38 @@ detective-typescript@^9.0.0, detective-typescript@^9.1.1: diff-sequences@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" dom-serializer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== dependencies: domelementtype "^2.3.0" @@ -6656,19 +6705,19 @@ dom-serializer@^2.0.0: domelementtype@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== dependencies: domelementtype "^2.3.0" domutils@^3.0.1, domutils@^3.1.0, domutils@^3.2.1, domutils@^3.2.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + resolved "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz" integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== dependencies: dom-serializer "^2.0.0" @@ -6677,22 +6726,22 @@ domutils@^3.0.1, domutils@^3.1.0, domutils@^3.2.1, domutils@^3.2.2: dotenv@*: version "17.2.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.3.tgz#ad995d6997f639b11065f419a22fabf567cdb9a2" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz" integrity sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w== dotenv@^16.3.1: version "16.6.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz" integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== dset@^3.1.4: version "3.1.4" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" + resolved "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz" integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" @@ -6701,12 +6750,12 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: duplexer@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== duplexify@^3.5.1: version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== dependencies: end-of-stream "^1.0.0" @@ -6716,7 +6765,7 @@ duplexify@^3.5.1: duplexify@^4.0.0, duplexify@^4.1.3: version "4.1.3" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" + resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz" integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== dependencies: end-of-stream "^1.4.1" @@ -6726,33 +6775,33 @@ duplexify@^4.0.0, duplexify@^4.1.3: dynamic-dedupe@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + resolved "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz" integrity sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ== dependencies: xtend "^4.0.0" eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== easy-table@1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.1.0.tgz#86f9ab4c102f0371b7297b92a651d5824bc8cb73" + resolved "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz" integrity sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA== optionalDependencies: wcwidth ">=1.0.1" -ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: +ecdsa-sig-formatter@^1.0.11, ecdsa-sig-formatter@1.0.11: version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" ecpair@2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/ecpair/-/ecpair-2.1.0.tgz#673f826b1d80d5eb091b8e2010c6b588e8d2cb45" + resolved "https://registry.npmjs.org/ecpair/-/ecpair-2.1.0.tgz" integrity sha512-cL/mh3MtJutFOvFc27GPZE2pWL3a3k4YvzUWEOvilnfZVlH3Jwgx/7d6tlD7/75tNk8TG2m+7Kgtz0SI1tWcqw== dependencies: randombytes "^2.1.0" @@ -6761,7 +6810,7 @@ ecpair@2.1.0: editorconfig@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-1.0.4.tgz#040c9a8e9a6c5288388b87c2db07028aa89f53a3" + resolved "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz" integrity sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q== dependencies: "@one-ini/wasm" "0.1.1" @@ -6771,17 +6820,17 @@ editorconfig@^1.0.3: ee-first@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.5.238: version "1.5.240" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz#bfd946570a723aa3754370065d02e23e30824774" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz" integrity sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ== elliptic@^6.5.7: version "6.6.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz" integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== dependencies: bn.js "^4.11.9" @@ -6794,32 +6843,37 @@ elliptic@^6.5.7: emittery@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +encodeurl@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== encodeurl@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== encoding-sniffer@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz#396ec97ac22ce5a037ba44af1992ac9d46a7b819" + resolved "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz" integrity sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw== dependencies: iconv-lite "^0.6.3" @@ -6827,14 +6881,14 @@ encoding-sniffer@^0.2.1: end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.5" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz" integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== dependencies: once "^1.4.0" enhanced-resolve@^5.8.3: version "5.18.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz" integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww== dependencies: graceful-fs "^4.2.4" @@ -6842,7 +6896,7 @@ enhanced-resolve@^5.8.3: ent@^2.2.0: version "2.2.2" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.2.tgz#22a5ed2fd7ce0cbcff1d1474cf4909a44bdb6e85" + resolved "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz" integrity sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw== dependencies: call-bound "^1.0.3" @@ -6852,31 +6906,31 @@ ent@^2.2.0: entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== entities@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + resolved "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz" integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== error-ex@^1.3.1: version "1.3.4" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz" integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" error@^7.0.0: version "7.2.1" - resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" + resolved "https://registry.npmjs.org/error/-/error-7.2.1.tgz" integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== dependencies: string-template "~0.2.1" es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0: version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz" integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== dependencies: array-buffer-byte-length "^1.0.2" @@ -6936,24 +6990,24 @@ es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24 es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" es-set-tostringtag@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== dependencies: es-errors "^1.3.0" @@ -6963,14 +7017,14 @@ es-set-tostringtag@^2.1.0: es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz" integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== dependencies: hasown "^2.0.2" es-to-primitive@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz" integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: is-callable "^1.2.7" @@ -6979,7 +7033,7 @@ es-to-primitive@^1.3.0: es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.2: version "0.10.64" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz" integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== dependencies: es6-iterator "^2.0.3" @@ -6989,7 +7043,7 @@ es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@ es6-iterator@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz" integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== dependencies: d "1" @@ -6998,17 +7052,17 @@ es6-iterator@^2.0.3: es6-promise@^3.2.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz" integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg== es6-promise@^4.2.8: version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.4" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz" integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== dependencies: d "^1.0.2" @@ -7016,7 +7070,7 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: es6-weak-map@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + resolved "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz" integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== dependencies: d "1" @@ -7026,32 +7080,32 @@ es6-weak-map@^2.0.3: escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@^1.13.0, escodegen@^1.8.1: version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== dependencies: esprima "^4.0.1" @@ -7063,7 +7117,7 @@ escodegen@^1.13.0, escodegen@^1.8.1: escodegen@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz" integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== dependencies: esprima "^4.0.1" @@ -7072,14 +7126,14 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^9.0.0: +eslint-config-prettier@^9.0.0, "eslint-config-prettier@>= 7.0.0 <10.0.0 || >=10.1.0": version "9.1.2" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz#90deb4fa0259592df774b600dbd1d2249a78ce91" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz" integrity sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ== eslint-import-resolver-node@^0.3.9: version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" @@ -7088,14 +7142,14 @@ eslint-import-resolver-node@^0.3.9: eslint-module-utils@^2.12.1: version "2.12.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz" integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== dependencies: debug "^3.2.7" eslint-plugin-import@^2.28.1: version "2.32.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz" integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== dependencies: "@rtsao/scc" "^1.1.0" @@ -7120,14 +7174,14 @@ eslint-plugin-import@^2.28.1: eslint-plugin-jest@^27.4.0: version "27.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz" integrity sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug== dependencies: "@typescript-eslint/utils" "^5.10.0" eslint-plugin-prettier@^5.0.0: version "5.5.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz#9d61c4ea11de5af704d4edf108c82ccfa7f2e61c" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz" integrity sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg== dependencies: prettier-linter-helpers "^1.0.0" @@ -7135,7 +7189,7 @@ eslint-plugin-prettier@^5.0.0: eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -7143,7 +7197,7 @@ eslint-scope@^5.1.1: eslint-scope@^7.2.2: version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" @@ -7151,17 +7205,17 @@ eslint-scope@^7.2.2: eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.49.0: +"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.49.0, eslint@>=7.0.0, eslint@>=8.0.0: version "8.57.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz" integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" @@ -7205,12 +7259,12 @@ eslint@^8.49.0: esm@^3.2.25: version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== esniff@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + resolved "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz" integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== dependencies: d "^1.0.1" @@ -7220,60 +7274,65 @@ esniff@^2.0.1: espree@^9.0.0, espree@^9.6.0, espree@^9.6.1: version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: acorn "^8.9.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.2.tgz#76a0fd66fcfe154fd292667dc264019750b1657b" - integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== - esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esprima@1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz" + integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== + esquery@^1.4.2: version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: +etag@^1.8.1, etag@~1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== event-emitter@^0.3.5: version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + resolved "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz" integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== dependencies: d "1" @@ -7281,27 +7340,39 @@ event-emitter@^0.3.5: event-target-shim@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== eventemitter2@~0.4.13: version "0.4.14" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" integrity sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ== +events@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + events@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + resolved "https://registry.npmjs.org/events/-/events-1.1.1.tgz" integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== -events@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: + version "3.0.6" + resolved "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" execa@^5.0.0, execa@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -7316,19 +7387,19 @@ execa@^5.0.0, execa@^5.1.1: exit@^0.1.2, exit@~0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + resolved "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz" integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== dependencies: homedir-polyfill "^1.0.1" expect@^29.0.0, expect@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz" integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: "@jest/expect-utils" "^29.7.0" @@ -7339,86 +7410,125 @@ expect@^29.0.0, expect@^29.7.0: express-jwt@^8.4.1: version "8.5.1" - resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-8.5.1.tgz#e167bb9d96eb6e8cf5aba28707e5ca6e142bd7d3" + resolved "https://registry.npmjs.org/express-jwt/-/express-jwt-8.5.1.tgz" integrity sha512-Dv6QjDLpR2jmdb8M6XQXiCcpEom7mK8TOqnr0/TngDKsG2DHVkO8+XnVxkJVN7BuS1I3OrGw6N8j5DaaGgkDRQ== dependencies: "@types/jsonwebtoken" "^9" express-unless "^2.1.3" jsonwebtoken "^9.0.0" +express-rate-limit@^7.5.0: + version "7.5.1" + resolved "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz" + integrity sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw== + express-unless@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-2.1.3.tgz#f951c6cca52a24da3de32d42cfd4db57bc0f9a2e" + resolved "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz" integrity sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ== -express@4.18.2: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== +express@^4.17.1, express@^4.18.2, "express@>= 4.11": + version "4.21.2" + resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.1" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.7.1" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "1.3.1" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.12" proxy-addr "~2.0.7" - qs "6.11.0" + qs "6.13.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "0.19.0" + serve-static "1.16.2" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" -express@^4.17.1, express@^4.18.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== +express@^5.0.1: + version "5.2.1" + resolved "https://registry.npmjs.org/express/-/express-5.2.1.tgz" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + +express@4.18.2: + version "4.18.2" + resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.3" + body-parser "1.20.1" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.7.1" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~2.0.0" + encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.3.1" + finalhandler "1.2.0" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.3" + merge-descriptors "1.0.1" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.12" + path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.13.0" + qs "6.11.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" + send "0.18.0" + serve-static "1.15.0" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" @@ -7427,34 +7537,34 @@ express@^4.17.1, express@^4.18.2: ext@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + resolved "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz" integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== dependencies: type "^2.7.2" extend@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== fast-copy@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" + resolved "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz" integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: version "3.3.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -7465,89 +7575,89 @@ fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-printf@^1.6.10: version "1.6.10" - resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.10.tgz#c44ad871726152159d7a903a5af0d65cf3d75875" + resolved "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.10.tgz" integrity sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w== fast-redact@^3.1.1: version "3.5.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz" integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== fast-sha256@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-sha256/-/fast-sha256-1.3.0.tgz#7916ba2054eeb255982608cccd0f6660c79b7ae6" + resolved "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz" integrity sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ== fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: version "1.0.6" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" + resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz" integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fast-uri@^3.0.1: version "3.1.0" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz" integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== -fast-xml-parser@5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz#4809fdfb1310494e341098c25cb1341a01a9144a" - integrity sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ== - dependencies: - strnum "^2.1.0" - fast-xml-parser@^4.2.2, fast-xml-parser@^4.4.1: version "4.5.3" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz#c54d6b35aa0f23dc1ea60b6c884340c006dc6efb" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz" integrity sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig== dependencies: strnum "^1.1.1" +fast-xml-parser@5.2.5: + version "5.2.5" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz" + integrity sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ== + dependencies: + strnum "^2.1.0" + fastq@^1.6.0: version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== dependencies: reusify "^1.0.4" -faye-websocket@0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - faye-websocket@~0.10.0: version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz" integrity sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ== dependencies: websocket-driver ">=0.5.1" +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + fb-watchman@^2.0.0, fb-watchman@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" fetch-har@^8.1.5: version "8.1.5" - resolved "https://registry.yarnpkg.com/fetch-har/-/fetch-har-8.1.5.tgz#a97732c6d42916e7812d498ab2363474a18a2a8e" + resolved "https://registry.npmjs.org/fetch-har/-/fetch-har-8.1.5.tgz" integrity sha512-c9WDro4RWC+suOVRJFNW21cgqTOELRZpvFJgfENvOM7Yt/VA4QeFtRax795SyOpTisdpcl5XNQlQZdAE6HERDA== dependencies: "@readme/data-urls" "^1.0.1" @@ -7558,26 +7668,26 @@ fetch-har@^8.1.5: figures@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" file-sync-cmp@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz#a5e7a8ffbfa493b43b923bbd4ca89a53b63b612b" + resolved "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz" integrity sha512-0k45oWBokCqh2MOexeYKpyqmGKG+8mQ2Wd8iawx+uWd/weWJQAZ6SoPybagdCI4xFisag8iAR77WPm4h3pTfxA== filing-cabinet@^3.0.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-3.3.1.tgz#45d87bb273a0e0a7dd6ac6bac9111059186e2e9c" + resolved "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.3.1.tgz" integrity sha512-renEK4Hh6DUl9Vl22Y3cxBq1yh8oNvbAdXnhih0wVpmea+uyKjC9K4QeRjUaybIiIewdzfum+Fg15ZqJ/GyCaA== dependencies: app-module-path "^2.2.0" @@ -7596,14 +7706,26 @@ filing-cabinet@^3.0.1: fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + finalhandler@1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" @@ -7616,7 +7738,7 @@ finalhandler@1.1.2: finalhandler@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" @@ -7629,7 +7751,7 @@ finalhandler@1.2.0: finalhandler@1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz" integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" @@ -7642,7 +7764,7 @@ finalhandler@1.3.1: find-cache-dir@^3.3.1: version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== dependencies: commondir "^1.0.1" @@ -7651,7 +7773,7 @@ find-cache-dir@^3.3.1: find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -7659,7 +7781,7 @@ find-up@^4.0.0, find-up@^4.1.0: find-up@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -7667,7 +7789,7 @@ find-up@^5.0.0: findup-sync@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0" + resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz" integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== dependencies: detect-file "^1.0.0" @@ -7677,14 +7799,14 @@ findup-sync@^4.0.0: findup-sync@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" + resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz" integrity sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg== dependencies: glob "~5.0.0" fined@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" + resolved "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz" integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== dependencies: expand-tilde "^2.0.2" @@ -7695,7 +7817,7 @@ fined@^1.2.0: firebase-admin@^11.9.0: version "11.11.1" - resolved "https://registry.yarnpkg.com/firebase-admin/-/firebase-admin-11.11.1.tgz#6712923de70d218c9f514d73005d976d03339605" + resolved "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.11.1.tgz" integrity sha512-UyEbq+3u6jWzCYbUntv/HuJiTixwh36G1R9j0v71mSvGAx/YZEWEW7uSGLYxBYE6ckVRQoKMr40PYUEzrm/4dg== dependencies: "@fastify/busboy" "^1.2.1" @@ -7712,12 +7834,12 @@ firebase-admin@^11.9.0: flagged-respawn@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" + resolved "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz" integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== flat-cache@^3.0.4: version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz" integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: flatted "^3.2.9" @@ -7726,46 +7848,46 @@ flat-cache@^3.0.4: flatted@^3.2.9: version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== flatten@^1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + resolved "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== fn-args@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/fn-args/-/fn-args-5.0.0.tgz#7a18e105c8fb3bf0a51c30389bf16c9ebe740bb3" + resolved "https://registry.npmjs.org/fn-args/-/fn-args-5.0.0.tgz" integrity sha512-CtbfI3oFFc3nbdIoHycrfbrxiGgxXBXXuyOl49h47JawM1mYrqpiRqnH5CB2mBatdXvHHOUO6a+RiAuuvKt0lw== follow-redirects@^1.14.8, follow-redirects@^1.15.6: version "1.15.11" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz" integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== dependencies: is-callable "^1.2.7" for-in@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== for-own@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + resolved "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz" integrity sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg== dependencies: for-in "^1.0.1" foreground-child@^3.1.0, foreground-child@^3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: cross-spawn "^7.0.6" @@ -7773,12 +7895,36 @@ foreground-child@^3.1.0, foreground-child@^3.3.1: form-data-encoder@^1.7.2: version "1.9.0" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.9.0.tgz#fd18d316b1ec830d2a8be8ad86c1cf0317320b34" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.9.0.tgz" integrity sha512-rahaRMkN8P8d/tgK/BLPX+WBVM27NbvdXBxqQujBtkDAIFspaRqN7Od7lfdGQA6KAD+f82fYCLBq1ipvcu8qLw== -form-data@^2.2.0, form-data@^2.5.0, form-data@^2.5.5: +form-data@^2.2.0: + version "2.5.5" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" + +form-data@^2.5.0: + version "2.5.5" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" + +form-data@^2.5.5: version "2.5.5" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== dependencies: asynckit "^0.4.0" @@ -7790,7 +7936,7 @@ form-data@^2.2.0, form-data@^2.5.0, form-data@^2.5.5: form-data@^4.0.2, form-data@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz" integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== dependencies: asynckit "^0.4.0" @@ -7801,7 +7947,7 @@ form-data@^4.0.2, form-data@^4.0.4: formdata-node@^4.3.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz" integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== dependencies: node-domexception "1.0.0" @@ -7809,17 +7955,22 @@ formdata-node@^4.3.2: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fresh@0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs-extra@^10.0.1: version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" @@ -7828,29 +7979,29 @@ fs-extra@^10.0.1: fs-minipass@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2, fsevents@^2.3.3, fsevents@~2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: version "1.1.8" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz" integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== dependencies: call-bind "^1.0.8" @@ -7862,17 +8013,17 @@ function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== functions-have-names@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gauge@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz" integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== dependencies: aproba "^1.0.3 || ^2.0.0" @@ -7887,7 +8038,7 @@ gauge@^3.0.0: gaxios@^5.0.0, gaxios@^5.0.1: version "5.1.3" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.3.tgz#f7fa92da0fe197c846441e5ead2573d4979e9013" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz" integrity sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA== dependencies: extend "^3.0.2" @@ -7897,7 +8048,7 @@ gaxios@^5.0.0, gaxios@^5.0.1: gaxios@^6.0.0, gaxios@^6.0.2, gaxios@^6.1.1: version "6.7.1" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz" integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== dependencies: extend "^3.0.2" @@ -7908,14 +8059,14 @@ gaxios@^6.0.0, gaxios@^6.0.2, gaxios@^6.1.1: gaze@^1.1.0: version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz" integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== dependencies: globule "^1.0.0" -gcp-metadata@^5.3.0: +gcp-metadata@^5.2.0, gcp-metadata@^5.3.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.3.0.tgz#6f45eb473d0cb47d15001476b48b663744d25408" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz" integrity sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w== dependencies: gaxios "^5.0.0" @@ -7923,7 +8074,7 @@ gcp-metadata@^5.3.0: gcp-metadata@^6.1.0: version "6.1.1" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.1.tgz#f65aa69f546bc56e116061d137d3f5f90bdec494" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz" integrity sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A== dependencies: gaxios "^6.1.1" @@ -7932,17 +8083,17 @@ gcp-metadata@^6.1.0: generator-function@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" + resolved "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz" integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-amd-module-type@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0" + resolved "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz" integrity sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw== dependencies: ast-module-types "^3.0.0" @@ -7950,7 +8101,7 @@ get-amd-module-type@^3.0.0: get-amd-module-type@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-4.1.0.tgz#af1396d02cd935cb6fafdc4a5282395db3422db6" + resolved "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-4.1.0.tgz" integrity sha512-0e/eK6vTGCnSfQ6eYs3wtH05KotJYIP7ZIZEueP/KlA+0dIAEs8bYFvOd/U56w1vfjhJqBagUxVMyy9Tr/cViQ== dependencies: ast-module-types "^4.0.0" @@ -7958,12 +8109,12 @@ get-amd-module-type@^4.1.0: get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: call-bind-apply-helpers "^1.0.2" @@ -7979,22 +8130,22 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@ get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-port@^3.1.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz" integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== get-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: dunder-proto "^1.0.1" @@ -8002,12 +8153,12 @@ get-proto@^1.0.1: get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz" integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== dependencies: call-bound "^1.0.3" @@ -8016,31 +8167,31 @@ get-symbol-description@^1.1.0: getobject@~1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/getobject/-/getobject-1.0.2.tgz#25ec87a50370f6dcc3c6ba7ef43c4c16215c4c89" + resolved "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz" integrity sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg== getopts@2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" + resolved "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz" integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob@*: version "11.0.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" + resolved "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz" integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== dependencies: foreground-child "^3.3.1" @@ -8052,7 +8203,7 @@ glob@*: glob@^10.3.3: version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== dependencies: foreground-child "^3.1.0" @@ -8064,7 +8215,7 @@ glob@^10.3.3: glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -8076,7 +8227,7 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: glob@^8.0.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" @@ -8087,7 +8238,7 @@ glob@^8.0.0: glob@~5.0.0: version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + resolved "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz" integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA== dependencies: inflight "^1.0.4" @@ -8096,9 +8247,21 @@ glob@~5.0.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~7.1.1, glob@~7.1.6: +glob@~7.1.1: + version "7.1.7" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~7.1.6: version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -8110,7 +8273,7 @@ glob@~7.1.1, glob@~7.1.6: global-modules@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + resolved "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz" integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== dependencies: global-prefix "^1.0.1" @@ -8119,7 +8282,7 @@ global-modules@^1.0.0: global-prefix@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz" integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg== dependencies: expand-tilde "^2.0.2" @@ -8130,22 +8293,22 @@ global-prefix@^1.0.1: globals@^13.19.0: version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" globalthis@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: define-properties "^1.2.1" gopd "^1.0.1" -globby@11.1.0, globby@^11.0.3, globby@^11.1.0: +globby@^11.0.3, globby@^11.1.0, globby@11.1.0: version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -8157,7 +8320,7 @@ globby@11.1.0, globby@^11.0.3, globby@^11.1.0: globby@^12.0.2: version "12.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" + resolved "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz" integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== dependencies: array-union "^3.0.1" @@ -8169,7 +8332,7 @@ globby@^12.0.2: globule@^1.0.0: version "1.3.4" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb" + resolved "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz" integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg== dependencies: glob "~7.1.1" @@ -8178,14 +8341,14 @@ globule@^1.0.0: gonzales-pe@^4.2.3, gonzales-pe@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz" integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== dependencies: minimist "^1.2.5" google-auth-library@^8.0.1, google-auth-library@^8.0.2: version "8.9.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.9.0.tgz#15a271eb2ec35d43b81deb72211bd61b1ef14dd0" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz" integrity sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg== dependencies: arrify "^2.0.0" @@ -8200,7 +8363,7 @@ google-auth-library@^8.0.1, google-auth-library@^8.0.2: google-auth-library@^9.6.3: version "9.15.1" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.15.1.tgz#0c5d84ed1890b2375f1cd74f03ac7b806b392928" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz" integrity sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng== dependencies: base64-js "^1.3.0" @@ -8212,7 +8375,7 @@ google-auth-library@^9.6.3: google-gax@^3.5.7: version "3.6.1" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-3.6.1.tgz#02c78fc496f5adf86f2ca9145545f4b6575f6118" + resolved "https://registry.npmjs.org/google-gax/-/google-gax-3.6.1.tgz" integrity sha512-g/lcUjGcB6DSw2HxgEmCDOrI/CByOwqRvsuUvNalHUK2iPPPlmAIpbMbl62u0YufGMr8zgE3JL7th6dCb1Ry+w== dependencies: "@grpc/grpc-js" "~1.8.0" @@ -8233,83 +8396,78 @@ google-gax@^3.5.7: google-logging-utils@^0.0.2: version "0.0.2" - resolved "https://registry.yarnpkg.com/google-logging-utils/-/google-logging-utils-0.0.2.tgz#5fd837e06fa334da450433b9e3e1870c1594466a" + resolved "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz" integrity sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ== google-p12-pem@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-4.0.1.tgz#82841798253c65b7dc2a4e5fe9df141db670172a" + resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz" integrity sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ== dependencies: node-forge "^1.3.1" -google-protobuf@3.15.8: - version "3.15.8" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.15.8.tgz#5f3948905e4951c867d6bc143f385a80e2a39efe" - integrity sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw== - google-protobuf@^3.21.2: version "3.21.4" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.4.tgz#2f933e8b6e5e9f8edde66b7be0024b68f77da6c9" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz" integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== +google-protobuf@3.15.8: + version "3.15.8" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.8.tgz" + integrity sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw== + gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9, graceful-fs@~4.2.10: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== graphemer@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -graphql-middleware@^6.1.33: +"graphql-middleware@^2.0.0 || ^3.0.0 || ^4.0.0 || ^6.0.0", graphql-middleware@^6.1.33: version "6.1.35" - resolved "https://registry.yarnpkg.com/graphql-middleware/-/graphql-middleware-6.1.35.tgz#7297d41ceb35742b80640cca5cd85d1e47e8ab24" + resolved "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.1.35.tgz" integrity sha512-azawK7ApUYtcuPGRGBR9vDZu795pRuaFhO5fgomdJppdfKRt7jwncuh0b7+D3i574/4B+16CNWgVpnGVlg3ZCg== dependencies: "@graphql-tools/delegate" "^8.8.1" "@graphql-tools/schema" "^8.5.1" -graphql-query-complexity-apollo-plugin@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/graphql-query-complexity-apollo-plugin/-/graphql-query-complexity-apollo-plugin-1.0.2.tgz#1eff2eb4ea455a3577c9a2f3580561a5e19b394a" - integrity sha512-TjDzIuAILNI0+wemtTpl47Vj/wCi8TGhq8tVcJQOsHUkiflrVhrHwKHiBX7V5KC5rgUAJtnsqSgVBDJqsAvwjg== - graphql-query-complexity@^0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/graphql-query-complexity/-/graphql-query-complexity-0.12.0.tgz#5f636ccc54da82225f31e898e7f27192fe074b4c" + resolved "https://registry.npmjs.org/graphql-query-complexity/-/graphql-query-complexity-0.12.0.tgz" integrity sha512-fWEyuSL6g/+nSiIRgIipfI6UXTI7bAxrpPlCY1c0+V3pAEUo1ybaKmSBgNr1ed2r+agm1plJww8Loig9y6s2dw== dependencies: lodash.get "^4.4.2" graphql-redis-subscriptions@^2.6.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/graphql-redis-subscriptions/-/graphql-redis-subscriptions-2.7.0.tgz#955826c8cccdae0f684014c72e2f7c0bf677e178" + resolved "https://registry.npmjs.org/graphql-redis-subscriptions/-/graphql-redis-subscriptions-2.7.0.tgz" integrity sha512-IK4uCKx1UNhkcnG9lIqFWz9PpltSbuM8RygwGoB/e1HZMuKpAGeqqfHFeLKkQjjubvk4tAdUdx48AUkTAXJ17Q== optionalDependencies: ioredis "^5.3.2" graphql-relay@^0.10.0: version "0.10.2" - resolved "https://registry.yarnpkg.com/graphql-relay/-/graphql-relay-0.10.2.tgz#a1cd0e4b63e671a08da6c021ec033086a4887f6a" + resolved "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.2.tgz" integrity sha512-abybva1hmlNt7Y9pMpAzHuFnM2Mme/a2Usd8S4X27fNteLGRAECMYfhmsrpZFvGn3BhmBZugMXYW/Mesv3P1Kw== graphql-scalars@^1.15.0: version "1.25.0" - resolved "https://registry.yarnpkg.com/graphql-scalars/-/graphql-scalars-1.25.0.tgz#88f2891d60942c420286a2e76a29abfe645ac899" + resolved "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.25.0.tgz" integrity sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg== dependencies: tslib "^2.5.0" graphql-shield@^7.6.4: version "7.6.5" - resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-7.6.5.tgz#91683254e498e120ce58d560b8b4f118e10530ea" + resolved "https://registry.npmjs.org/graphql-shield/-/graphql-shield-7.6.5.tgz" integrity sha512-etbzf7UIhQW6vadn/UR+ds0LJOceO8ITDXwbUkQMlP2KqPgSKTZRE2zci+AUfqP+cpV9zDQdbTJfPfW5OCEamg== dependencies: "@types/yup" "0.29.13" @@ -8317,23 +8475,23 @@ graphql-shield@^7.6.4: tslib "^2.4.0" yup "^0.32.0" -graphql-subscriptions@^2.0.0: +"graphql-subscriptions@^1.0.0 || ^2.0.0 || ^3.0.0", graphql-subscriptions@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz#11ec181d475852d8aec879183e8e1eb94f2eb79a" + resolved "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz" integrity sha512-s6k2b8mmt9gF9pEfkxsaO1lTxaySfKoEJzEfmwguBbQ//Oq23hIXCfR1hm4kdh5hnR20RdwB+s3BCb+0duHSZA== dependencies: iterall "^1.3.0" graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== dependencies: tslib "^2.1.0" graphql-tools@^9.0.0: version "9.0.20" - resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-9.0.20.tgz#a51024b29c0da9f057a42d9ddeaf153d142b7962" + resolved "https://registry.npmjs.org/graphql-tools/-/graphql-tools-9.0.20.tgz" integrity sha512-n9T4EEVzfJyQ0N3XAH4bMKhrEhyllBkfFq/4bpnEzkmkvYJ2D120Y5YslqYvpq0kohPFU706Q6zTHrHOfbL7MA== dependencies: "@graphql-tools/schema" "^10.0.25" @@ -8341,34 +8499,34 @@ graphql-tools@^9.0.0: optionalDependencies: "@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0" -graphql-ws@^5.13.1: +graphql-ws@^5.13.1, graphql-ws@^5.5.5, "graphql-ws@^5.5.5 || ^6.0.3": version "5.16.2" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.16.2.tgz#7b0306c1bdb0e97a05e800ccd523f46fb212e37c" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.2.tgz" integrity sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ== -graphql@^16.3.0, graphql@^16.8.0: +"graphql@^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql@^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql@^14.2.1 || ^15.0.0 || ^16.0.0", "graphql@^14.6.0 || ^15.0.0 || ^16.0.0", "graphql@^15.0.0 || ^16.0.0", "graphql@^15.3.0 || ^16.0.0", "graphql@^15.7.2 || ^16.0.0", graphql@^16.2.0, graphql@^16.3.0, graphql@^16.8.0, "graphql@>=0.11 <=16", "graphql@14.x || 15.x || 16.x": version "16.11.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" + resolved "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz" integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== -grpc-tools@^1.12.4: - version "1.13.0" - resolved "https://registry.yarnpkg.com/grpc-tools/-/grpc-tools-1.13.0.tgz#a4fea8eebce51fb9fec00055a3e52016dfd5af89" - integrity sha512-7CbkJ1yWPfX0nHjbYG58BQThNhbICXBZynzCUxCb3LzX5X9B3hQbRY2STiRgIEiLILlK9fgl0z0QVGwPCdXf5g== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.5" - grpc_tools_node_protoc_ts@^5.3.3: version "5.3.3" - resolved "https://registry.yarnpkg.com/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.3.tgz#9a6c1c2f41563a1ab259c0177496d7dfed30dbfe" + resolved "https://registry.npmjs.org/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.3.tgz" integrity sha512-M/YrklvVXMtuuj9kb42PxeouZhs7Ul+R4e/31XwrankUcKL8cQQP50Q9q+KEHGyHQaPt6VtKKsxMgLaKbCxeww== dependencies: google-protobuf "3.15.8" handlebars "4.7.7" +grpc-tools@^1.12.4: + version "1.13.0" + resolved "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.0.tgz" + integrity sha512-7CbkJ1yWPfX0nHjbYG58BQThNhbICXBZynzCUxCb3LzX5X9B3hQbRY2STiRgIEiLILlK9fgl0z0QVGwPCdXf5g== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.5" + grunt-cli@~1.4.3: version "1.4.3" - resolved "https://registry.yarnpkg.com/grunt-cli/-/grunt-cli-1.4.3.tgz#22c9f1a3d2780bf9b0d206e832e40f8f499175ff" + resolved "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz" integrity sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ== dependencies: grunt-known-options "~2.0.0" @@ -8379,7 +8537,7 @@ grunt-cli@~1.4.3: grunt-contrib-clean@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/grunt-contrib-clean/-/grunt-contrib-clean-2.0.1.tgz#062e8019d31bfca35af8929a2ee1063c6c46dd2d" + resolved "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-2.0.1.tgz" integrity sha512-uRvnXfhiZt8akb/ZRDHJpQQtkkVkqc/opWO4Po/9ehC2hPxgptB9S6JHDC/Nxswo4CJSM0iFPT/Iym3cEMWzKA== dependencies: async "^3.2.3" @@ -8387,7 +8545,7 @@ grunt-contrib-clean@^2.0.0: grunt-contrib-concat@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-concat/-/grunt-contrib-concat-2.1.0.tgz#9ac62117a18b48d1bfccb3eef46c960bbd163d75" + resolved "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-2.1.0.tgz" integrity sha512-Vnl95JIOxfhEN7bnYIlCgQz41kkbi7tsZ/9a4usZmxNxi1S2YAIOy8ysFmO8u4MN26Apal1O106BwARdaNxXQw== dependencies: chalk "^4.1.2" @@ -8395,7 +8553,7 @@ grunt-contrib-concat@^2.1.0: grunt-contrib-connect@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-connect/-/grunt-contrib-connect-3.0.0.tgz#720e9ef39f976b804baf994345c2f6ecfdf3b264" + resolved "https://registry.npmjs.org/grunt-contrib-connect/-/grunt-contrib-connect-3.0.0.tgz" integrity sha512-L1GXk6PqDP/meX0IOX1MByBvOph6h8Pvx4/iBIYD7dpokVCAAQPR/IIV1jkTONEM09xig/Y8/y3R9Fqc8U3HSA== dependencies: async "^3.2.0" @@ -8410,7 +8568,7 @@ grunt-contrib-connect@^3.0.0: grunt-contrib-copy@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz#7060c6581e904b8ab0d00f076e0a8f6e3e7c3573" + resolved "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz" integrity sha512-gFRFUB0ZbLcjKb67Magz1yOHGBkyU6uL29hiEW1tdQ9gQt72NuMKIy/kS6dsCbV0cZ0maNCb0s6y+uT1FKU7jA== dependencies: chalk "^1.1.1" @@ -8418,7 +8576,7 @@ grunt-contrib-copy@^1.0.0: grunt-contrib-cssmin@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-cssmin/-/grunt-contrib-cssmin-5.0.0.tgz#c572bc20e8d085fcd66fc1c53dcd20d2c6e9b547" + resolved "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-5.0.0.tgz" integrity sha512-SNp4H4+85mm2xaHYi83FBHuOXylpi5vcwgtNoYCZBbkgeXQXoeTAKa59VODRb0woTDBvxouP91Ff5PzCkikg6g== dependencies: chalk "^4.1.2" @@ -8427,7 +8585,7 @@ grunt-contrib-cssmin@^5.0.0: grunt-contrib-uglify@^5.0.1: version "5.2.2" - resolved "https://registry.yarnpkg.com/grunt-contrib-uglify/-/grunt-contrib-uglify-5.2.2.tgz#447c0b58451a1fca20768371e07e723a870dfe98" + resolved "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-5.2.2.tgz" integrity sha512-ITxiWxrjjP+RZu/aJ5GLvdele+sxlznh+6fK9Qckio5ma8f7Iv8woZjRkGfafvpuygxNefOJNc+hfjjBayRn2Q== dependencies: chalk "^4.1.2" @@ -8437,7 +8595,7 @@ grunt-contrib-uglify@^5.0.1: grunt-contrib-watch@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz#c143ca5b824b288a024b856639a5345aedb78ed4" + resolved "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz" integrity sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg== dependencies: async "^2.6.0" @@ -8447,12 +8605,12 @@ grunt-contrib-watch@^1.1.0: grunt-known-options@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-2.0.0.tgz#cac641e897f9a0a680b8c9839803d35f3325103c" + resolved "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz" integrity sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA== grunt-legacy-log-utils@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz#49a8c7dc74051476dcc116c32faf9db8646856ef" + resolved "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz" integrity sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw== dependencies: chalk "~4.1.0" @@ -8460,7 +8618,7 @@ grunt-legacy-log-utils@~2.1.0: grunt-legacy-log@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz#1c6eaf92371ea415af31ea84ce50d434ef6d39c4" + resolved "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz" integrity sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA== dependencies: colors "~1.1.2" @@ -8470,7 +8628,7 @@ grunt-legacy-log@~3.0.0: grunt-legacy-util@~2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz#0f929d13a2faf9988c9917c82bff609e2d9ba255" + resolved "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz" integrity sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w== dependencies: async "~3.2.0" @@ -8483,12 +8641,12 @@ grunt-legacy-util@~2.0.1: grunt-sass@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/grunt-sass/-/grunt-sass-3.1.0.tgz#a5936cc2a80ec08092d9f31c101dc307d1e4f71c" + resolved "https://registry.npmjs.org/grunt-sass/-/grunt-sass-3.1.0.tgz" integrity sha512-90s27H7FoCDcA8C8+R0GwC+ntYD3lG6S/jqcavWm3bn9RiJTmSfOvfbFa1PXx4NbBWuiGQMLfQTj/JvvqT5w6A== -grunt@~1.5.3: +grunt@>=0.4.5, grunt@>=1, grunt@>=1.4.1, grunt@~1.5.3: version "1.5.3" - resolved "https://registry.yarnpkg.com/grunt/-/grunt-1.5.3.tgz#3214101d11257b7e83cf2b38ea173b824deab76a" + resolved "https://registry.npmjs.org/grunt/-/grunt-1.5.3.tgz" integrity sha512-mKwmo4X2d8/4c/BmcOETHek675uOqw0RuA/zy12jaspWqvTp4+ZeQF1W+OTpcbncnaBsfbQJ6l0l4j+Sn/GmaQ== dependencies: dateformat "~3.0.3" @@ -8509,7 +8667,7 @@ grunt@~1.5.3: "gt3-server-node-express-sdk@https://github.com/GaloyMoney/gt3-server-node-express-bypass#master": version "3.1.0" - resolved "https://github.com/GaloyMoney/gt3-server-node-express-bypass#6559a1a39ef408de3aefa6321934c831edac001e" + resolved "git+ssh://git@github.com/GaloyMoney/gt3-server-node-express-bypass.git#6559a1a39ef408de3aefa6321934c831edac001e" dependencies: async-redis "^2.0.0" axios "^1.3.4" @@ -8518,7 +8676,7 @@ grunt@~1.5.3: gtoken@^6.1.0: version "6.1.2" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-6.1.2.tgz#aeb7bdb019ff4c3ba3ac100bbe7b6e74dce0e8bc" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz" integrity sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ== dependencies: gaxios "^5.0.1" @@ -8527,7 +8685,7 @@ gtoken@^6.1.0: gtoken@^7.0.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz" integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== dependencies: gaxios "^6.0.0" @@ -8535,31 +8693,31 @@ gtoken@^7.0.0: gzip-size@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz" integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== dependencies: duplexer "^0.1.1" pify "^4.0.1" -handlebars@4.7.7: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" +handlebars@^4.7.7: + version "4.7.8" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: uglify-js "^3.1.4" -handlebars@^4.7.7: - version "4.7.8" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" - integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" - neo-async "^2.6.2" + neo-async "^2.6.0" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: @@ -8567,60 +8725,60 @@ handlebars@^4.7.7: has-ansi@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== dependencies: ansi-regex "^2.0.0" has-bigints@^1.0.2: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz" integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" has-proto@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz" integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== dependencies: dunder-proto "^1.0.0" has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-tostringtag@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" has-unicode@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== hash-base@^3.0.0, hash-base@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.2.tgz#79d72def7611c3f6e3c3b5730652638001b10a74" + resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz" integrity sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg== dependencies: inherits "^2.0.4" @@ -8630,7 +8788,7 @@ hash-base@^3.0.0, hash-base@^3.1.2: hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" @@ -8638,24 +8796,24 @@ hash.js@^1.0.0, hash.js@^1.0.3: hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" help-me@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + resolved "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz" integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== highlight.js@^11.4.0: version "11.11.1" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.11.1.tgz#fca06fa0e5aeecf6c4d437239135fabc15213585" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz" integrity sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w== hmac-drbg@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== dependencies: hash.js "^1.0.3" @@ -8664,36 +8822,41 @@ hmac-drbg@^1.0.1: hoist-non-react-statics@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" homedir-polyfill@^1.0.1: version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + resolved "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz" integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== dependencies: parse-passwd "^1.0.0" +hono@^4: + version "4.11.5" + resolved "https://registry.npmjs.org/hono/-/hono-4.11.5.tgz" + integrity sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g== + hooker@~0.2.3: version "0.2.3" - resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959" + resolved "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" integrity sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA== html-entities@^2.5.2: version "2.6.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.6.0.tgz#7c64f1ea3b36818ccae3d3fb48b6974208e984f8" + resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz" integrity sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ== html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== htmlparser2@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-10.0.0.tgz#77ad249037b66bf8cc99c6e286ef73b83aeb621d" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz" integrity sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g== dependencies: domelementtype "^2.3.0" @@ -8703,7 +8866,7 @@ htmlparser2@^10.0.0: htmlparser2@~9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.0.0.tgz#e431142b7eeb1d91672742dea48af8ac7140cddb" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.0.0.tgz" integrity sha512-uxbSI98wmFT/G4P2zXx4OVx04qWUmyFPrD2/CNepa2Zo3GPNaCaaxElDgwUrwYWkK1nr9fft0Ya8dws8coDLLQ== dependencies: domelementtype "^2.3.0" @@ -8713,7 +8876,7 @@ htmlparser2@~9.0.0: http-basic@^8.1.1: version "8.1.3" - resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" + resolved "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz" integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== dependencies: caseless "^0.12.0" @@ -8721,25 +8884,20 @@ http-basic@^8.1.1: http-response-object "^3.0.1" parse-cache-control "^1.0.1" -http-cache-semantics@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" http-errors@~1.6.2: version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== dependencies: depd "~1.1.2" @@ -8747,14 +8905,25 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + http-parser-js@>=0.5.1: version "0.5.10" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.10.tgz#b3277bd6d7ed5588e20ea73bf724fcbe44609075" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz" integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA== http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" @@ -8763,24 +8932,24 @@ http-proxy-agent@^5.0.0: http-response-object@^3.0.1: version "3.0.2" - resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" + resolved "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz" integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== dependencies: "@types/node" "^10.0.3" http2-client@^1.2.5: version "1.3.5" - resolved "https://registry.yarnpkg.com/http2-client/-/http2-client-1.3.5.tgz#20c9dc909e3cc98284dd20af2432c524086df181" + resolved "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz" integrity sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA== https-browserify@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz" integrity sha512-EjDQFbgJr1vDD/175UJeSX3ncQ3+RUnCL5NkthQGHvF4VNHlzTy8ifJfTqz47qiPRqaFH58+CbuG3x51WuB1XQ== https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -8788,7 +8957,7 @@ https-proxy-agent@^5.0.0: https-proxy-agent@^7.0.1: version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: agent-base "^7.1.2" @@ -8796,12 +8965,12 @@ https-proxy-agent@^7.0.1: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== i18n@^0.15.1: version "0.15.2" - resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.15.2.tgz#0f73b9d3545c2a7b00ea34d134d0c2d5b75aa38b" + resolved "https://registry.npmjs.org/i18n/-/i18n-0.15.2.tgz" integrity sha512-mdBxCfC651UL/hNizIQgB1NHwbBKjlrPcsoTzd/X8rNbJlS1FMF//TOyHEVFg9Dxo0RcrI2ZKt1AFTNe3Q40og== dependencies: "@messageformat/core" "^3.4.0" @@ -8813,64 +8982,78 @@ i18n@^0.15.1: ibex-client@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/ibex-client/-/ibex-client-2.2.0.tgz#8fd0aa95611b4d2a7bd84a486c22a8918521583d" + resolved "https://registry.npmjs.org/ibex-client/-/ibex-client-2.2.0.tgz" integrity sha512-RCokoz7YXrCzLPrY+bNYCwHvPPX8cragPgBhCQcPDHWAf5dfPSyW+A0nCx/kkYyEhrHfpBmE17jHVCL82Gqicg== dependencies: api "^6.1.2" node-cache "^5.1.2" -iconv-lite@0.4.24, iconv-lite@~0.4.13: +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.2" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +iconv-lite@~0.4.13, iconv-lite@0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.3: +iconv-lite@0.6.3: version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-by-default@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4: version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== image-size@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.0.tgz#58b31fe4743b1cec0a0ac26f5c914d3c5b2f0750" + resolved "https://registry.npmjs.org/image-size/-/image-size-1.0.0.tgz" integrity sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw== dependencies: queue "6.0.2" immutable@^5.0.2: version "5.1.4" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.4.tgz#e3f8c1fe7b567d56cf26698f31918c241dae8c1f" + resolved "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz" integrity sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA== import-fresh@^3.2.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz" integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@1.4.2, import-in-the-middle@1.7.1: +import-in-the-middle@1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + resolved "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz" integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== dependencies: acorn "^8.8.2" @@ -8878,9 +9061,19 @@ import-in-the-middle@1.4.2, import-in-the-middle@1.7.1: cjs-module-lexer "^1.2.2" module-details-from-path "^1.0.3" +import-in-the-middle@1.7.1: + version "1.7.1" + resolved "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz" + integrity sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + import-local@^3.0.2: version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" @@ -8888,45 +9081,45 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indexes-of@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4, inherits@2, inherits@2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== inherits@2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== ini@^1.3.4, ini@~1.3.0: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== internal-slot@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz" integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== dependencies: es-errors "^1.3.0" @@ -8935,17 +9128,17 @@ internal-slot@^1.1.0: interpret@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== interpret@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + resolved "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz" integrity sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA== -invoices@3.0.0, invoices@^3.0.0: +invoices@^3.0.0, invoices@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/invoices/-/invoices-3.0.0.tgz#70e3478aa084fb7fca9f54093ca9afa507fb80ba" + resolved "https://registry.npmjs.org/invoices/-/invoices-3.0.0.tgz" integrity sha512-/WDTkfU2RMelQpQ54BwZssqGXYNWbPnWkZ/9QV57vAvD3RLdCDbhDuucOGti8CK3sgk8nmhRV6V0WfMrxojMmA== dependencies: bech32 "2.0.0" @@ -8957,14 +9150,14 @@ invoices@3.0.0, invoices@^3.0.0: ioredis-cache@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/ioredis-cache/-/ioredis-cache-2.0.3.tgz#581dd9c6ec8c99f5880144a5e728f9b3677a712a" + resolved "https://registry.npmjs.org/ioredis-cache/-/ioredis-cache-2.0.3.tgz" integrity sha512-BTwbzfVXfPPBULRLPJBm4M1gGFMR5Eg4dHWAGCb6kWEIlsaOiNgWbUqImGwEHpW4bMRm991Grtnl5Z4qakHfsw== dependencies: ioredis "4 - 5" -"ioredis@4 - 5", ioredis@^5.3.2: +ioredis@^5.3.2, "ioredis@4 - 5": version "5.8.2" - resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.8.2.tgz#c7a228a26cf36f17a5a8011148836877780e2e14" + resolved "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz" integrity sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q== dependencies: "@ioredis/commands" "1.4.0" @@ -8979,22 +9172,22 @@ ioredis-cache@^2.0.0: ip-address@^10.0.1: version "10.0.1" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.0.1.tgz#a8180b783ce7788777d796286d61bce4276818ed" + resolved "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz" integrity sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA== -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - ipaddr.js@^2.1.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== +ipaddr.js@1.9.1: + 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== + is-absolute@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" @@ -9002,7 +9195,7 @@ is-absolute@^1.0.0: is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz" integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== dependencies: call-bind "^1.0.8" @@ -9011,12 +9204,12 @@ is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-async-function@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz" integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== dependencies: async-function "^1.0.0" @@ -9027,21 +9220,21 @@ is-async-function@^2.0.0: is-bigint@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz" integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== dependencies: has-bigints "^1.0.2" is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.2.1: version "1.2.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz" integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== dependencies: call-bound "^1.0.3" @@ -9049,24 +9242,24 @@ is-boolean-object@^1.2.1: is-buffer@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.13.0, is-core-module@^2.16.1: version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" is-data-view@^1.0.1, is-data-view@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz" integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== dependencies: call-bound "^1.0.2" @@ -9075,7 +9268,7 @@ is-data-view@^1.0.1, is-data-view@^1.0.2: is-date-object@^1.0.5, is-date-object@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz" integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== dependencies: call-bound "^1.0.2" @@ -9083,29 +9276,29 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-finalizationregistry@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz" integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== dependencies: call-bound "^1.0.3" is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-generator-function@^1.0.10: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz" integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: call-bound "^1.0.4" @@ -9116,36 +9309,36 @@ is-generator-function@^1.0.10: is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== is-map@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== is-negative-zero@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== is-number-like@^1.0.3: version "1.0.8" - resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3" + resolved "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz" integrity sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA== dependencies: lodash.isfinite "^3.3.2" is-number-object@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz" integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== dependencies: call-bound "^1.0.3" @@ -9153,34 +9346,39 @@ is-number-object@^1.1.1: is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== is-path-inside@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-promise@^2.2.2: version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-regex@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz" integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: call-bound "^1.0.2" @@ -9190,46 +9388,46 @@ is-regex@^1.2.1: is-regexp@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== is-relative-path@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" + resolved "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.2.tgz" integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA== is-relative@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" is-set@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== is-shared-array-buffer@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz" integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== dependencies: call-bound "^1.0.3" is-stream-ended@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" + resolved "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz" integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz" integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== dependencies: call-bound "^1.0.3" @@ -9237,7 +9435,7 @@ is-string@^1.1.1: is-symbol@^1.0.4, is-symbol@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz" integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== dependencies: call-bound "^1.0.2" @@ -9246,48 +9444,48 @@ is-symbol@^1.0.4, is-symbol@^1.1.1: is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: version "1.1.15" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== dependencies: which-typed-array "^1.1.16" is-unc-path@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-url-superb@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" + resolved "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz" integrity sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA== is-url@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== is-weakmap@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== is-weakref@^1.0.2, is-weakref@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz" integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== dependencies: call-bound "^1.0.3" is-weakset@^2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz" integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== dependencies: call-bound "^1.0.3" @@ -9295,37 +9493,37 @@ is-weakset@^2.0.3: is-windows@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== isarray@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isomorphic-fetch@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz" integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== dependencies: node-fetch "^2.6.1" @@ -9333,12 +9531,12 @@ isomorphic-fetch@^3.0.0: istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" @@ -9349,7 +9547,7 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" @@ -9360,7 +9558,7 @@ istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: istanbul-lib-report@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz" integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -9369,7 +9567,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" @@ -9378,7 +9576,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.1.3: version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz" integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== dependencies: html-escaper "^2.0.0" @@ -9386,12 +9584,12 @@ istanbul-reports@^3.1.3: iterall@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== jackspeak@^3.1.2: version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: "@isaacs/cliui" "^8.0.2" @@ -9400,14 +9598,19 @@ jackspeak@^3.1.2: jackspeak@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz" integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ== dependencies: "@isaacs/cliui" "^8.0.2" +jest_workaround@^0.79.19: + version "0.79.19" + resolved "https://registry.npmjs.org/jest_workaround/-/jest_workaround-0.79.19.tgz" + integrity sha512-g/MtKSwyb4Ohnd5GHeJaduTgznkyst81x+eUBGOSGK7f8doWuRMPpt6XM/13sM2jLB2QNzT/7Djj7o2PhsozIA== + jest-changed-files@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz" integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" @@ -9416,7 +9619,7 @@ jest-changed-files@^29.7.0: jest-circus@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz" integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: "@jest/environment" "^29.7.0" @@ -9442,7 +9645,7 @@ jest-circus@^29.7.0: jest-cli@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz" integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: "@jest/core" "^29.7.0" @@ -9459,7 +9662,7 @@ jest-cli@^29.7.0: jest-config@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz" integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" @@ -9487,7 +9690,7 @@ jest-config@^29.7.0: jest-diff@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz" integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" @@ -9497,14 +9700,14 @@ jest-diff@^29.7.0: jest-docblock@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz" integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" jest-each@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz" integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: "@jest/types" "^29.6.3" @@ -9515,7 +9718,7 @@ jest-each@^29.7.0: jest-environment-node@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== dependencies: "@jest/environment" "^29.7.0" @@ -9527,30 +9730,12 @@ jest-environment-node@^29.7.0: jest-get-type@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e" - integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== - dependencies: - "@jest/types" "30.2.0" - "@types/node" "*" - anymatch "^3.1.3" - fb-watchman "^2.0.2" - graceful-fs "^4.2.11" - jest-regex-util "30.0.1" - jest-util "30.2.0" - jest-worker "30.2.0" - micromatch "^4.0.8" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.3" - jest-haste-map@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz" integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: "@jest/types" "^29.6.3" @@ -9567,9 +9752,27 @@ jest-haste-map@^29.7.0: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@30.2.0: + version "30.2.0" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz" + integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== + dependencies: + "@jest/types" "30.2.0" + "@types/node" "*" + anymatch "^3.1.3" + fb-watchman "^2.0.2" + graceful-fs "^4.2.11" + jest-regex-util "30.0.1" + jest-util "30.2.0" + jest-worker "30.2.0" + micromatch "^4.0.8" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.3" + jest-junit@^16.0.0: version "16.0.0" - resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-16.0.0.tgz#d838e8c561cf9fdd7eb54f63020777eee4136785" + resolved "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz" integrity sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ== dependencies: mkdirp "^1.0.4" @@ -9579,7 +9782,7 @@ jest-junit@^16.0.0: jest-leak-detector@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz" integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: jest-get-type "^29.6.3" @@ -9587,7 +9790,7 @@ jest-leak-detector@^29.7.0: jest-matcher-utils@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz" integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" @@ -9597,7 +9800,7 @@ jest-matcher-utils@^29.7.0: jest-message-util@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz" integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" @@ -9612,7 +9815,7 @@ jest-message-util@^29.7.0: jest-mock@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: "@jest/types" "^29.6.3" @@ -9621,30 +9824,30 @@ jest-mock@^29.7.0: jest-pnp-resolver@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@30.0.1: - version "30.0.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" - integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== - jest-regex-util@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== +jest-regex-util@30.0.1: + version "30.0.1" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz" + integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== + jest-resolve-dependencies@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz" integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@^29.7.0: +jest-resolve@*, jest-resolve@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" @@ -9659,7 +9862,7 @@ jest-resolve@^29.7.0: jest-runner@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz" integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: "@jest/console" "^29.7.0" @@ -9686,7 +9889,7 @@ jest-runner@^29.7.0: jest-runtime@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz" integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== dependencies: "@jest/environment" "^29.7.0" @@ -9714,7 +9917,7 @@ jest-runtime@^29.7.0: jest-snapshot@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz" integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" @@ -9738,21 +9941,9 @@ jest-snapshot@^29.7.0: pretty-format "^29.7.0" semver "^7.5.3" -jest-util@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" - integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== - dependencies: - "@jest/types" "30.2.0" - "@types/node" "*" - chalk "^4.1.2" - ci-info "^4.2.0" - graceful-fs "^4.2.11" - picomatch "^4.0.2" - jest-util@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: "@jest/types" "^29.6.3" @@ -9762,9 +9953,21 @@ jest-util@^29.7.0: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@30.2.0: + version "30.2.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz" + integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== + dependencies: + "@jest/types" "30.2.0" + "@types/node" "*" + chalk "^4.1.2" + ci-info "^4.2.0" + graceful-fs "^4.2.11" + picomatch "^4.0.2" + jest-validate@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz" integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: "@jest/types" "^29.6.3" @@ -9776,7 +9979,7 @@ jest-validate@^29.7.0: jest-watcher@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz" integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: "@jest/test-result" "^29.7.0" @@ -9788,9 +9991,19 @@ jest-watcher@^29.7.0: jest-util "^29.7.0" string-length "^4.0.1" +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest-worker@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.2.0.tgz#fd5c2a36ff6058ec8f74366ec89538cc99539d26" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz" integrity sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g== dependencies: "@types/node" "*" @@ -9799,19 +10012,9 @@ jest-worker@30.2.0: merge-stream "^2.0.0" supports-color "^8.1.1" -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.7.0: +jest@*, jest@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: "@jest/core" "^29.7.0" @@ -9819,24 +10022,24 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -jest_workaround@^0.79.19: - version "0.79.19" - resolved "https://registry.yarnpkg.com/jest_workaround/-/jest_workaround-0.79.19.tgz#f5bc569163c33c4533e1c4053c533a8811152a91" - integrity sha512-g/MtKSwyb4Ohnd5GHeJaduTgznkyst81x+eUBGOSGK7f8doWuRMPpt6XM/13sM2jLB2QNzT/7Djj7o2PhsozIA== - jose@^4.15.4: version "4.15.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" + resolved "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz" integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== +jose@^6.1.1: + version "6.1.3" + resolved "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz" + integrity sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ== + joycon@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== js-beautify@~1.14.7: version "1.14.11" - resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.11.tgz#57b17e009549ac845bdc58eddf8e1862e311314e" + resolved "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz" integrity sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw== dependencies: config-chain "^1.1.13" @@ -9846,22 +10049,22 @@ js-beautify@~1.14.7: js-md5@^0.7.3: version "0.7.3" - resolved "https://registry.yarnpkg.com/js-md5/-/js-md5-0.7.3.tgz#b4f2fbb0b327455f598d6727e38ec272cd09c3f2" + resolved "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz" integrity sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ== js-sha1@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/js-sha1/-/js-sha1-0.6.0.tgz#adbee10f0e8e18aa07cdea807cf08e9183dbc7f9" + resolved "https://registry.npmjs.org/js-sha1/-/js-sha1-0.6.0.tgz" integrity sha512-01gwBFreYydzmU9BmZxpVk6svJJHrVxEN3IOiGl6VO93bVKYETJ0sIth6DASI6mIFdt7NmfX9UiByRzsYHGU9w== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1, js-yaml@~3.14.0: +js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -9869,21 +10072,29 @@ js-yaml@^3.13.1, js-yaml@~3.14.0: js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" +js-yaml@~3.14.0: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js2xmlparser@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.2.tgz#2a1fdf01e90585ef2ae872a01bc169c6a8d5e60a" + resolved "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz" integrity sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA== dependencies: xmlcreate "^2.0.4" jsdoc@^4.0.0: version "4.0.5" - resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-4.0.5.tgz#fbed70e04a3abcf2143dad6b184947682bbc7315" + resolved "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz" integrity sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g== dependencies: "@babel/parser" "^7.20.15" @@ -9904,41 +10115,41 @@ jsdoc@^4.0.0: jsesc@^3.0.2, jsesc@~3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== json-bigint@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz" integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== dependencies: bignumber.js "^9.0.0" json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + 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== json-rpc-2.0@^1.6.0: version "1.7.1" - resolved "https://registry.yarnpkg.com/json-rpc-2.0/-/json-rpc-2.0-1.7.1.tgz#f4a5095d0a7e51701bffe153ce19087a8b13418e" + resolved "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.7.1.tgz" integrity sha512-JqZjhjAanbpkXIzFE7u8mE/iFblawwlXtONaCvRqI+pyABVz7B4M1EUNpyVW+dZjqgQ2L5HFmZCmOCgUKm00hg== json-schema-compare@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/json-schema-compare/-/json-schema-compare-0.2.2.tgz#dd601508335a90c7f4cfadb6b2e397225c908e56" + resolved "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz" integrity sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ== dependencies: lodash "^4.17.4" json-schema-merge-allof@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz#ed2828cdd958616ff74f932830a26291789eaaf2" + resolved "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz" integrity sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w== dependencies: compute-lcm "^1.1.2" @@ -9947,7 +10158,7 @@ json-schema-merge-allof@^0.8.1: json-schema-to-ts@^2.6.2-beta.0: version "2.12.0" - resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-2.12.0.tgz#fd8bc79cebadc7bee5be1e32b289f4f7e1c2e4ae" + resolved "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-2.12.0.tgz" integrity sha512-uTde38yBm5lzJSRPWRaasxZo72pb+JGE4iUksNdNfAkFaLhV4N9akeBxPPUpZy5onINt9Zo0oTLrAoEXyZESiQ== dependencies: "@babel/runtime" "^7.18.3" @@ -9956,49 +10167,54 @@ json-schema-to-ts@^2.6.2-beta.0: json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stringify-pretty-compact@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz#f71ef9d82ef16483a407869556588e91b681d9ab" + resolved "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz" integrity sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA== json5@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== - jsonc-parser@^3.2.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz" integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== +jsonc-parser@3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + jsonfile@^6.0.1: version "6.2.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz" integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== dependencies: universalify "^2.0.0" @@ -10007,7 +10223,7 @@ jsonfile@^6.0.1: jsonpath@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901" + resolved "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz" integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== dependencies: esprima "1.2.2" @@ -10016,12 +10232,12 @@ jsonpath@^1.1.1: jsonpointer@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== jsonwebtoken@^9.0.0, jsonwebtoken@^9.0.2: version "9.0.2" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz" integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== dependencies: jws "^3.2.2" @@ -10037,7 +10253,7 @@ jsonwebtoken@^9.0.0, jsonwebtoken@^9.0.2: jwa@^1.4.1: version "1.4.2" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" + resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz" integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== dependencies: buffer-equal-constant-time "^1.0.1" @@ -10046,7 +10262,7 @@ jwa@^1.4.1: jwa@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804" + resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz" integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== dependencies: buffer-equal-constant-time "^1.0.1" @@ -10055,7 +10271,7 @@ jwa@^2.0.0: jwks-rsa@^3.0.1: version "3.2.0" - resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-3.2.0.tgz#132bc8bfa7b03928a273bbc93486f70226449e04" + resolved "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz" integrity sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww== dependencies: "@types/express" "^4.17.20" @@ -10067,7 +10283,7 @@ jwks-rsa@^3.0.1: jws@^3.2.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: jwa "^1.4.1" @@ -10075,7 +10291,7 @@ jws@^3.2.2: jws@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== dependencies: jwa "^2.0.0" @@ -10083,36 +10299,36 @@ jws@^4.0.0: kareem@2.5.1: version "2.5.1" - resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.5.1.tgz#7b8203e11819a8e77a34b3517d3ead206764d15d" + resolved "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz" integrity sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA== keyv@^4.5.3: version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== klaw@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146" + resolved "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz" integrity sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g== dependencies: graceful-fs "^4.1.9" kleur@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knex@^2.5.1: version "2.5.1" - resolved "https://registry.yarnpkg.com/knex/-/knex-2.5.1.tgz#a6c6b449866cf4229f070c17411f23871ba52ef9" + resolved "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz" integrity sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA== dependencies: colorette "2.0.19" @@ -10132,12 +10348,12 @@ knex@^2.5.1: leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -10145,7 +10361,7 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== dependencies: prelude-ls "~1.1.2" @@ -10153,12 +10369,12 @@ levn@~0.3.0: libphonenumber-js@^1.10.44: version "1.12.25" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.25.tgz#1af48b816082100bf88f47d342387fbac1f1a773" + resolved "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.25.tgz" integrity sha512-u90tUu/SEF8b+RaDKCoW7ZNFDakyBtFlX1ex3J+VH+ElWes/UaitJLt/w4jGu8uAE41lltV/s+kMVtywcMEg7g== liftup@~3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/liftup/-/liftup-3.0.1.tgz#1cb81aff0f368464ed3a5f1a7286372d6b1a60ce" + resolved "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz" integrity sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw== dependencies: extend "^3.0.2" @@ -10170,9 +10386,9 @@ liftup@~3.0.1: rechoir "^0.7.0" resolve "^1.19.0" -lightning@9.14.0, lightning@^9.13.3: +lightning@^9.13.3, lightning@9.14.0: version "9.14.0" - resolved "https://registry.yarnpkg.com/lightning/-/lightning-9.14.0.tgz#d5b93306ffe2b4f4f01d866eacf40770d744887b" + resolved "https://registry.npmjs.org/lightning/-/lightning-9.14.0.tgz" integrity sha512-08TEh9McKE6uMlCLulNPPyMzZcMBPzZDo2BMnRL+ImnkKZp8QCjioK4jdsl/41npDTsIgeSYdfk8vUrS0zSZng== dependencies: "@grpc/grpc-js" "1.9.4" @@ -10198,29 +10414,29 @@ lightning@9.14.0, lightning@^9.13.3: limiter@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" + resolved "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz" integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== linkify-it@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz" integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== dependencies: uc.micro "^2.0.0" livereload-js@^2.3.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" + resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz" integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== ln-service@^56.13.0: version "56.14.0" - resolved "https://registry.yarnpkg.com/ln-service/-/ln-service-56.14.0.tgz#7f7b00513e283c472ee51b247c40ef1c6da9956c" + resolved "https://registry.npmjs.org/ln-service/-/ln-service-56.14.0.tgz" integrity sha512-3Bs3TzaZZVnL3OF4WNcs6daMp4uA54zgxPIaM6XUYPEIa9qHKUHacPrgOy9/ScDwywrZ3eFI/G9EJeNYngao7g== dependencies: bolt07 "1.8.4" @@ -10234,151 +10450,151 @@ ln-service@^56.13.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash-es@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.clonedeep@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.defaults@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== lodash.difference@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + resolved "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz" integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== lodash.find@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" + resolved "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz" integrity sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg== lodash.get@^4.4.2: version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== lodash.groupby@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + resolved "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz" integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== lodash.includes@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== lodash.isarguments@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + resolved "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz" integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== lodash.isboolean@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== lodash.isfinite@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3" + resolved "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz" integrity sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA== lodash.isinteger@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== lodash.isnumber@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== lodash.isplainobject@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== lodash.isstring@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== lodash.map@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + resolved "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz" integrity sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q== lodash.mapvalues@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + resolved "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz" integrity sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.mergewith@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== lodash.once@^4.0.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== lodash.sortby@^4.7.0: version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== lodash.sumby@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.sumby/-/lodash.sumby-4.6.0.tgz#7d87737ddb216da2f7e5e7cd2dd9c403a7887346" + resolved "https://registry.npmjs.org/lodash.sumby/-/lodash.sumby-4.6.0.tgz" integrity sha512-DCF6ONVffD6cdnZzZ7Ewhl23AOvXAvwGTfabmj4+WWkX8nXvyklgLkT5IyGt3kxX5CgfvQgLgYRRvEDsi7jTOQ== lodash.unset@^4.5.2: version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.unset/-/lodash.unset-4.5.2.tgz#370d1d3e85b72a7e1b0cdf2d272121306f23e4ed" + resolved "https://registry.npmjs.org/lodash.unset/-/lodash.unset-4.5.2.tgz" integrity sha512-bwKX88k2JhCV9D1vtE8+naDKlLiGrSmf8zi/Y9ivFHwbmRfA8RxS/aVJ+sIht2XOwqoNr4xUPUkGZpc1sHFEKg== lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@~4.17.19, lodash@~4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -10386,58 +10602,58 @@ log-symbols@^4.1.0: loglevel@^1.6.8: version "1.9.2" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08" + resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz" integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg== long@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== long@^5.0.0, long@^5.2.0: version "5.3.2" - resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + resolved "https://registry.npmjs.org/long/-/long-5.3.2.tgz" integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru-cache@6.0.0, lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -"lru-cache@7.10.1 - 7.13.1": - version "7.13.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4" - integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== - lru-cache@^10.2.0: version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^11.0.0: version "11.2.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz" integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" +lru-cache@^6.0.0, lru-cache@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +"lru-cache@7.10.1 - 7.13.1": + version "7.13.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz" + integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== + lru-memoizer@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.3.0.tgz#ef0fbc021bceb666794b145eefac6be49dc47f31" + resolved "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz" integrity sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug== dependencies: lodash.clonedeep "^4.5.0" @@ -10445,14 +10661,14 @@ lru-memoizer@^2.2.0: lru-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + resolved "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz" integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== dependencies: es5-ext "~0.10.2" macaroon@3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/macaroon/-/macaroon-3.0.4.tgz#dac1a4b17cd973c1000703f40b19bdbb1d6191ce" + resolved "https://registry.npmjs.org/macaroon/-/macaroon-3.0.4.tgz" integrity sha512-Tja2jvupseKxltPZbu5RPSz2Pgh6peYA3O46YCTcYL8PI1VqtGwDqRhGfP8pows26xx9wTiygk+en62Bq+Y8JA== dependencies: sjcl "^1.0.6" @@ -10461,7 +10677,7 @@ macaroon@3.0.4: madge@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/madge/-/madge-6.1.0.tgz#9835bb53f2e00d184914c2b007b50736a964d297" + resolved "https://registry.npmjs.org/madge/-/madge-6.1.0.tgz" integrity sha512-irWhT5RpFOc6lkzGHKLihonCVgM0YtfNUh4IrFeW3EqHpnt/JHUG3z26j8PeJEktCGB4tmGOOOJi1Rl/ACWucQ== dependencies: chalk "^4.1.1" @@ -10489,55 +10705,55 @@ madge@^6.1.0: make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-dir@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: semver "^7.5.3" make-error@^1.1.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== make-iterator@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + resolved "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz" integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== dependencies: kind-of "^6.0.2" make-plural@^7.0.0, make-plural@^7.4.0: version "7.4.0" - resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d" + resolved "https://registry.npmjs.org/make-plural/-/make-plural-7.4.0.tgz" integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg== makeerror@1.0.12: version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" map-cache@^0.2.0: version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== markdown-it-anchor@^8.6.7: version "8.6.7" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz#ee6926daf3ad1ed5e4e3968b1740eef1c6399634" + resolved "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz" integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA== -markdown-it@^14.1.0: +markdown-it@*, markdown-it@^14.1.0: version "14.1.0" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz" integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== dependencies: argparse "^2.0.1" @@ -10549,22 +10765,22 @@ markdown-it@^14.1.0: marked@^4.0.10, marked@^4.0.12, marked@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + resolved "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== math-interval-parser@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/math-interval-parser/-/math-interval-parser-2.0.1.tgz#e22cd6d15a0a7f4c03aec560db76513da615bed4" + resolved "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz" integrity sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA== math-intrinsics@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== maxmin@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/maxmin/-/maxmin-3.0.0.tgz#3ee9acc8a2b9f2b5416e94f5705319df8a9c71e6" + resolved "https://registry.npmjs.org/maxmin/-/maxmin-3.0.0.tgz" integrity sha512-wcahMInmGtg/7c6a75fr21Ch/Ks1Tb+Jtoan5Ft4bAI0ZvJqyOw8kkM7e7p8hDSzY805vmxwHT50KcjGwKyJ0g== dependencies: chalk "^4.1.0" @@ -10574,7 +10790,7 @@ maxmin@^3.0.0: md5.js@^1.3.4: version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== dependencies: hash-base "^3.0.0" @@ -10583,24 +10799,29 @@ md5.js@^1.3.4: mdurl@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz" integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + media-typer@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== medici@^6.2.0: version "6.3.3" - resolved "https://registry.yarnpkg.com/medici/-/medici-6.3.3.tgz#131b24e696eb7f7669966884151a67b04cbf6fd1" + resolved "https://registry.npmjs.org/medici/-/medici-6.3.3.tgz" integrity sha512-qz/IvdT2jcREGbKqtQA5ytYLQRVFYo1pY5XvtMc7NI5Yg9irR3g8PMFyBsk7PeS4F5RuLP8S8nFZ9RxxHeK6yQ== dependencies: mongoose "5 - 8" memoizee@^0.4.14: version "0.4.17" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.17.tgz#942a5f8acee281fa6fb9c620bddc57e3b7382949" + resolved "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz" integrity sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA== dependencies: d "^1.0.2" @@ -10614,37 +10835,42 @@ memoizee@^0.4.14: memory-pager@^1.0.2: version "1.5.0" - resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" + resolved "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz" integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== merge-descriptors@1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== microfiber@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/microfiber/-/microfiber-2.1.1.tgz#a8935f242eb322f6f0eba472edc5951c8b55dbbf" + resolved "https://registry.npmjs.org/microfiber/-/microfiber-2.1.1.tgz" integrity sha512-lLHfeAXpPQZIU7/UMwtNtNXpb3PphaNZJSVwEntkRl9ir+lgJr534dTXfbILE1hksNMggOYwAZhAGFrgiySc5A== dependencies: lodash.defaults "^4.2.0" @@ -10653,7 +10879,7 @@ microfiber@^2.0.1: micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -10661,7 +10887,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: migrate-mongo@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/migrate-mongo/-/migrate-mongo-10.0.0.tgz#c29c7e80dcbdb548e172c7395ccc439cdece1bc6" + resolved "https://registry.npmjs.org/migrate-mongo/-/migrate-mongo-10.0.0.tgz" integrity sha512-QA/bBKNMq/FmuK3fDbgwfNoW2riiU1wLDWGXv/tGhUItPLGqcciPPmu29SrnYAzRKMOVaGEXxzmrBs1zp5cQ7Q== dependencies: cli-table3 "^0.6.1" @@ -10672,127 +10898,153 @@ migrate-mongo@^10.0.0: lodash "^4.17.21" p-each-series "^2.2.0" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== "mime-db@>= 1.43.0 < 2": version "1.54.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" mime@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mimer@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/mimer/-/mimer-2.0.2.tgz#941da26070e80bb485aed8a1ef4a5a325cbbfa96" + resolved "https://registry.npmjs.org/mimer/-/mimer-2.0.2.tgz" integrity sha512-izxvjsB7Ur5HrTbPu6VKTrzxSMBFBqyZQc6dWlZNQ4/wAvf886fD4lrjtFd8IQ8/WmZKdxKjUtqFFNaj3hQ52g== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2, "minimatch@2 || 3": version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" - integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== - dependencies: - brace-expansion "^2.0.1" - -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" -minimatch@^10.0.3: - version "10.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" - integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== - dependencies: - "@isaacs/brace-expansion" "^5.0.0" - -minimatch@^5.0.1, minimatch@^5.1.0: +minimatch@^5.1.0: version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" minimatch@^9.0.4: version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" -minimatch@~3.0.2, minimatch@~3.0.4: +minimatch@~3.0.2: version "3.0.8" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== dependencies: brace-expansion "^1.1.7" +minimatch@~3.0.4: + version "3.0.8" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" + integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== + dependencies: + brace-expansion "^1.1.7" + +minimatch@9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz" + integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + dependencies: + brace-expansion "^2.0.1" + +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minipass@^3.0.0: version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3, minipass@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + minizlib@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" @@ -10800,12 +11052,12 @@ minizlib@^2.1.1: mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== module-definition@^3.3.1: version "3.4.0" - resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.4.0.tgz#953a3861f65df5e43e80487df98bb35b70614c2b" + resolved "https://registry.npmjs.org/module-definition/-/module-definition-3.4.0.tgz" integrity sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA== dependencies: ast-module-types "^3.0.0" @@ -10813,7 +11065,7 @@ module-definition@^3.3.1: module-definition@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-4.1.0.tgz#148ff9348e3401867229dcbe5947f4f6d5ccd3a2" + resolved "https://registry.npmjs.org/module-definition/-/module-definition-4.1.0.tgz" integrity sha512-rHXi/DpMcD2qcKbPCTklDbX9lBKJrUSl971TW5l6nMpqKCIlzJqmQ8cfEF5M923h2OOLHPDVlh5pJxNyV+AJlw== dependencies: ast-module-types "^4.0.0" @@ -10821,12 +11073,12 @@ module-definition@^4.1.0: module-details-from-path@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz#b662fdcd93f6c83d3f25289da0ce81c8d9685b94" + resolved "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz" integrity sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w== module-lookup-amd@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz#d67c1a93f2ff8e38b8774b99a638e9a4395774b2" + resolved "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz" integrity sha512-w9mCNlj0S8qviuHzpakaLVc+/7q50jl9a/kmJ/n8bmXQZgDPkQHnPBb8MUOYh3WpAYkXuNc2c+khsozhIp/amQ== dependencies: commander "^2.8.1" @@ -10837,7 +11089,7 @@ module-lookup-amd@^7.0.1: mongodb-connection-string-url@^2.6.0: version "2.6.0" - resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf" + resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz" integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ== dependencies: "@types/whatwg-url" "^8.2.1" @@ -10845,26 +11097,15 @@ mongodb-connection-string-url@^2.6.0: mongodb-connection-string-url@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz#e223089dfa0a5fa9bf505f8aedcbc67b077b33e7" + resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz" integrity sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA== dependencies: "@types/whatwg-url" "^11.0.2" whatwg-url "^14.1.0 || ^13.0.0" -mongodb@5.8.1: - version "5.8.1" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.8.1.tgz#dc201adfbd6c6d73401cdcf12ebdb75f14771faf" - integrity sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg== - dependencies: - bson "^5.4.0" - mongodb-connection-string-url "^2.6.0" - socks "^2.7.1" - optionalDependencies: - "@mongodb-js/saslprep" "^1.1.0" - -mongodb@5.9.2: +"mongodb@^4.4.1 || ^5.0.0", mongodb@5.9.2: version "5.9.2" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.9.2.tgz#39a73b9fbc87ac9d9c1aaf8aab5c5bb69e2b913e" + resolved "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz" integrity sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ== dependencies: bson "^5.5.0" @@ -10875,29 +11116,16 @@ mongodb@5.9.2: mongodb@^6.1.0: version "6.20.0" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.20.0.tgz#5212dcf512719385287aa4574265352eefb01d8e" + resolved "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz" integrity sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ== dependencies: "@mongodb-js/saslprep" "^1.3.0" bson "^6.10.4" mongodb-connection-string-url "^3.0.2" -"mongoose@5 - 8", mongoose@~7.5.1: - version "7.5.4" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.5.4.tgz#c83d0a3de8e1a13c19ce6cc5810bf93f0fb4c2a2" - integrity sha512-u97BOfBOoCxysnH5X0WeF/O39DO1di75dYU75xaSs9mL3Si0qmP0qLWvWpBRdVkiiRVw+eaqJyKwaq6RvKPVZw== - dependencies: - bson "^5.4.0" - kareem "2.5.1" - mongodb "5.8.1" - mpath "0.9.0" - mquery "5.0.0" - ms "2.1.3" - sift "16.0.1" - -mongoose@^7.8.4: +mongoose@^7.8.4, "mongoose@5 - 8": version "7.8.7" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.8.7.tgz#7a7ccbfd3da25990c88a8c41cd5b5abc30b913c7" + resolved "https://registry.npmjs.org/mongoose/-/mongoose-7.8.7.tgz" integrity sha512-5Bo4CrUxrPITrhMKsqUTOkXXo2CoRC5tXxVQhnddCzqDMwRXfyStrxj1oY865g8gaekSBhxAeNkYyUSJvGm9Hw== dependencies: bson "^5.5.0" @@ -10910,147 +11138,152 @@ mongoose@^7.8.4: moo@^0.5.1: version "0.5.2" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + resolved "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz" integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== -morgan@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== +morgan@^1.10.0: + version "1.10.1" + resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz" + integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== dependencies: basic-auth "~2.0.1" debug "2.6.9" depd "~2.0.0" on-finished "~2.3.0" - on-headers "~1.0.2" + on-headers "~1.1.0" -morgan@^1.10.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.1.tgz#4e02e6a4465a48e26af540191593955d17f61570" - integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== +morgan@1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== dependencies: basic-auth "~2.0.1" debug "2.6.9" depd "~2.0.0" on-finished "~2.3.0" - on-headers "~1.1.0" + on-headers "~1.0.2" mpath@0.9.0: version "0.9.0" - resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" + resolved "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz" integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew== mquery@5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d" + resolved "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz" integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg== dependencies: debug "4.x" +ms@^2.1.1, ms@^2.1.3, ms@2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - mustache@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + resolved "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== nanoclone@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz" integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== nanoid@^3.3.11: version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + negotiator@0.6.3: version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== neo-async@^2.6.0, neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== next-tick@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== node-abort-controller@^3.0.1, node-abort-controller@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== node-addon-api@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz" integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== node-addon-api@^7.0.0: version "7.1.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== node-cache@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + resolved "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz" integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== dependencies: clone "2.x" node-domexception@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== node-fetch-h2@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz#c6188325f9bd3d834020bf0f2d6dc17ced2241ac" + resolved "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz" integrity sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg== dependencies: http2-client "^1.2.5" node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" node-forge@^1.2.1, node-forge@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-gyp-build@^4.2.0: version "4.8.4" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" + resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz" integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== node-http2@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/node-http2/-/node-http2-4.0.1.tgz#164ff53b5dd22c84f0af142b877c5eaeb6809959" + resolved "https://registry.npmjs.org/node-http2/-/node-http2-4.0.1.tgz" integrity sha512-AP21BjQsOAMTCJCCkdXUUMa1o7/Qx+yAWHnHZbCf8RhZ+hKMjB9rUkAtnfayk/yGj1qapZ5eBHZJBpk1dqdNlw== dependencies: assert "1.4.1" @@ -11064,12 +11297,12 @@ node-http2@^4.0.1: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-jose@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.2.0.tgz#b64f3225ad6bec328509a420800de597ba2bf3ed" + resolved "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz" integrity sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw== dependencies: base64url "^3.0.1" @@ -11084,33 +11317,40 @@ node-jose@^2.2.0: node-readfiles@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/node-readfiles/-/node-readfiles-0.2.0.tgz#dbbd4af12134e2e635c245ef93ffcf6f60673a5d" + resolved "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz" integrity sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA== dependencies: es6-promise "^3.2.1" node-releases@^2.0.26: version "2.0.26" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.26.tgz#fdfa272f2718a1309489d18aef4ef5ba7f5dfb52" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz" integrity sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA== node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: version "4.3.0" - resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.3.0.tgz#8336b56cfed23ac5180fe98f1e3bb6b11fd5317c" + resolved "https://registry.npmjs.org/node-source-walk/-/node-source-walk-4.3.0.tgz" integrity sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA== dependencies: "@babel/parser" "^7.0.0" -node-source-walk@^5.0.0, node-source-walk@^5.0.1: +node-source-walk@^5.0.0: version "5.0.2" - resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-5.0.2.tgz#0eb439ce378946ce531e07a6a0073d06288396dd" + resolved "https://registry.npmjs.org/node-source-walk/-/node-source-walk-5.0.2.tgz" + integrity sha512-Y4jr/8SRS5hzEdZ7SGuvZGwfORvNsSsNRwDXx5WisiqzsVfeftDvRgfeqWNgZvWSJbgubTRVRYBzK6UO+ErqjA== + dependencies: + "@babel/parser" "^7.21.4" + +node-source-walk@^5.0.1: + version "5.0.2" + resolved "https://registry.npmjs.org/node-source-walk/-/node-source-walk-5.0.2.tgz" integrity sha512-Y4jr/8SRS5hzEdZ7SGuvZGwfORvNsSsNRwDXx5WisiqzsVfeftDvRgfeqWNgZvWSJbgubTRVRYBzK6UO+ErqjA== dependencies: "@babel/parser" "^7.21.4" nodemon@^3.0.1: version "3.1.10" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.10.tgz#5015c5eb4fffcb24d98cf9454df14f4fecec9bc1" + resolved "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz" integrity sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw== dependencies: chokidar "^3.5.2" @@ -11126,33 +11366,33 @@ nodemon@^3.0.1: nofilter@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + resolved "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz" integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== nopt@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" nopt@^7.2.0: version "7.2.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + resolved "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz" integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== dependencies: abbrev "^2.0.0" nopt@~3.0.6: version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg== dependencies: abbrev "1" nopt@~4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" @@ -11160,19 +11400,19 @@ nopt@~4.0.1: normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== nostr-tools@^2.17.0: version "2.17.0" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.17.0.tgz#ae357479b957ff897ae404761d5cd88f8064d713" + resolved "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.17.0.tgz" integrity sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w== dependencies: "@noble/ciphers" "^0.5.1" @@ -11185,19 +11425,19 @@ nostr-tools@^2.17.0: nostr-wasm@0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94" + resolved "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz" integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" npmlog@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz" integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== dependencies: are-we-there-yet "^2.0.0" @@ -11207,21 +11447,21 @@ npmlog@^5.0.1: nth-check@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" oas-kit-common@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/oas-kit-common/-/oas-kit-common-1.0.8.tgz#6d8cacf6e9097967a4c7ea8bcbcbd77018e1f535" + resolved "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz" integrity sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ== dependencies: fast-safe-stringify "^2.0.7" oas-linter@^3.2.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/oas-linter/-/oas-linter-3.2.2.tgz#ab6a33736313490659035ca6802dc4b35d48aa1e" + resolved "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz" integrity sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ== dependencies: "@exodus/schemasafe" "^1.0.0-rc.2" @@ -11230,7 +11470,7 @@ oas-linter@^3.2.2: oas-normalize@^8.3.0: version "8.4.1" - resolved "https://registry.yarnpkg.com/oas-normalize/-/oas-normalize-8.4.1.tgz#b647addbf4cfc1a28db6405649f6e9ec414f94ae" + resolved "https://registry.npmjs.org/oas-normalize/-/oas-normalize-8.4.1.tgz" integrity sha512-cGODg+AntZteJRHBiYDWKtcO2svWGMXuFWYu2I8b4hOrNiwB3hgDs/ScX3O9mYm6RpLsUIftt6rDHGc8eYG8aA== dependencies: "@readme/openapi-parser" "^2.5.0" @@ -11242,7 +11482,7 @@ oas-normalize@^8.3.0: oas-resolver@^2.5.6: version "2.5.6" - resolved "https://registry.yarnpkg.com/oas-resolver/-/oas-resolver-2.5.6.tgz#10430569cb7daca56115c915e611ebc5515c561b" + resolved "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz" integrity sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ== dependencies: node-fetch-h2 "^2.3.0" @@ -11253,12 +11493,12 @@ oas-resolver@^2.5.6: oas-schema-walker@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz#74c3cd47b70ff8e0b19adada14455b5d3ac38a22" + resolved "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz" integrity sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ== oas-validator@^5.0.8: version "5.0.8" - resolved "https://registry.yarnpkg.com/oas-validator/-/oas-validator-5.0.8.tgz#387e90df7cafa2d3ffc83b5fb976052b87e73c28" + resolved "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz" integrity sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw== dependencies: call-me-maybe "^1.0.1" @@ -11270,9 +11510,9 @@ oas-validator@^5.0.8: should "^13.2.1" yaml "^1.10.0" -oas@^20.11.0, oas@^20.5.0: +oas@^20.0.0, oas@^20.11.0, oas@^20.5.0: version "20.11.0" - resolved "https://registry.yarnpkg.com/oas/-/oas-20.11.0.tgz#dd7903caf721df05430805790be89e236e04622f" + resolved "https://registry.npmjs.org/oas/-/oas-20.11.0.tgz" integrity sha512-Eio2qil8z86PD8KJTdyGPN7hbcF3dV0gc5B6mkSQgL3MLMVFE/XyVAmq/9BRh51zkIXOPmZpSIF3nStADbnkHA== dependencies: "@readme/json-schema-ref-parser" "^1.2.0" @@ -11287,27 +11527,27 @@ oas@^20.11.0, oas@^20.5.0: object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-hash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.7: version "4.1.7" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz" integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== dependencies: call-bind "^1.0.8" @@ -11319,7 +11559,7 @@ object.assign@^4.1.7: object.defaults@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + resolved "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz" integrity sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA== dependencies: array-each "^1.0.1" @@ -11329,7 +11569,7 @@ object.defaults@^1.1.0: object.fromentries@^2.0.8: version "2.0.8" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== dependencies: call-bind "^1.0.7" @@ -11339,7 +11579,7 @@ object.fromentries@^2.0.8: object.groupby@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz" integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== dependencies: call-bind "^1.0.7" @@ -11348,7 +11588,7 @@ object.groupby@^1.0.3: object.map@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + resolved "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz" integrity sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w== dependencies: for-own "^1.0.0" @@ -11356,14 +11596,14 @@ object.map@^1.0.1: object.pick@^1.2.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== dependencies: isobject "^3.0.1" object.values@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz" integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== dependencies: call-bind "^1.0.8" @@ -11373,62 +11613,62 @@ object.values@^1.2.1: on-exit-leak-free@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== -on-finished@2.4.1: +on-finished@^2.4.1, on-finished@2.4.1: version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== on-headers@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz" integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -openapi-types@^12.1.0: +openapi-types@^12.1.0, openapi-types@>=7: version "12.1.3" - resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" + resolved "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== opn@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-6.0.0.tgz#3c5b0db676d5f97da1233d1ed42d182bc5a27d2d" + resolved "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz" integrity sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ== dependencies: is-wsl "^1.1.0" optimism@^0.17.5: version "0.17.5" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.17.5.tgz#a4c78b3ad12c58623abedbebb4f2f2c19b8e8816" + resolved "https://registry.npmjs.org/optimism/-/optimism-0.17.5.tgz" integrity sha512-TEcp8ZwK1RczmvMnvktxHSF2tKgMWjJ71xEFGX5ApLh67VsMSTy1ZUlipJw8W+KaqgOmQ+4pqwkeivY89j+4Vw== dependencies: "@wry/context" "^0.7.0" @@ -11437,7 +11677,7 @@ optimism@^0.17.5: optimism@^0.18.0: version "0.18.1" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" + resolved "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz" integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== dependencies: "@wry/caches" "^1.0.0" @@ -11447,7 +11687,7 @@ optimism@^0.18.0: optionator@^0.8.1: version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -11459,7 +11699,7 @@ optionator@^0.8.1: optionator@^0.9.3: version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: deep-is "^0.1.3" @@ -11471,7 +11711,7 @@ optionator@^0.9.3: ora@^5.4.1: version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: bl "^4.1.0" @@ -11486,17 +11726,17 @@ ora@^5.4.1: os-homedir@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== os-tmpdir@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== osenv@^0.1.4: version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" @@ -11504,7 +11744,7 @@ osenv@^0.1.4: otplib@^12.0.1: version "12.0.1" - resolved "https://registry.yarnpkg.com/otplib/-/otplib-12.0.1.tgz#c1d3060ab7aadf041ed2960302f27095777d1f73" + resolved "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz" integrity sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg== dependencies: "@otplib/core" "^12.0.1" @@ -11513,7 +11753,7 @@ otplib@^12.0.1: own-keys@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + resolved "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz" integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== dependencies: get-intrinsic "^1.2.6" @@ -11522,67 +11762,67 @@ own-keys@^1.0.1: p-each-series@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz" integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.1, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json-from-dist@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== pako@^2.0.4: version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-cache-control@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + resolved "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz" integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== parse-filepath@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== dependencies: is-absolute "^1.0.0" @@ -11591,7 +11831,7 @@ parse-filepath@^1.0.1: parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -11601,17 +11841,17 @@ parse-json@^5.2.0: parse-ms@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== parse-passwd@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== parse5-htmlparser2-tree-adapter@^7.1.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b" + resolved "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz" integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g== dependencies: domhandler "^5.0.3" @@ -11619,63 +11859,63 @@ parse5-htmlparser2-tree-adapter@^7.1.0: parse5-parser-stream@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" + resolved "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz" integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== dependencies: parse5 "^7.0.0" parse5@^7.0.0, parse5@^7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + resolved "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz" integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== dependencies: entities "^6.0.0" parseurl@^1.3.3, parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-browserify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ== path-root@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg== dependencies: path-root-regex "^0.1.0" path-scurry@^1.11.1: version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: lru-cache "^10.2.0" @@ -11683,65 +11923,70 @@ path-scurry@^1.11.1: path-scurry@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz" integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== dependencies: lru-cache "^11.0.0" minipass "^7.1.2" +path-to-regexp@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== + +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-to-regexp@0.1.12: version "0.1.12" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== -path-to-regexp@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" - integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== - path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pg-cloudflare@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz#a1f3d226bab2c45ae75ea54d65ec05ac6cfafbef" + resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz" integrity sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg== -pg-connection-string@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.1.tgz#78c23c21a35dd116f48e12e23c0965e8d9e2cbfb" - integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg== - pg-connection-string@^2.9.1: version "2.9.1" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.9.1.tgz#bb1fd0011e2eb76ac17360dc8fa183b2d3465238" + resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz" integrity sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w== +pg-connection-string@2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz" + integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg== + pg-int8@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== pg-pool@^3.10.1: version "3.10.1" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.10.1.tgz#481047c720be2d624792100cac1816f8850d31b2" + resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz" integrity sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg== pg-protocol@^1.10.3: version "1.10.3" - resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.10.3.tgz#ac9e4778ad3f84d0c5670583bab976ea0a34f69f" + resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz" integrity sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ== pg-types@2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz" integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== dependencies: pg-int8 "1.0.1" @@ -11750,9 +11995,9 @@ pg-types@2.2.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@^8.11.3: +pg@^8.11.3, pg@>=8.0: version "8.16.3" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.16.3.tgz#160741d0b44fdf64680e45374b06d632e86c99fd" + resolved "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz" integrity sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw== dependencies: pg-connection-string "^2.9.1" @@ -11765,34 +12010,34 @@ pg@^8.11.3: pgpass@1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" + resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz" integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== dependencies: split2 "^4.1.0" picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== picomatch@^4.0.2: version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pify@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz" integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== dependencies: readable-stream "^4.0.0" @@ -11800,7 +12045,7 @@ pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0: pino-http@^8.5.0: version "8.6.1" - resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-8.6.1.tgz#46338caea759c9c86fc44f3226ed6976364ec269" + resolved "https://registry.npmjs.org/pino-http/-/pino-http-8.6.1.tgz" integrity sha512-J0hiJgUExtBXP2BjrK4VB305tHXS31sCmWJ9XJo2wPkLHa1NFPuW4V9wjG27PAc2fmBCigiNhQKpvrx+kntBPA== dependencies: get-caller-file "^2.0.5" @@ -11810,7 +12055,7 @@ pino-http@^8.5.0: pino-pretty@^10.2.0: version "10.3.1" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-10.3.1.tgz#e3285a5265211ac6c7cd5988f9e65bf3371a0ca9" + resolved "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz" integrity sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g== dependencies: colorette "^2.0.7" @@ -11830,12 +12075,12 @@ pino-pretty@^10.2.0: pino-std-serializers@^6.0.0, pino-std-serializers@^6.2.2: version "6.2.2" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz" integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== -pino@8.21.0, pino@^8.17.1: +pino@^8.17.1, pino@8.21.0: version "8.21.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-8.21.0.tgz#e1207f3675a2722940d62da79a7a55a98409f00d" + resolved "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz" integrity sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q== dependencies: atomic-sleep "^1.0.0" @@ -11852,24 +12097,29 @@ pino@8.21.0, pino@^8.17.1: pirates@^4.0.4, pirates@^4.0.7: version "4.0.7" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" pluralize@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== portscanner@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.2.0.tgz#6059189b3efa0965c9d96a56b958eb9508411cf1" + resolved "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz" integrity sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw== dependencies: async "^2.6.0" @@ -11877,12 +12127,12 @@ portscanner@^2.2.0: possible-typed-array-names@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== postcss-values-parser@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + resolved "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz" integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== dependencies: flatten "^1.0.2" @@ -11891,16 +12141,16 @@ postcss-values-parser@^2.0.1: postcss-values-parser@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz#636edc5b86c953896f1bb0d7a7a6615df00fb76f" + resolved "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz" integrity sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw== dependencies: color-name "^1.1.4" is-url-superb "^4.0.0" quote-unquote "^1.0.0" -postcss@^8.1.7, postcss@^8.4.19, postcss@^8.4.23: +postcss@^8.1.7, postcss@^8.2.9, postcss@^8.4.19, postcss@^8.4.23: version "8.5.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz" integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== dependencies: nanoid "^3.3.11" @@ -11909,29 +12159,29 @@ postcss@^8.1.7, postcss@^8.4.19, postcss@^8.4.23: postgres-array@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz" integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== postgres-bytea@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz" integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== postgres-date@~1.0.4: version "1.0.7" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz" integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== postgres-interval@^1.1.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz" integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== dependencies: xtend "^4.0.0" precinct@^8.1.0: version "8.3.1" - resolved "https://registry.yarnpkg.com/precinct/-/precinct-8.3.1.tgz#94b99b623df144eed1ce40e0801c86078466f0dc" + resolved "https://registry.npmjs.org/precinct/-/precinct-8.3.1.tgz" integrity sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q== dependencies: commander "^2.20.3" @@ -11950,7 +12200,7 @@ precinct@^8.1.0: precinct@^9.0.0: version "9.2.1" - resolved "https://registry.yarnpkg.com/precinct/-/precinct-9.2.1.tgz#db0a67abff7b0a9a3b2b1ac33d170e8a5fcac7b2" + resolved "https://registry.npmjs.org/precinct/-/precinct-9.2.1.tgz" integrity sha512-uzKHaTyiVejWW7VJtHInb9KBUq9yl9ojxXGujhjhDmPon2wgZPBKQIKR+6csGqSlUeGXAA4MEFnU6DesxZib+A== dependencies: "@dependents/detective-less" "^3.0.1" @@ -11968,34 +12218,34 @@ precinct@^9.0.0: prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" -prettier@^3.0.3: +prettier@^3.0.3, prettier@>=3.0.0: version "3.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== pretty-bytes@^5.3.0: version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: "@jest/schemas" "^29.6.3" @@ -12004,43 +12254,43 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: pretty-ms@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz" integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== dependencies: parse-ms "^2.1.0" process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process-warning@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-3.0.0.tgz#96e5b88884187a1dce6f5c3166d611132058710b" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz" integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ== process@^0.11.10: version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== prom-client@^14.2.0: version "14.2.0" - resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-14.2.0.tgz#ca94504e64156f6506574c25fb1c34df7812cf11" + resolved "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz" integrity sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA== dependencies: tdigest "^0.1.1" promise@^8.0.0: version "8.3.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + resolved "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz" integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== dependencies: asap "~2.0.6" prompts@^2.0.1, prompts@^2.4.2: version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" @@ -12048,7 +12298,7 @@ prompts@^2.0.1, prompts@^2.4.2: prop-types@^15.7.2: version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -12057,24 +12307,24 @@ prop-types@^15.7.2: property-expr@^2.0.4: version "2.0.6" - resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8" + resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz" integrity sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA== proto-list@~1.2.1: version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== proto3-json-serializer@^1.0.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz#1b5703152b6ce811c5cdcc6468032caf53521331" + resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz" integrity sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw== dependencies: protobufjs "^7.0.0" protobufjs-cli@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz#f531201b1c8c7772066aa822bf9a08318b24a704" + resolved "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz" integrity sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA== dependencies: chalk "^4.0.0" @@ -12088,10 +12338,28 @@ protobufjs-cli@1.1.1: tmp "^0.2.1" uglify-js "^3.7.7" -protobufjs@7.2.4, protobufjs@7.2.5, protobufjs@^7.0.0, protobufjs@^7.2.4, protobufjs@^7.2.5, protobufjs@^7.5.3: - version "7.2.5" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" - integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== +protobufjs@^7.0.0, protobufjs@^7.2.4, protobufjs@^7.2.5, protobufjs@^7.5.3: + version "7.5.4" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz" + integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + +protobufjs@7.2.4: + version "7.2.4" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -12108,14 +12376,14 @@ protobufjs@7.2.4, protobufjs@7.2.5, protobufjs@^7.0.0, protobufjs@^7.2.4, protob protoc-gen-js@^3.21.2: version "3.21.4" - resolved "https://registry.yarnpkg.com/protoc-gen-js/-/protoc-gen-js-3.21.4.tgz#d48eeb87a998b2c6181cd2aff6f50b2d7eee559e" + resolved "https://registry.npmjs.org/protoc-gen-js/-/protoc-gen-js-3.21.4.tgz" integrity sha512-GGUkE4fwesiwq8cWkJ+pU6DqJiokXS8nkDGZe7SscxKBSALUnXxNaBwfXODkbi4Cji8E/s+71zNKJoEEza/M7Q== dependencies: adm-zip "0.5.10" -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -12123,12 +12391,12 @@ proxy-addr@~2.0.7: proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== psbt@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/psbt/-/psbt-3.0.0.tgz#fcdaad203eec644189bccd226e25ffc22dbb5388" + resolved "https://registry.npmjs.org/psbt/-/psbt-3.0.0.tgz" integrity sha512-Gg35WfXAdbVqION9AJ4cO7cdgqR2iL1Dyq00AmvKCosTbm7EQrrMmHGjkWACb7ue9+aaHjMi/UdDG1SZwqA6bg== dependencies: bip66 "1.1.5" @@ -12140,12 +12408,12 @@ psbt@3.0.0: pstree.remy@^1.1.8: version "1.1.8" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + resolved "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== pump@^3.0.0: version "3.0.3" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz" integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== dependencies: end-of-stream "^1.1.0" @@ -12153,99 +12421,117 @@ pump@^3.0.0: punycode.js@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + resolved "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz" integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== punycode@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== pushdata-bitcoin@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz#15931d3cd967ade52206f523aa7331aef7d43af7" + resolved "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz" integrity sha512-hw7rcYTJRAl4olM8Owe8x0fBuJJ+WGbMhQuLWOXEMN3PxPCKQHRkhfL+XG0+iXUmSHjkMmb3Ba55Mt21cZc9kQ== dependencies: - bitcoin-ops "^1.3.0" + bitcoin-ops "^1.3.0" + +qs@^6.10.5, qs@^6.12.3, qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@^6.9.4: + version "6.14.1" + resolved "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz" + integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== + dependencies: + side-channel "^1.1.0" qs@6.11.0: version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" qs@6.13.0: version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" -qs@^6.10.5, qs@^6.12.3, qs@^6.4.0, qs@^6.9.4: - version "6.14.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" - integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== - dependencies: - side-channel "^1.1.0" - querystringify@^2.1.1: version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== queue@6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + resolved "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== dependencies: inherits "~2.0.3" quick-format-unescaped@^4.0.3: version "4.0.4" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== quote-unquote@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" + resolved "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz" integrity sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" -range-parser@~1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== rate-limiter-flexible@^3.0.0: version "3.0.6" - resolved "https://registry.yarnpkg.com/rate-limiter-flexible/-/rate-limiter-flexible-3.0.6.tgz#e7436428577bd5881f7c1549ce5f95923bbed908" + resolved "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-3.0.6.tgz" integrity sha512-tlvbee6lyse/XTWmsuBDS4MT8N65FyM151bPmQlFyfhv9+RIHs7d3rSTXoz0j35H910dM01mH0yTIeWYo8+aAw== +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + +raw-body@~1.1.0: + version "1.1.7" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz" + integrity sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg== + dependencies: + bytes "1" + string_decoder "0.10" + raw-body@2.5.1: version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: bytes "3.1.2" @@ -12255,7 +12541,7 @@ raw-body@2.5.1: raw-body@2.5.2: version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: bytes "3.1.2" @@ -12263,17 +12549,9 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" - integrity sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg== - dependencies: - bytes "1" - string_decoder "0.10" - rc@^1.2.7: version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" @@ -12283,24 +12561,24 @@ rc@^1.2.7: react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^18.0.0: version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react@^18.2.0: +react@*, "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", react@^18.2.0: version "18.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== dependencies: loose-envify "^1.1.0" readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.8: version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" @@ -12311,9 +12589,27 @@ readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.2.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.1.1: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^3.6.0: version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" @@ -12322,7 +12618,7 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: readable-stream@^4.0.0: version "4.7.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz" integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== dependencies: abort-controller "^3.0.0" @@ -12333,55 +12629,55 @@ readable-stream@^4.0.0: readdirp@^4.0.1: version "4.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz" integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" real-require@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + resolved "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz" integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== rechoir@^0.7.0: version "0.7.1" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz" integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== dependencies: resolve "^1.9.0" rechoir@^0.8.0: version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: resolve "^1.20.0" redis-commands@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" + resolved "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz" integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== redis-errors@^1.0.0, redis-errors@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + resolved "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== redis-parser@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + resolved "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== dependencies: redis-errors "^1.0.0" redis@3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/redis/-/redis-3.1.2.tgz#766851117e80653d23e0ed536254677ab647638c" + resolved "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz" integrity sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw== dependencies: denque "^1.5.0" @@ -12390,15 +12686,13 @@ redis@3.1.2: redis-parser "^3.0.0" redlock@^5.0.0-beta.2: - version "5.0.0-beta.2" - resolved "https://registry.yarnpkg.com/redlock/-/redlock-5.0.0-beta.2.tgz#a629c07e07d001c0fdd9f2efa614144c4416fe44" - integrity sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw== + version "v5.0.0-beta.2" dependencies: node-abort-controller "^3.0.1" reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz" integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== dependencies: call-bind "^1.0.8" @@ -12412,24 +12706,24 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: reftools@^1.1.9: version "1.1.9" - resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.1.9.tgz#e16e19f662ccd4648605312c06d34e5da3a2b77e" + resolved "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz" integrity sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w== regenerate-unicode-properties@^10.2.2: version "10.2.2" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz#aa113812ba899b630658c7623466be71e1f86f66" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz" integrity sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regexp.prototype.flags@^1.5.4: version "1.5.4" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz" integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== dependencies: call-bind "^1.0.8" @@ -12441,7 +12735,7 @@ regexp.prototype.flags@^1.5.4: regexpu-core@^6.3.1: version "6.4.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.4.0.tgz#3580ce0c4faedef599eccb146612436b62a176e5" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz" integrity sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA== dependencies: regenerate "^1.4.2" @@ -12453,49 +12747,49 @@ regexpu-core@^6.3.1: regjsgen@^0.8.0: version "0.8.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== regjsparser@^0.13.0: version "0.13.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.13.0.tgz#01f8351335cf7898d43686bc74d2dd71c847ecc0" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz" integrity sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q== dependencies: jsesc "~3.1.0" rehackt@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" + resolved "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz" integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== remove-undefined-objects@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/remove-undefined-objects/-/remove-undefined-objects-2.0.2.tgz#d323b075b12439077454ecf056a72dc2c94c9945" + resolved "https://registry.npmjs.org/remove-undefined-objects/-/remove-undefined-objects-2.0.2.tgz" integrity sha512-b6x4MUtR4YBW1aCoGx3tE4mA2PFjiXSmtSdNmLexQzUdZa4ybnJAItXLKpkcVgCUJIzJtk2DFG402sMSEMlonQ== request-ip@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-3.3.0.tgz#863451e8fec03847d44f223e30a5d63e369fa611" + resolved "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz" integrity sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA== require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-in-the-middle@^7.1.1: version "7.5.2" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz#dc25b148affad42e570cf0e41ba30dc00f1703ec" + resolved "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz" integrity sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ== dependencies: debug "^4.3.5" @@ -12504,7 +12798,7 @@ require-in-the-middle@^7.1.1: requirejs-config-file@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" + resolved "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz" integrity sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw== dependencies: esprima "^4.0.0" @@ -12512,36 +12806,36 @@ requirejs-config-file@^4.0.0: requirejs@^2.3.5: version "2.3.7" - resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.7.tgz#0b22032e51a967900e0ae9f32762c23a87036bd0" + resolved "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz" integrity sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw== requires-port@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== requizzle@^0.2.3: version "0.2.4" - resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.4.tgz#319eb658b28c370f0c20f968fa8ceab98c13d27c" + resolved "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz" integrity sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw== dependencies: lodash "^4.17.21" resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-dependency-path@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" + resolved "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz" integrity sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w== resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + resolved "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz" integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg== dependencies: expand-tilde "^2.0.0" @@ -12549,22 +12843,22 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve.exports@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz" integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== resolve@^1.0.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.10, resolve@^1.22.4, resolve@^1.22.8, resolve@^1.9.0: version "1.22.11" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz" integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: is-core-module "^2.16.1" @@ -12573,12 +12867,12 @@ resolve@^1.0.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22 response-iterator@^0.2.6: version "0.2.25" - resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.25.tgz#5e8dae715ed9ca68b37919f4eae0a90d3ab1ae64" + resolved "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.25.tgz" integrity sha512-15K4tT8X35W0zJ5bv3fAf4eEKqOwS7yzd+Bg6YEE9NLltVbPbuTcYo3J2AP6AMQGMJmJkFCG421+kP2/iCBfDA== restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -12586,7 +12880,7 @@ restore-cursor@^3.1.0: retry-request@^5.0.0: version "5.0.2" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-5.0.2.tgz#143d85f90c755af407fcc46b7166a4ba520e44da" + resolved "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz" integrity sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ== dependencies: debug "^4.1.1" @@ -12594,7 +12888,7 @@ retry-request@^5.0.0: retry-request@^7.0.0: version "7.0.2" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-7.0.2.tgz#60bf48cfb424ec01b03fca6665dee91d06dd95f3" + resolved "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz" integrity sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w== dependencies: "@types/request" "^2.48.8" @@ -12603,46 +12897,64 @@ retry-request@^7.0.0: retry@0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== reusify@^1.0.4: version "1.1.0" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== -rimraf@^2.6.1, rimraf@^2.6.2: +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^2.6.2: version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" ripemd160@^2.0.1: version "2.0.3" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.3.tgz#9be54e4ba5e3559c8eee06a25cd7648bbccdf5a8" + resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz" integrity sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA== dependencies: hash-base "^3.1.2" inherits "^2.0.4" +router@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/router/-/router-2.2.0.tgz" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" safe-array-concat@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz" integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== dependencies: call-bind "^1.0.8" @@ -12651,29 +12963,34 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-identifier@^0.4.1: version "0.4.2" - resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" + resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== safe-json-parse@~1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" + resolved "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz" integrity sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A== safe-push-apply@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz" integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== dependencies: es-errors "^1.3.0" @@ -12681,7 +12998,7 @@ safe-push-apply@^1.0.0: safe-regex-test@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz" integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== dependencies: call-bound "^1.0.2" @@ -12690,24 +13007,24 @@ safe-regex-test@^1.1.0: safe-stable-stringify@^2.3.1: version "2.5.0" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sass-lookup@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac" + resolved "https://registry.npmjs.org/sass-lookup/-/sass-lookup-3.0.0.tgz" integrity sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg== dependencies: commander "^2.16.0" sass@^1.32.13: version "1.93.2" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.93.2.tgz#e97d225d60f59a3b3dbb6d2ae3c1b955fd1f2cd1" + resolved "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz" integrity sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg== dependencies: chokidar "^4.0.0" @@ -12718,12 +13035,12 @@ sass@^1.32.13: scmp@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/scmp/-/scmp-2.1.0.tgz#37b8e197c425bdeb570ab91cc356b311a11f9c9a" + resolved "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz" integrity sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q== secp256k1@^4.0.2: version "4.0.4" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.4.tgz#58f0bfe1830fe777d9ca1ffc7574962a8189f8ab" + resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz" integrity sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw== dependencies: elliptic "^6.5.7" @@ -12732,22 +13049,49 @@ secp256k1@^4.0.2: secure-json-parse@^2.4.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: +semver@^6.0.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.3.1: version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.1.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.npmjs.org/send/-/send-1.2.1.tgz" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + send@0.18.0: version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" @@ -12766,7 +13110,7 @@ send@0.18.0: send@0.19.0: version "0.19.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" @@ -12785,7 +13129,7 @@ send@0.19.0: serve-index@^1.9.1: version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== dependencies: accepts "~1.3.4" @@ -12796,9 +13140,29 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" +serve-static@^1.14.1, serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + serve-static@1.15.0: version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" @@ -12806,29 +13170,19 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" -serve-static@1.16.2, serve-static@^1.14.1: - version "1.16.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" - set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== set-cookie-parser@^2.6.0: version "2.7.1" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz" integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== set-function-length@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -12840,7 +13194,7 @@ set-function-length@^1.2.2: set-function-name@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== dependencies: define-data-property "^1.1.4" @@ -12850,7 +13204,7 @@ set-function-name@^2.0.2: set-proto@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + resolved "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz" integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== dependencies: dunder-proto "^1.0.1" @@ -12859,22 +13213,22 @@ set-proto@^1.0.0: setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== +setprototypeof@~1.2.0, setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + setprototypeof@1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - sha.js@^2.4.0, sha.js@^2.4.11: version "2.4.12" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.12.tgz#eb8b568bf383dfd1867a32c3f2b74eb52bdbf23f" + resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz" integrity sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w== dependencies: inherits "^2.0.4" @@ -12883,31 +13237,31 @@ sha.js@^2.4.0, sha.js@^2.4.11: shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shimmer@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + resolved "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== should-equal@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" + resolved "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz" integrity sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA== dependencies: should-type "^1.4.0" should-format@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1" + resolved "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz" integrity sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q== dependencies: should-type "^1.3.0" @@ -12915,7 +13269,7 @@ should-format@^3.0.3: should-type-adaptors@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz#401e7f33b5533033944d5cd8bf2b65027792e27a" + resolved "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz" integrity sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA== dependencies: should-type "^1.3.0" @@ -12923,17 +13277,17 @@ should-type-adaptors@^1.0.1: should-type@^1.3.0, should-type@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3" + resolved "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz" integrity sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ== should-util@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.1.tgz#fb0d71338f532a3a149213639e2d32cbea8bcb28" + resolved "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz" integrity sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g== should@^13.2.1: version "13.2.3" - resolved "https://registry.yarnpkg.com/should/-/should-13.2.3.tgz#96d8e5acf3e97b49d89b51feaa5ae8d07ef58f10" + resolved "https://registry.npmjs.org/should/-/should-13.2.3.tgz" integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== dependencies: should-equal "^2.0.0" @@ -12944,7 +13298,7 @@ should@^13.2.1: side-channel-list@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz" integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: es-errors "^1.3.0" @@ -12952,7 +13306,7 @@ side-channel-list@^1.0.0: side-channel-map@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz" integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== dependencies: call-bound "^1.0.2" @@ -12962,7 +13316,7 @@ side-channel-map@^1.0.1: side-channel-weakmap@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz" integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== dependencies: call-bound "^1.0.2" @@ -12973,7 +13327,7 @@ side-channel-weakmap@^1.0.2: side-channel@^1.0.4, side-channel@^1.0.6, side-channel@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: es-errors "^1.3.0" @@ -12984,54 +13338,54 @@ side-channel@^1.0.4, side-channel@^1.0.6, side-channel@^1.1.0: sift@16.0.1: version "16.0.1" - resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.1.tgz#e9c2ccc72191585008cf3e36fc447b2d2633a053" + resolved "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz" integrity sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ== signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.0.1: version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== simple-update-notifier@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + resolved "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz" integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== dependencies: semver "^7.5.3" sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== sjcl@^1.0.6: version "1.0.8" - resolved "https://registry.yarnpkg.com/sjcl/-/sjcl-1.0.8.tgz#f2ec8d7dc1f0f21b069b8914a41a8f236b0e252a" + resolved "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz" integrity sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ== slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slash@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== smart-buffer@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socks@^2.7.1: version "2.8.7" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea" + resolved "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz" integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A== dependencies: ip-address "^10.0.1" @@ -13039,52 +13393,52 @@ socks@^2.7.1: sonic-boom@^3.0.0, sonic-boom@^3.7.0: version "3.8.1" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz" integrity sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg== dependencies: atomic-sleep "^1.0.0" -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1: +source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0": version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== +source-map-support@^0.5.12: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.12: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" source-map@^0.5.3: version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== sparse-bitfield@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" + resolved "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz" integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ== dependencies: memory-pager "^1.0.2" spectaql@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/spectaql/-/spectaql-2.3.1.tgz#6b92dc7759f35d51d9d94dbe61a5740b81977bf0" + resolved "https://registry.npmjs.org/spectaql/-/spectaql-2.3.1.tgz" integrity sha512-YQgp6yQAUjRkQ8ixLnjBkyhLcTfbtIErTrzsWB+rdS270hhKr0NeknEg0Yp7ElfMJWE101HERKoc4sA6anCiog== dependencies: "@anvilco/apollo-server-plugin-introspection-metadata" "^2.2.3" @@ -13125,58 +13479,68 @@ spectaql@^2.3.0: split2@^4.0.0, split2@^4.1.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== sprintf-js@^1.1.1: version "1.1.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssri@^10.0.1: version "10.0.6" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" + resolved "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz" integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== dependencies: minipass "^7.0.3" stack-utils@^2.0.3: version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" standard-as-callback@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + resolved "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz" integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== static-eval@2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" + resolved "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz" integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== dependencies: escodegen "^1.8.1" -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -"statuses@>= 1.4.0 < 2", statuses@~1.5.0: +statuses@~1.5.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + stop-iteration-iterator@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz" integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== dependencies: es-errors "^1.3.0" @@ -13184,7 +13548,7 @@ stop-iteration-iterator@^1.1.0: stream-browserify@2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz" integrity sha512-nmQnY9D9TlnfQIkYJCCWxvCcQODilFRZIw14gCMYQVXOiY4E1Ze1VMxB+6y3qdXHpTordULo2qWloHmNcNAQYw== dependencies: inherits "~2.0.1" @@ -13192,26 +13556,45 @@ stream-browserify@2.0.1: stream-events@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz" integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== dependencies: stubs "^3.0.0" stream-shift@^1.0.0, stream-shift@^1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" + resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz" integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== stream-to-array@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + resolved "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz" integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== dependencies: any-promise "^1.1.0" +string_decoder@^1.1.1, string_decoder@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +string_decoder@0.10: + version "0.10.31" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + string-length@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -13219,17 +13602,26 @@ string-length@^4.0.1: string-random@^0.1.3: version "0.1.3" - resolved "https://registry.yarnpkg.com/string-random/-/string-random-0.1.3.tgz#083f39835c2430fe0be76b9e72f0736e2e0f7b74" + resolved "https://registry.npmjs.org/string-random/-/string-random-0.1.3.tgz" integrity sha512-g+UsIwzKhNi+9/+Q3Q7hP8R4HkQxiIkQlttnxw6GRdk9pnnkGIv53C6H8dvh8wxAVDhkqpnWeauaPXS1b2sBJg== string-template@~0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" + resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -13238,7 +13630,7 @@ string-template@~0.2.1: string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" @@ -13247,7 +13639,7 @@ string-width@^5.0.1, string-width@^5.1.2: string.prototype.trim@^1.2.10: version "1.2.10" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz" integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== dependencies: call-bind "^1.0.8" @@ -13260,7 +13652,7 @@ string.prototype.trim@^1.2.10: string.prototype.trimend@^1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz" integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== dependencies: call-bind "^1.0.8" @@ -13270,105 +13662,98 @@ string.prototype.trimend@^1.0.9: string.prototype.trimstart@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== dependencies: call-bind "^1.0.7" define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@0.10: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - -string_decoder@^1.1.1, string_decoder@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - stringify-object@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== dependencies: get-own-enumerable-property-symbols "^3.0.0" is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.2" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: +strip-json-comments@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + strnum@^1.1.1: version "1.1.2" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.1.2.tgz#57bca4fbaa6f271081715dbc9ed7cee5493e28e4" + resolved "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz" integrity sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA== strnum@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a" + resolved "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz" integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ== stubs@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + resolved "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz" integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== stylus-lookup@^3.0.1: version "3.0.2" - resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" + resolved "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-3.0.2.tgz" integrity sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg== dependencies: commander "^2.8.1" @@ -13376,38 +13761,45 @@ stylus-lookup@^3.0.1: supports-color@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== supports-color@^5.5.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.1: +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.1.1: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== svix@^1.12.0: version "1.80.0" - resolved "https://registry.yarnpkg.com/svix/-/svix-1.80.0.tgz#13b406c6065f7b7d047b8423771e2fdb8d73c663" + resolved "https://registry.npmjs.org/svix/-/svix-1.80.0.tgz" integrity sha512-OdWaotwCJnPP1q381gacgwGm9HUAKgv0/EhMv+YdL0k3EI009i/3V8CvUka8a/MUDGgRQ0BsRPDzTy6ldxf13Q== dependencies: "@stablelib/base64" "^1.0.0" @@ -13416,7 +13808,7 @@ svix@^1.12.0: swagger2openapi@^7.0.8: version "7.0.8" - resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-7.0.8.tgz#12c88d5de776cb1cbba758994930f40ad0afac59" + resolved "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz" integrity sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g== dependencies: call-me-maybe "^1.0.1" @@ -13433,12 +13825,12 @@ swagger2openapi@^7.0.8: symbol-observable@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== sync-request@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" + resolved "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz" integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== dependencies: http-response-object "^3.0.1" @@ -13447,26 +13839,26 @@ sync-request@^6.1.0: sync-rpc@^1.2.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" + resolved "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz" integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== dependencies: get-port "^3.1.0" synckit@^0.11.7: version "0.11.11" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz" integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== dependencies: "@pkgr/core" "^0.2.9" tapable@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz" integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== tar@^6.1.11: version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== dependencies: chownr "^2.0.0" @@ -13478,19 +13870,19 @@ tar@^6.1.11: tarn@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" + resolved "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz" integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ== tdigest@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" + resolved "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz" integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA== dependencies: bintrees "1.0.2" teeny-request@^8.0.0: version "8.0.3" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-8.0.3.tgz#5cb9c471ef5e59f2fca8280dc3c5909595e6ca24" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz" integrity sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww== dependencies: http-proxy-agent "^5.0.0" @@ -13501,7 +13893,7 @@ teeny-request@^8.0.0: teeny-request@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-9.0.0.tgz#18140de2eb6595771b1b02203312dfad79a4716d" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz" integrity sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g== dependencies: http-proxy-agent "^5.0.0" @@ -13512,7 +13904,7 @@ teeny-request@^9.0.0: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -13521,17 +13913,17 @@ test-exclude@^6.0.0: text-decoding@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/text-decoding/-/text-decoding-1.0.0.tgz#38a5692d23b5c2b12942d6e245599cb58b1bc52f" + resolved "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz" integrity sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA== text-table@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== then-request@^6.0.0: version "6.0.2" - resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" + resolved "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz" integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== dependencies: "@types/concat-stream" "^1.6.0" @@ -13548,31 +13940,31 @@ then-request@^6.0.0: thirty-two@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a" + resolved "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz" integrity sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA== thread-stream@^2.6.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.7.0.tgz#d8a8e1b3fd538a6cca8ce69dbe5d3d097b601e11" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz" integrity sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw== dependencies: real-require "^0.2.0" tildify@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" + resolved "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz" integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== timers-browserify@2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz" integrity sha512-O7UB405+hxP2OWqlBdlUMxZVEdsi8NOWL2c730Cs6zeO1l1AkxygvTm6yC4nTw84iGbFcqxbIkkrdNKzq/3Fvg== dependencies: setimmediate "^1.0.4" timers-ext@^0.1.7: version "0.1.8" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.8.tgz#b4e442f10b7624a29dd2aa42c295e257150cf16c" + resolved "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz" integrity sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww== dependencies: es5-ext "^0.10.64" @@ -13580,7 +13972,7 @@ timers-ext@^0.1.7: tiny-lr@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" + resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz" integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== dependencies: body "^5.1.0" @@ -13590,47 +13982,47 @@ tiny-lr@^1.1.1: object-assign "^4.1.0" qs "^6.4.0" +tiny-secp256k1@^2.2.3: + version "2.2.4" + resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.4.tgz" + integrity sha512-FoDTcToPqZE454Q04hH9o2EhxWsm7pOSpicyHkgTwKhdKWdsTUuqfP5MLq3g+VjAtl2vSx6JpXGdwA2qpYkI0Q== + dependencies: + uint8array-tools "0.0.7" + tiny-secp256k1@2.2.2: version "2.2.2" - resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-2.2.2.tgz#3600e24844cb6390876ef11174116c0386d129bf" + resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.2.tgz" integrity sha512-KP3eqslmiUH9jxhyQuLY+GqI4wt1EiHWNHHqKVUxCZV41+MT+esucaK4mb6Ji0vKWVKBffJ6tlxU83Pq5TIUwg== dependencies: uint8array-tools "0.0.7" tiny-secp256k1@2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-2.2.3.tgz#fe1dde11a64fcee2091157d4b78bcb300feb9b65" + resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.3.tgz" integrity sha512-SGcL07SxcPN2nGKHTCvRMkQLYPSoeFcvArUSCYtjVARiFAWU44cCIqYS0mYAU6nY7XfvwURuTIGo2Omt3ZQr0Q== dependencies: uint8array-tools "0.0.7" -tiny-secp256k1@^2.2.3: - version "2.2.4" - resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-2.2.4.tgz#1d9e45c2facb8607847da71a0a3d9cb2fd027eb2" - integrity sha512-FoDTcToPqZE454Q04hH9o2EhxWsm7pOSpicyHkgTwKhdKWdsTUuqfP5MLq3g+VjAtl2vSx6JpXGdwA2qpYkI0Q== - dependencies: - uint8array-tools "0.0.7" +tmp@^0.2.1: + version "0.2.5" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== tmp@0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== dependencies: rimraf "^3.0.0" -tmp@^0.2.1: - version "0.2.5" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" - integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== - tmpl@1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-buffer@^1.2.0, to-buffer@^1.2.1, to-buffer@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.2.2.tgz#ffe59ef7522ada0a2d1cb5dfe03bb8abc3cdc133" + resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz" integrity sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw== dependencies: isarray "^2.0.5" @@ -13639,75 +14031,75 @@ to-buffer@^1.2.0, to-buffer@^1.2.1, to-buffer@^1.2.2: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" -toidentifier@1.0.1: +toidentifier@~1.0.1, toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== toposort@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz" integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== touch@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + resolved "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz" integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== tr46@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + resolved "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz" integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== dependencies: punycode "^2.1.1" tr46@^5.1.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca" + resolved "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz" integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw== dependencies: punycode "^2.3.1" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== tree-kill@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== ts-algebra@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/ts-algebra/-/ts-algebra-1.2.2.tgz#b75d301c28cd4126cd344760a47b43e48e2872e0" + resolved "https://registry.npmjs.org/ts-algebra/-/ts-algebra-1.2.2.tgz" integrity sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA== ts-api-utils@^1.0.1: version "1.4.3" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz" integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== ts-graphviz@^1.5.0: version "1.8.2" - resolved "https://registry.yarnpkg.com/ts-graphviz/-/ts-graphviz-1.8.2.tgz#6c4768d05f8a36e37abe34855ffe89a4c4bd96cc" + resolved "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-1.8.2.tgz" integrity sha512-5YhbFoHmjxa7pgQLkB07MtGnGJ/yhvjmc9uhsnDBEICME6gkPf83SBwLDQqGDoCa3XzUMWLk1AU2Wn1u1naDtA== ts-invariant@^0.10.3: version "0.10.3" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" + resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz" integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== dependencies: tslib "^2.1.0" ts-morph@^17.0.1: version "17.0.1" - resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-17.0.1.tgz#d85df4fcf9a1fcda1b331d52c00655f381c932d1" + resolved "https://registry.npmjs.org/ts-morph/-/ts-morph-17.0.1.tgz" integrity sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g== dependencies: "@ts-morph/common" "~0.18.0" @@ -13715,7 +14107,7 @@ ts-morph@^17.0.1: ts-node-dev@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065" + resolved "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz" integrity sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w== dependencies: chokidar "^3.5.1" @@ -13729,9 +14121,9 @@ ts-node-dev@^2.0.0: ts-node "^10.4.0" tsconfig "^7.0.0" -ts-node@^10.4.0, ts-node@^10.9.1: +ts-node@^10.4.0, ts-node@^10.9.1, ts-node@>=9.0.0: version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" @@ -13748,9 +14140,19 @@ ts-node@^10.4.0, ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.10.1, tsconfig-paths@^3.15.0: +tsconfig-paths@^3.10.1: + version "3.15.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tsconfig-paths@^3.15.0: version "3.15.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" @@ -13760,7 +14162,7 @@ tsconfig-paths@^3.10.1, tsconfig-paths@^3.15.0: tsconfig-paths@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz" integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== dependencies: json5 "^2.2.2" @@ -13769,7 +14171,7 @@ tsconfig-paths@^4.2.0: tsconfig@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + resolved "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz" integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== dependencies: "@types/strip-bom" "^3.0.0" @@ -13779,44 +14181,44 @@ tsconfig@^7.0.0: tslib@^1.8.1: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.2, tslib@^2.6.3: version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== tslib@~2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + resolved "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== tweetnacl-util@^0.15.0: version "0.15.1" - resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== tweetnacl@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== twilio@^4.17.0: version "4.23.0" - resolved "https://registry.yarnpkg.com/twilio/-/twilio-4.23.0.tgz#46b69c231fc7aa9488a7bde09b8659da8c459a56" + resolved "https://registry.npmjs.org/twilio/-/twilio-4.23.0.tgz" integrity sha512-LdNBQfOe0dY2oJH2sAsrxazpgfFQo5yXGxe96QA8UWB5uu+433PrUbkv8gQ5RmrRCqUTPQ0aOrIyAdBr1aB03Q== dependencies: axios "^1.6.0" @@ -13828,43 +14230,59 @@ twilio@^4.17.0: url-parse "^1.5.9" xmlbuilder "^13.0.2" -type-check@^0.4.0, type-check@~0.4.0: +type-check@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== dependencies: prelude-ls "~1.1.2" +type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-detect@4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.3.2.tgz#bb7948127bb644364994dc1b44b8a797da8aadcd" - integrity sha512-VpwuOgnTsQUUWi0id8Hl4/xiQ+OoaeJGe8dnFjzubJYe/lOc2/d1Qx/d3FqWR0FlpOG/cvukAXfB12A49Y4iiA== - type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.3.2.tgz" + integrity sha512-VpwuOgnTsQUUWi0id8Hl4/xiQ+OoaeJGe8dnFjzubJYe/lOc2/d1Qx/d3FqWR0FlpOG/cvukAXfB12A49Y4iiA== + +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + type-is@~1.6.18: version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -13872,12 +14290,12 @@ type-is@~1.6.18: type@^2.7.2: version "2.7.3" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + resolved "https://registry.npmjs.org/type/-/type-2.7.3.tgz" integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== typed-array-buffer@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz" integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== dependencies: call-bound "^1.0.3" @@ -13886,7 +14304,7 @@ typed-array-buffer@^1.0.3: typed-array-byte-length@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz" integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== dependencies: call-bind "^1.0.8" @@ -13897,7 +14315,7 @@ typed-array-byte-length@^1.0.3: typed-array-byte-offset@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz" integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== dependencies: available-typed-arrays "^1.0.7" @@ -13910,7 +14328,7 @@ typed-array-byte-offset@^1.0.4: typed-array-length@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz" integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== dependencies: call-bind "^1.0.7" @@ -13922,52 +14340,62 @@ typed-array-length@^1.0.7: typedarray@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typeforce@^1.11.3, typeforce@^1.11.5, typeforce@^1.18.0: version "1.18.0" - resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" + resolved "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@^3.9.10, typescript@^3.9.7: +typescript@*, "typescript@^3.9.5 || ^4.9.5 || ^5", typescript@^5.2.2, typescript@>=2.7, "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.2.0, typescript@>=4.7.2, typescript@>=5.0.0: + version "5.9.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + +typescript@^3.9.10: + version "3.9.10" + resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + +typescript@^3.9.7: version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typescript@^4.0.0, typescript@^4.9.5: +typescript@^4.0.0: version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -typescript@^5.2.2: - version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz" integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== uglify-js@^3.1.4, uglify-js@^3.16.1, uglify-js@^3.7.7: version "3.19.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== uint8array-tools@0.0.7: version "0.0.7" - resolved "https://registry.yarnpkg.com/uint8array-tools/-/uint8array-tools-0.0.7.tgz#a7a2bb5d8836eae2fade68c771454e6a438b390d" + resolved "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz" integrity sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ== ultron@~1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + resolved "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz" integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== unbox-primitive@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz" integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== dependencies: call-bound "^1.0.3" @@ -13977,62 +14405,62 @@ unbox-primitive@^1.1.0: unc-path-regex@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== undefsafe@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== underscore.string@~3.3.5: version "3.3.6" - resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.6.tgz#ad8cf23d7423cb3b53b898476117588f4e2f9159" + resolved "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz" integrity sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ== dependencies: sprintf-js "^1.1.1" util-deprecate "^1.0.2" -underscore@1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" - integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== - underscore@~1.13.2: version "1.13.7" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz" integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== +underscore@1.12.1: + version "1.12.1" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + undici-types@~6.21.0: version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== undici-types@~7.16.0: version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== undici@^5.22.1: version "5.29.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3" + resolved "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz" integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg== dependencies: "@fastify/busboy" "^2.0.0" undici@^7.12.0: version "7.16.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-7.16.0.tgz#cb2a1e957726d458b536e3f076bf51f066901c1a" + resolved "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz" integrity sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g== unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz" integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" @@ -14040,39 +14468,39 @@ unicode-match-property-ecmascript@^2.0.0: unicode-match-property-value-ecmascript@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz#65a7adfad8574c219890e219285ce4c64ed67eaa" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz" integrity sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg== unicode-property-aliases-ecmascript@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz#301d4f8a43d2b75c97adfad87c9dd5350c9475d1" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz" integrity sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ== uniq@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== universalify@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== unixify@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" + resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz" integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== dependencies: normalize-path "^2.1.1" -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== update-browserslist-db@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz" integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== dependencies: escalade "^3.2.0" @@ -14080,19 +14508,19 @@ update-browserslist-db@^1.1.4: uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" uri-path@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/uri-path/-/uri-path-1.0.0.tgz#9747f018358933c31de0fccfd82d138e67262e32" + resolved "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz" integrity sha512-8pMuAn4KacYdGMkFaoQARicp4HSw24/DHOVKWqVRJ8LhhAwPPFpdGvdL9184JVmUwe7vz7Z9n6IqI6t5n2ELdg== url-parse@^1.5.9: version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" @@ -14100,7 +14528,7 @@ url-parse@^1.5.9: url@^0.11.0: version "0.11.4" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" + resolved "https://registry.npmjs.org/url/-/url-0.11.4.tgz" integrity sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg== dependencies: punycode "^1.4.1" @@ -14108,24 +14536,24 @@ url@^0.11.0: util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== util@0.10.3: version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz" integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== dependencies: inherits "2.0.1" utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== uuid-by-string@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/uuid-by-string/-/uuid-by-string-4.0.0.tgz#1d6da2154294368ae85d3853e4c895cfa52c1179" + resolved "https://registry.npmjs.org/uuid-by-string/-/uuid-by-string-4.0.0.tgz" integrity sha512-88ZSfcSkN04juiLqSsuyteqlSrXNFdsEPzSv3urnElDXNsZUXQN0smeTnh99x2DE15SCUQNgqKBfro54CuzHNQ== dependencies: js-md5 "^0.7.3" @@ -14133,27 +14561,37 @@ uuid-by-string@^4.0.0: uuid@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + resolved "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz" integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== -uuid@^8.0.0, uuid@^8.3.2: +uuid@^8.0.0: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuid@^8.3.2: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0, uuid@^9.0.1: +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +uuid@^9.0.1: version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" @@ -14162,29 +14600,29 @@ v8-to-istanbul@^9.0.1: v8flags@~3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" + resolved "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz" integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== dependencies: homedir-polyfill "^1.0.1" validate-npm-package-name@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz" integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== validate.io-array@^1.0.3: version "1.0.6" - resolved "https://registry.yarnpkg.com/validate.io-array/-/validate.io-array-1.0.6.tgz#5b5a2cafd8f8b85abb2f886ba153f2d93a27774d" + resolved "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz" integrity sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg== validate.io-function@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/validate.io-function/-/validate.io-function-1.0.2.tgz#343a19802ed3b1968269c780e558e93411c0bad7" + resolved "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz" integrity sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ== validate.io-integer-array@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz#2cabde033293a6bcbe063feafe91eaf46b13a089" + resolved "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz" integrity sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA== dependencies: validate.io-array "^1.0.3" @@ -14192,75 +14630,75 @@ validate.io-integer-array@^1.0.0: validate.io-integer@^1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/validate.io-integer/-/validate.io-integer-1.0.5.tgz#168496480b95be2247ec443f2233de4f89878068" + resolved "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz" integrity sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ== dependencies: validate.io-number "^1.0.3" validate.io-number@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8" + resolved "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz" integrity sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg== -value-or-promise@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" - integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== - value-or-promise@^1.0.12: version "1.0.12" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" + resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz" integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== -varuint-bitcoin@1.1.2, varuint-bitcoin@^1.1.2: +value-or-promise@1.0.11: + version "1.0.11" + resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz" + integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== + +varuint-bitcoin@^1.1.2, varuint-bitcoin@1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz#e76c138249d06138b480d4c5b40ef53693e24e92" + resolved "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz" integrity sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw== dependencies: safe-buffer "^5.1.1" -vary@^1, vary@~1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== walkdir@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" + resolved "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz" integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== walker@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" -wcwidth@>=1.0.1, wcwidth@^1.0.1: +wcwidth@^1.0.1, wcwidth@>=1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" web-streams-polyfill@4.0.0-beta.3: version "4.0.0-beta.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz" integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== webidl-conversions@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== websocket-driver@>=0.5.1: version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== dependencies: http-parser-js ">=0.5.1" @@ -14269,12 +14707,12 @@ websocket-driver@>=0.5.1: websocket-extensions@>=0.1.1: version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== websocket-stream@^5.0.1: version "5.5.2" - resolved "https://registry.yarnpkg.com/websocket-stream/-/websocket-stream-5.5.2.tgz#49d87083d96839f0648f5513bbddd581f496b8a2" + resolved "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.2.tgz" integrity sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ== dependencies: duplexify "^3.5.1" @@ -14286,29 +14724,29 @@ websocket-stream@^5.0.1: whatwg-encoding@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz" integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== dependencies: iconv-lite "0.6.3" whatwg-fetch@^3.4.1: version "3.6.20" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz" integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== whatwg-mimetype@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== whatwg-mimetype@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== whatwg-url@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz" integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== dependencies: tr46 "^3.0.0" @@ -14316,7 +14754,7 @@ whatwg-url@^11.0.0: "whatwg-url@^14.1.0 || ^13.0.0": version "14.2.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz" integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== dependencies: tr46 "^5.1.0" @@ -14324,7 +14762,7 @@ whatwg-url@^11.0.0: whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" @@ -14332,7 +14770,7 @@ whatwg-url@^5.0.0: which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz" integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== dependencies: is-bigint "^1.1.0" @@ -14343,7 +14781,7 @@ which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: which-builtin-type@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz" integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== dependencies: call-bound "^1.0.2" @@ -14362,7 +14800,7 @@ which-builtin-type@^1.2.1: which-collection@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: is-map "^2.0.3" @@ -14372,7 +14810,7 @@ which-collection@^1.0.2: which-typed-array@^1.1.16, which-typed-array@^1.1.19: version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz" integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== dependencies: available-typed-arrays "^1.0.7" @@ -14385,45 +14823,54 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.19: which@^1.2.14: version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@~2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wide-align@^1.1.2: version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: string-width "^1.0.2 || 2 || 3 || 4" wif@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" + resolved "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz" integrity sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ== dependencies: bs58check "<3.0.0" word-wrap@^1.2.5, word-wrap@~1.2.3: version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -14432,7 +14879,7 @@ wordwrap@^1.0.0: wrap-ansi@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" @@ -14441,12 +14888,12 @@ wrap-ansi@^8.1.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" @@ -14454,20 +14901,15 @@ write-file-atomic@^4.0.2: write-file-atomic@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" signal-exit "^4.0.1" -ws@8.14.2: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== - ws@^3.2.0: version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + resolved "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz" integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== dependencies: async-limiter "~1.0.0" @@ -14476,27 +14918,32 @@ ws@^3.2.0: ws@^8.14.1: version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== +ws@8.14.2: + version "8.14.2" + resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + xml@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== xmlbuilder@^13.0.2: version "13.0.2" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz" integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ== xmlcreate@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be" + resolved "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz" integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg== xss@^1.0.8: version "1.0.15" - resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a" + resolved "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz" integrity sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg== dependencies: commander "^2.20.3" @@ -14504,37 +14951,37 @@ xss@^1.0.8: xtend@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.0: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^17.0.1, yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -14547,17 +14994,17 @@ yargs@^17.0.1, yargs@^17.3.1, yargs@^17.7.2: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yup@^0.32.0: version "0.32.11" - resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5" + resolved "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz" integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg== dependencies: "@babel/runtime" "^7.15.4" @@ -14570,7 +15017,7 @@ yup@^0.32.0: zen-observable-ts@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz" integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA== dependencies: "@types/zen-observable" "0.8.3" @@ -14578,17 +15025,22 @@ zen-observable-ts@^1.1.0: zen-observable-ts@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz" integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== dependencies: zen-observable "0.8.15" zen-observable@0.8.15: version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== -zod@^3.22.2: +zod-to-json-schema@^3.25.0: + version "3.25.1" + resolved "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz" + integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== + +zod@^3.0.0, zod@^3.22.0, zod@^3.22.2, "zod@^3.25 || ^4", "zod@^3.25 || ^4.0": version "3.25.76" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + resolved "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== From 454bf99fbde5fe316d0c81e8c765b4d6312cc605 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:16 -0400 Subject: [PATCH 02/22] ENG-278 ENG-280 ENG-281 ENG-282 ENG-283 ENG-284 ENG-285 ENG-349 ENG-363 ENG-297 fix(bridge): apply audit hardening and hosted KYC refinements --- dev/apollo-federation/supergraph.graphql | 140 +++++++++- .../admin/account-update-level.bru | 2 +- .../Flash GraphQL API/environments/local.bru | 2 + .../mutations/bridgeCreateVirtualAccount.bru | 37 +++ .../token/mutations/bridgeInitiateKyc.bru | 44 +++ .../token/queries/bridgeKycStatus.bru | 26 ++ .../token/queries/bridgeVirtualAccount.bru | 32 +++ dev/config/base-config.yaml | 33 ++- package.json | 2 +- src/app/offers/Validator.ts | 2 +- src/app/wallets/send-on-chain.ts | 6 +- src/config/schema.ts | 1 + src/config/schema.types.d.ts | 3 + src/domain/accounts/index.types.d.ts | 6 +- .../bitcoin/lightning/invoice-expiration.ts | 6 +- src/domain/shared/index.ts | 1 + src/graphql/admin/schema.graphql | 1 + src/graphql/error-map.ts | 8 + .../mutation/bridge-add-external-account.ts | 3 +- .../mutation/bridge-create-virtual-account.ts | 3 +- .../root/mutation/bridge-initiate-kyc.ts | 20 +- .../mutation/bridge-initiate-withdrawal.ts | 24 +- .../root/mutation/onchain-payment-send-all.ts | 4 + .../root/query/bridge-virtual-account.ts | 15 +- src/graphql/public/schema.graphql | 117 +++++++- .../types/object/bridge-virtual-account.ts | 13 +- ...bridge-virtual-account-unique-accountid.ts | 103 +++++++ src/servers/bridge-webhook-server.ts | 13 +- src/services/bridge/client.ts | 116 +++++--- src/services/bridge/errors.ts | 12 + src/services/bridge/index.ts | 245 ++++++++++++----- .../middleware/verify-signature.ts | 24 +- .../bridge/webhook-server/routes/deposit.ts | 30 +- .../bridge/webhook-server/routes/kyc.ts | 36 ++- src/services/ibex/client.ts | 119 ++++++-- src/services/ibex/types.ts | 4 +- .../webhook-server/routes/crypto-receive.ts | 4 +- src/services/lock/index.ts | 7 +- src/services/mongoose/accounts.ts | 12 +- src/services/mongoose/bridge-accounts.ts | 48 +++- src/services/mongoose/schema.ts | 21 +- src/services/mongoose/schema.types.d.ts | 2 +- test/flash/unit/services/bridge/index.spec.ts | 260 ++++++++++++++++++ 43 files changed, 1403 insertions(+), 204 deletions(-) create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru create mode 100644 src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts create mode 100644 test/flash/unit/services/bridge/index.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 0b7a93596..7f2bcb3a7 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -264,6 +264,89 @@ input BankAccountInput currency: String! } +type BridgeAddExternalAccountPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + externalAccount: BridgeExternalAccount +} + +type BridgeCreateVirtualAccountPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + virtualAccount: BridgeVirtualAccount +} + +type BridgeExternalAccount + @join__type(graph: PUBLIC) +{ + accountNumberLast4: String! + bankName: String! + id: ID! + status: String! +} + +input BridgeInitiateKycInput + @join__type(graph: PUBLIC) +{ + email: String + full_name: String + type: String +} + +type BridgeInitiateKycPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + kycLink: BridgeKycLink +} + +input BridgeInitiateWithdrawalInput + @join__type(graph: PUBLIC) +{ + amount: String! + externalAccountId: ID! +} + +type BridgeInitiateWithdrawalPayload + @join__type(graph: PUBLIC) +{ + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + +type BridgeKycLink + @join__type(graph: PUBLIC) +{ + kycLink: String! + tosLink: String! +} + +type BridgeVirtualAccount + @join__type(graph: PUBLIC) +{ + accountNumber: String + accountNumberLast4: String + bankName: String + id: ID + kycLink: String + message: String + pending: Boolean + routingNumber: String + tosLink: String +} + +type BridgeWithdrawal + @join__type(graph: PUBLIC) +{ + amount: String! + createdAt: String! + currency: String! + id: ID! + status: String! +} + """ A wallet belonging to an account which contains a BTC balance and a list of transactions. """ @@ -1101,6 +1184,10 @@ type Mutation accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! + bridgeAddExternalAccount: BridgeAddExternalAccountPayload! + bridgeCreateVirtualAccount: BridgeCreateVirtualAccountPayload! + bridgeInitiateKyc(input: BridgeInitiateKycInput!): BridgeInitiateKycPayload! + bridgeInitiateWithdrawal(input: BridgeInitiateWithdrawalInput!): BridgeInitiateWithdrawalPayload! businessAccountUpgradeRequest(input: BusinessAccountUpgradeRequestInput!): AccountUpgradePayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! @@ -1557,13 +1644,17 @@ type Query @join__type(graph: PUBLIC) { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! + accountUpgradeRequest: AccountUpgradeRequestPayload! + bridgeExternalAccounts: [BridgeExternalAccount] + bridgeKycStatus: String + bridgeVirtualAccount: BridgeVirtualAccount + bridgeWithdrawals: [BridgeWithdrawal] btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] @@ -1940,6 +2031,52 @@ type UpgradePayload scalar USDCents @join__type(graph: PUBLIC) +""" +A wallet belonging to an account which contains a USDT balance and a list of transactions. +""" +type UsdtWallet implements Wallet + @join__implements(graph: PUBLIC, interface: "Wallet") + @join__type(graph: PUBLIC) +{ + accountId: ID! + balance: FractionalCentAmount! + id: ID! + lnurlp: Lnurl + + """An unconfirmed incoming onchain balance.""" + pendingIncomingBalance: SignedAmount! + transactions( + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + transactionsByAddress( + """Returns the items that include this address.""" + address: OnChainAddress! + + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + walletCurrency: WalletCurrency! +} + """ A wallet belonging to an account which contains a USD balance and a list of transactions. """ @@ -2333,6 +2470,7 @@ enum WalletCurrency { BTC @join__enumValue(graph: PUBLIC) USD @join__enumValue(graph: PUBLIC) + USDT @join__enumValue(graph: PUBLIC) } """Unique identifier of a wallet""" diff --git a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru index 679cd0e8e..d9fb2ed1e 100644 --- a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru +++ b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru @@ -32,7 +32,7 @@ body:graphql { body:graphql:vars { { "input": { - "uid": "69a6f3aeaa8667d2c0d65922", + "uid": "69f636d77a39a2a9a1807aa9", "level": "THREE", "erpParty": "John Doe" } diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 9ea0c4934..311dcfcbf 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -8,4 +8,6 @@ vars { token: walletId: walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 + userEmail: maurientesol@gmail.com + userFullName: maurientesol } diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru new file mode 100644 index 000000000..740fb6ec7 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeCreateVirtualAccount.bru @@ -0,0 +1,37 @@ +meta { + name: bridgeCreateVirtualAccount + type: graphql + seq: 37 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeCreateVirtualAccount { + bridgeCreateVirtualAccount { + errors { + message + code + } + virtualAccount { + id + bankName + routingNumber + accountNumberLast4 + } + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} \ No newline at end of file diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru new file mode 100644 index 000000000..4d9db31b0 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateKyc.bru @@ -0,0 +1,44 @@ +meta { + name: bridgeInitiateKyc + type: graphql + seq: 36 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeInitiateKyc($input: BridgeInitiateKycInput!) { + bridgeInitiateKyc(input: $input) { + errors { + message + } + kycLink { + kycLink + tosLink + } + } + } +} + +body:graphql:vars { + { + "input": { + "email": "{{userEmail}}", + "type": "individual", + "full_name": "{{userFullName}}" + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru new file mode 100644 index 000000000..c85ab9cb9 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeKycStatus.bru @@ -0,0 +1,26 @@ +meta { + name: bridgeKycStatus + type: graphql + seq: 19 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeKycStatus { + bridgeKycStatus + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru new file mode 100644 index 000000000..e3e9c0f3e --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeVirtualAccount.bru @@ -0,0 +1,32 @@ +meta { + name: bridgeVirtualAccount + type: graphql + seq: 18 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeVirtualAccount { + bridgeVirtualAccount { + id + bankName + routingNumber + accountNumberLast4 + accountNumber + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index da545d17e..0b1fe24fb 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -5,22 +5,43 @@ admin_accounts: ibex: url: "https://api-sandbox.poweredbyibex.io" + authUrl: "https://auth.hub.sandbox.poweredbyibex.io" email: "" - password: "" + password: "Ibex@1234" + apiKey: "sk-test-3bd6463c9cd77c3d8858c60b9997d0c6" webhook: uri: "" # external uri defined in overrides port: 4008 secret: "not-so-secret" bridge: - enabled: false - apiKey: "" - baseUrl: "https://api.bridge.xyz" + enabled: true + apiKey: "sk-test-3bd6463c9cd77c3d8858c60b9997d0c6" + baseUrl: "https://api.sandbox.bridge.xyz/v0" + minWithdrawalAmount: 10 webhook: port: 4009 publicKeys: - kyc: "" - deposit: "" + kyc: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrV+s8CvC0+s1W6vZG52 + 5eozo6W6HzkTcLQMWDoEzQX+ulEoYH2fPuXeupi11MdVLpEqNqYas8LD3BIf/c9H + kK54V8vnXNwoHa5ROp/Gjp3B17q3wGfjLa8bQJoJZFWd9W+e3TjUohCDNpeD/qv+ + bkY2y3b1QixmXKK3REw35sfiEe5NkGMU4aEfXhZieIZ1mKXLsIgsgrIpv9BFwQr5 + +h3R7Vv3hGKVgSZHnRMa9F1/go8v5Au8gj+9w0LxxRJikoJCubI6igaTCivibxuo + QXWfFylw6m7eQTvZDQz70pnUEakofRlvKasetbyKmvLzMhuRHeqsxgi8C4ZCx7MP + dwIDAQAB + -----END PUBLIC KEY----- + deposit: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrV+s8CvC0+s1W6vZG52 + 5eozo6W6HzkTcLQMWDoEzQX+ulEoYH2fPuXeupi11MdVLpEqNqYas8LD3BIf/c9H + kK54V8vnXNwoHa5ROp/Gjp3B17q3wGfjLa8bQJoJZFWd9W+e3TjUohCDNpeD/qv+ + bkY2y3b1QixmXKK3REw35sfiEe5NkGMU4aEfXhZieIZ1mKXLsIgsgrIpv9BFwQr5 + +h3R7Vv3hGKVgSZHnRMa9F1/go8v5Au8gj+9w0LxxRJikoJCubI6igaTCivibxuo + QXWfFylw6m7eQTvZDQz70pnUEakofRlvKasetbyKmvLzMhuRHeqsxgi8C4ZCx7MP + dwIDAQAB + -----END PUBLIC KEY----- transfer: "" timestampSkewMs: 300000 diff --git a/package.json b/package.json index e109959dc..da883eb60 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "start-main": ". ./.env && yarn run build && node --inspect -r ./lib/services/tracing.js ./lib/servers/graphql-main-server.js", "migrate-mongo-up": "migrate-mongo up -f './src/migrations/migrate-mongo-config.js'", "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts", - "bridge-webhook": "ts-node src/servers/bridge-webhook-server.ts" + "bridge-webhook": ". ./.env && ts-node --transpile-only -r tsconfig-paths/register src/servers/bridge-webhook-server.ts --configPath dev/config/base-config.yaml" }, "engines": { "node": ">=20.18.1 <21" diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index 680e6538a..e16b97735 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -67,7 +67,7 @@ const verifyBankAccount = async (o: ValidationInputs): Promise([ isUsd, cashoutMin, cashoutMax, diff --git a/src/app/wallets/send-on-chain.ts b/src/app/wallets/send-on-chain.ts index 28deaa0e7..a50ed3034 100644 --- a/src/app/wallets/send-on-chain.ts +++ b/src/app/wallets/send-on-chain.ts @@ -75,7 +75,11 @@ export const payOnChainByWalletId = async ({ }) if (validationResult instanceof Error) return validationResult - const resp = await Ibex.sendOnchain(args) + const resp = await Ibex.sendOnchain({ + accountId: args.accountId, + address: args.address, + amount: amount.toIbex(), + }) if (resp instanceof IbexError) return resp let status = IbexAdaptor.toPaymentSendStatus(resp.status) diff --git a/src/config/schema.ts b/src/config/schema.ts index 18010350d..2ed56f48d 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -630,6 +630,7 @@ export const configSchema = { type: "object", properties: { url: { type: "string" }, + authUrl: { type: "string" }, email: { type: "string" }, password: { type: "string" }, webhook: { diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index b12767c01..36e6df9f5 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -24,8 +24,10 @@ type WebhookServer = { type IbexConfig = { url: string + authUrl: string email: string password: string + apiKey?: string webhook: WebhookServer } @@ -45,6 +47,7 @@ type BridgeConfig = { enabled: boolean apiKey: string baseUrl: string + minWithdrawalAmount: number webhook: BridgeWebhook } diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index c2c11bd2a..3fed86428 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -89,7 +89,7 @@ type Account = { // Bridge integration: bridgeCustomerId?: BridgeCustomerId bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string } // deprecated @@ -180,11 +180,11 @@ interface IAccountsRepository { fields: { bridgeCustomerId?: BridgeCustomerId bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string }, ): Promise - findByBridgeTronAddress(address: string): Promise + findByBridgeEthereumAddress(address: string): Promise findByBridgeCustomerId(customerId: BridgeCustomerId): Promise } diff --git a/src/domain/bitcoin/lightning/invoice-expiration.ts b/src/domain/bitcoin/lightning/invoice-expiration.ts index 886d3bdc8..8608ebf18 100644 --- a/src/domain/bitcoin/lightning/invoice-expiration.ts +++ b/src/domain/bitcoin/lightning/invoice-expiration.ts @@ -15,7 +15,11 @@ export const DEFAULT_EXPIRATIONS = { JMD: { delay: defaultTimeToExpiryInSeconds, delayMinutes: (defaultTimeToExpiryInSeconds / SECS_PER_MIN) as Minutes, - } + }, + USDT: { + delay: defaultTimeToExpiryInSeconds, + delayMinutes: (defaultTimeToExpiryInSeconds / SECS_PER_MIN) as Minutes, + }, } export const invoiceExpirationForCurrency = ( diff --git a/src/domain/shared/index.ts b/src/domain/shared/index.ts index d7b3f887c..72ced5a95 100644 --- a/src/domain/shared/index.ts +++ b/src/domain/shared/index.ts @@ -9,6 +9,7 @@ export * from "./errors" export * from "./error-parsers" export * from "./error-parsers-unknown" export * from "./validation" +export { USDTAmount } from "./MoneyAmount" export const setErrorWarn = (error: DomainError): DomainError => { error.level = ErrorLevel.Warn diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index ab1413521..d858eaf79 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -614,6 +614,7 @@ interface Wallet { enum WalletCurrency { BTC USD + USDT } """Unique identifier of a wallet""" diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 67183ead0..95a461d66 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -476,6 +476,14 @@ export const mapError = (error: ApplicationError): CustomApolloError => { logger: baseLogger, }) + case "BridgeInvalidAmountError": + message = error.message || "Amount must be strictly positive with at most 6 decimal places" + return new ValidationInternalError({ message, logger: baseLogger }) + + case "BridgeBelowMinimumWithdrawalError": + message = error.message || "Withdrawal amount is below the minimum" + return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeDisabledError": message = "Bridge integration is currently disabled" return new ValidationInternalError({ message, logger: baseLogger }) diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts index 10e4e279b..0c4a2fd19 100644 --- a/src/graphql/public/root/mutation/bridge-add-external-account.ts +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -1,5 +1,6 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" @@ -8,7 +9,7 @@ import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/e const BridgeAddExternalAccountPayload = GT.Object({ name: "BridgeAddExternalAccountPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, externalAccount: { type: BridgeExternalAccount }, }), }) diff --git a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts index 55abd4998..487a732f8 100644 --- a/src/graphql/public/root/mutation/bridge-create-virtual-account.ts +++ b/src/graphql/public/root/mutation/bridge-create-virtual-account.ts @@ -1,5 +1,6 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" @@ -8,7 +9,7 @@ import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/e const BridgeCreateVirtualAccountPayload = GT.Object({ name: "BridgeCreateVirtualAccountPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, virtualAccount: { type: BridgeVirtualAccount }, }), }) diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts index 9e3234ee3..89da4f77a 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -1,5 +1,6 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeKycLink from "@graphql/public/types/object/bridge-kyc-link" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" @@ -8,14 +9,27 @@ import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/e const BridgeInitiateKycPayload = GT.Object({ name: "BridgeInitiateKycPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, kycLink: { type: BridgeKycLink }, }), }) +const BridgeInitiateKycInput = GT.Input({ + name: "BridgeInitiateKycInput", + fields: () => ({ + email: { type: GT.String }, + type: { type: GT.String }, + full_name: { type: GT.String }, + }), +}) + const bridgeInitiateKyc = GT.Field({ type: GT.NonNull(BridgeInitiateKycPayload), - resolve: async (_, __, { domainAccount }: GraphQLPublicContextAuth) => { + args: { + input: { type: GT.NonNull(BridgeInitiateKycInput) }, + }, + resolve: async (_, { input }, { domainAccount }: GraphQLPublicContextAuth) => { + const { email, type, full_name } = input if (!BridgeConfig.enabled) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } @@ -24,7 +38,7 @@ const bridgeInitiateKyc = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } - const result = await BridgeService.initiateKyc(domainAccount.id) + const result = await BridgeService.initiateKyc({ accountId: domainAccount.id, email, type, full_name }) if (result instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(result)] } } diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts index 738c10c83..afef326e6 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -1,9 +1,15 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import IError from "@graphql/shared/types/abstract/error" import BridgeWithdrawal from "@graphql/public/types/object/bridge-withdrawal" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" -import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" +import { + BridgeDisabledError, + BridgeAccountLevelError, + BridgeInvalidAmountError, + BridgeBelowMinimumWithdrawalError, +} from "@services/bridge/errors" const BridgeInitiateWithdrawalInput = GT.Input({ name: "BridgeInitiateWithdrawalInput", @@ -16,7 +22,7 @@ const BridgeInitiateWithdrawalInput = GT.Input({ const BridgeInitiateWithdrawalPayload = GT.Object({ name: "BridgeInitiateWithdrawalPayload", fields: () => ({ - errors: { type: GT.List(GT.NonNull(Error)) }, + errors: { type: GT.NonNullList(IError) }, withdrawal: { type: BridgeWithdrawal }, }), }) @@ -29,6 +35,20 @@ const bridgeInitiateWithdrawal = GT.Field({ resolve: async (_, args, { domainAccount }: GraphQLPublicContextAuth) => { const { amount, externalAccountId } = args.input + // validate the amount is positive and has at most 6 decimal places + if (!/^\d+(\.\d{1,6})?$/.test(amount) || parseFloat(amount) <= 0) { + return { + errors: [mapAndParseErrorForGqlResponse(new BridgeInvalidAmountError())], + } + } + + // validate the amount is greater than the minimum withdrawal amount + if (parseFloat(amount) < BridgeConfig.minWithdrawalAmount) { + return { + errors: [mapAndParseErrorForGqlResponse(new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount))], + } + } + if (!BridgeConfig.enabled) { return { errors: [mapAndParseErrorForGqlResponse(new BridgeDisabledError())] } } diff --git a/src/graphql/public/root/mutation/onchain-payment-send-all.ts b/src/graphql/public/root/mutation/onchain-payment-send-all.ts index b6a2f2003..2f90ff8c9 100644 --- a/src/graphql/public/root/mutation/onchain-payment-send-all.ts +++ b/src/graphql/public/root/mutation/onchain-payment-send-all.ts @@ -10,6 +10,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import { Wallets } from "@app" import { getBalanceForWallet } from "@app/wallets" +import { USDAmount } from "@domain/shared" const OnChainPaymentSendAllInput = GT.Input({ name: "OnChainPaymentSendAllInput", @@ -64,6 +65,9 @@ const OnChainPaymentSendAllMutation = GT.Field< const amount = await getBalanceForWallet({ walletId }) if (amount instanceof Error) return amount + if (!(amount instanceof USDAmount)) { + return { errors: [{ message: "Onchain payments require a USD wallet" }] } + } const result = await Wallets.payOnChainByWalletId({ senderAccount: domainAccount, diff --git a/src/graphql/public/root/query/bridge-virtual-account.ts b/src/graphql/public/root/query/bridge-virtual-account.ts index a563273dd..eebcc6c84 100644 --- a/src/graphql/public/root/query/bridge-virtual-account.ts +++ b/src/graphql/public/root/query/bridge-virtual-account.ts @@ -3,7 +3,7 @@ import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import BridgeVirtualAccount from "@graphql/public/types/object/bridge-virtual-account" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" -import { BridgeDisabledError } from "@services/bridge/errors" +import { BridgeAccountLevelError, BridgeDisabledError } from "@services/bridge/errors" const bridgeVirtualAccount = GT.Field({ type: BridgeVirtualAccount, @@ -14,6 +14,19 @@ const bridgeVirtualAccount = GT.Field({ if (!domainAccount) return null + if (domainAccount.level < 2) { + throw mapAndParseErrorForGqlResponse(new BridgeAccountLevelError()) + } + + // KYC exists but not yet approved + if (domainAccount.bridgeKycStatus !== "approved") { + return { + pending: true, + message: "KYC verification is pending. Please wait for approval.", + } + } + + // KYC approved — return existing virtual account const result = await BridgeService.getVirtualAccount(domainAccount.id) if (result instanceof Error) { throw mapAndParseErrorForGqlResponse(result) diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 2935c0472..a6e0a50a1 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -241,6 +241,69 @@ input BankAccountInput { currency: String! } +type BridgeAddExternalAccountPayload { + errors: [Error!]! + externalAccount: BridgeExternalAccount +} + +type BridgeCreateVirtualAccountPayload { + errors: [Error!]! + virtualAccount: BridgeVirtualAccount +} + +type BridgeExternalAccount { + accountNumberLast4: String! + bankName: String! + id: ID! + status: String! +} + +input BridgeInitiateKycInput { + email: String + full_name: String + type: String +} + +type BridgeInitiateKycPayload { + errors: [Error!]! + kycLink: BridgeKycLink +} + +input BridgeInitiateWithdrawalInput { + amount: String! + externalAccountId: ID! +} + +type BridgeInitiateWithdrawalPayload { + errors: [Error!]! + withdrawal: BridgeWithdrawal +} + +type BridgeKycLink { + kycLink: String! + tosLink: String! +} + +type BridgeVirtualAccount { + accountNumber: String + accountNumberLast4: String + bankName: String + id: ID + kycLink: String + message: String + pending: Boolean + routingNumber: String + tosLink: String +} + +type BridgeWithdrawal { + amount: String! + createdAt: String! + currency: String! + id: ID! + status: String! +} + type BuildInformation { commitHash: String helmRevision: Int @@ -853,6 +916,10 @@ type Mutation { accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! + bridgeAddExternalAccount: BridgeAddExternalAccountPayload! + bridgeCreateVirtualAccount: BridgeCreateVirtualAccountPayload! + bridgeInitiateKyc(input: BridgeInitiateKycInput!): BridgeInitiateKycPayload! + bridgeInitiateWithdrawal(input: BridgeInitiateWithdrawalInput!): BridgeInitiateWithdrawalPayload! businessAccountUpgradeRequest(input: BusinessAccountUpgradeRequestInput!): AccountUpgradePayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! @@ -1221,13 +1288,17 @@ type PublicWallet { type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! + accountUpgradeRequest: AccountUpgradeRequestPayload! + bridgeExternalAccounts: [BridgeExternalAccount] + bridgeKycStatus: String + bridgeVirtualAccount: BridgeVirtualAccount + bridgeWithdrawals: [BridgeWithdrawal] btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] @@ -1577,6 +1648,49 @@ type UsdWallet implements Wallet { walletCurrency: WalletCurrency! } +""" +A wallet belonging to an account which contains a USDT balance and a list of transactions. +""" +type UsdtWallet implements Wallet { + accountId: ID! + balance: FractionalCentAmount! + id: ID! + lnurlp: Lnurl + + """An unconfirmed incoming onchain balance.""" + pendingIncomingBalance: SignedAmount! + transactions( + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + transactionsByAddress( + """Returns the items that include this address.""" + address: OnChainAddress! + + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + walletCurrency: WalletCurrency! +} + type User { """Bank accounts available for cashout""" bankAccounts: [BankAccount!]! @@ -1849,6 +1963,7 @@ interface Wallet { enum WalletCurrency { BTC USD + USDT } """Unique identifier of a wallet""" diff --git a/src/graphql/public/types/object/bridge-virtual-account.ts b/src/graphql/public/types/object/bridge-virtual-account.ts index eef1c4468..f2c294b83 100644 --- a/src/graphql/public/types/object/bridge-virtual-account.ts +++ b/src/graphql/public/types/object/bridge-virtual-account.ts @@ -3,10 +3,15 @@ import { GT } from "@graphql/index" const BridgeVirtualAccount = GT.Object({ name: "BridgeVirtualAccount", fields: () => ({ - id: { type: GT.NonNullID }, - bankName: { type: GT.NonNull(GT.String) }, - routingNumber: { type: GT.NonNull(GT.String) }, - accountNumberLast4: { type: GT.NonNull(GT.String) }, + id: { type: GT.ID, resolve: (src) => src.virtualAccountId ?? src.bridgeVirtualAccountId }, + bankName: { type: GT.String }, + routingNumber: { type: GT.String }, + accountNumber: { type: GT.String }, + accountNumberLast4: { type: GT.String }, + pending: { type: GT.Boolean }, + message: { type: GT.String }, + kycLink: { type: GT.String }, + tosLink: { type: GT.String }, }), }) diff --git a/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts new file mode 100644 index 000000000..3f6f6f71a --- /dev/null +++ b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts @@ -0,0 +1,103 @@ +// @ts-nocheck +/* eslint @typescript-eslint/no-var-requires: "off" */ + +/** + * Migration: unique index on bridgevirtualaccounts.accountId + * + * Background + * ---------- + * BridgeService.createVirtualAccount now carries an idempotency guard that + * uses a MongoDB upsert keyed on accountId. The upsert is only atomic against + * concurrent writes if a unique index backs the filter field. Without it two + * racing writes can both pass the findOneAndUpdate "find" phase, both insert, + * and produce duplicate VAs for the same account. + * + * What this migration does + * ------------------------ + * 1. Audits the collection for any existing accountId duplicates. + * 2. For each duplicate group, keeps the oldest document (earliest createdAt) + * and removes the rest. Removed _ids are logged so they can be reconciled + * against Bridge if necessary. + * 3. Drops any existing plain index on accountId (Mongoose created it as + * index:true before this change). + * 4. Creates the new unique index { accountId: 1 }. + * + * Rollback (down) + * --------------- + * Drops the unique index and restores a plain index so the app schema stays + * consistent with a pre-migration schema.ts. + */ + +const COLLECTION = "bridgevirtualaccounts" +const INDEX_NAME = "accountId_1" + +module.exports = { + async up(db) { + const col = db.collection(COLLECTION) + + // ── Step 1: find duplicate accountId groups ────────────────────────────── + const duplicates = await col + .aggregate([ + { $group: { _id: "$accountId", count: { $sum: 1 }, docs: { $push: "$$ROOT" } } }, + { $match: { count: { $gt: 1 } } }, + ]) + .toArray() + + if (duplicates.length > 0) { + console.log( + `[migration] Found ${duplicates.length} accountId(s) with duplicate VA records. Deduplicating...`, + ) + + for (const group of duplicates) { + // Sort ascending by createdAt — keep the winner (index 0), remove the rest + const sorted = group.docs.sort( + (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(), + ) + const [winner, ...losers] = sorted + const loserIds = losers.map((d) => d._id) + + console.log( + `[migration] accountId=${group._id} — keeping _id=${winner._id} bridgeVAId=${winner.bridgeVirtualAccountId}, removing ${loserIds.length} duplicate(s): ${loserIds.join(", ")}`, + ) + + await col.deleteMany({ _id: { $in: loserIds } }) + } + + console.log("[migration] Deduplication complete.") + } else { + console.log("[migration] No duplicate accountId records found. Proceeding.") + } + + // ── Step 2: drop stale plain index if it exists ────────────────────────── + const existingIndexes = await col.indexes() + const hasPlainIndex = existingIndexes.some( + (idx) => idx.name === INDEX_NAME && !idx.unique, + ) + if (hasPlainIndex) { + await col.dropIndex(INDEX_NAME) + console.log(`[migration] Dropped existing non-unique index "${INDEX_NAME}".`) + } + + // ── Step 3: create unique index ────────────────────────────────────────── + await col.createIndex({ accountId: 1 }, { unique: true, name: INDEX_NAME }) + console.log(`[migration] Created unique index "${INDEX_NAME}" on ${COLLECTION}.`) + }, + + async down(db) { + const col = db.collection(COLLECTION) + + // Drop the unique index + const existingIndexes = await col.indexes() + const hasUniqueIndex = existingIndexes.some( + (idx) => idx.name === INDEX_NAME && idx.unique, + ) + if (hasUniqueIndex) { + await col.dropIndex(INDEX_NAME) + console.log(`[migration] Dropped unique index "${INDEX_NAME}".`) + } + + // Restore plain index so Mongoose schema (pre-change) stays consistent + await col.createIndex({ accountId: 1 }, { name: INDEX_NAME }) + console.log(`[migration] Restored plain index "${INDEX_NAME}" on ${COLLECTION}.`) + }, +} diff --git a/src/servers/bridge-webhook-server.ts b/src/servers/bridge-webhook-server.ts index b05763936..6dd964ab2 100644 --- a/src/servers/bridge-webhook-server.ts +++ b/src/servers/bridge-webhook-server.ts @@ -1,8 +1,9 @@ -/** - * Bridge Webhook Server Entrypoint - * Starts the standalone Bridge webhook server - */ - import { startBridgeWebhookServer } from "@services/bridge/webhook-server" +import { baseLogger } from "@services/logger" +import { setupMongoConnection } from "@services/mongodb" -startBridgeWebhookServer() +if (require.main === module) { + setupMongoConnection() + .then(async () => startBridgeWebhookServer()) + .catch((err) => baseLogger.error(err, "bridge webhook server error")) +} diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index f8e64dfd2..6052cc983 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -3,7 +3,9 @@ * Ported from bridge-mcp and extended with Tron/USDT support */ +import crypto from "crypto" import { BridgeConfig } from "@config" + import { BridgeCustomerId, BridgeVirtualAccountId, @@ -36,16 +38,16 @@ export interface CreateIndividualCustomerRequest { last_name: string email: string phone?: string - address?: { + residential_address?: { street_line_1: string street_line_2?: string city: string - state?: string + subdivision?: string postal_code: string country: string } birth_date?: string - tax_identification_number?: string + signed_agreement_id?: string } export interface CreateBusinessCustomerRequest { @@ -53,15 +55,15 @@ export interface CreateBusinessCustomerRequest { business_name: string email: string phone?: string - address?: { + residential_address?: { street_line_1: string street_line_2?: string city: string - state?: string + subdivision?: string postal_code: string country: string } - ein?: string + signed_agreement_id?: string } export type CreateCustomerRequest = @@ -71,8 +73,8 @@ export type CreateCustomerRequest = export interface Customer { id: string type: "individual" | "business" - kyc_status: string - tos_status: string + status?: "active" | "awaiting_questionnaire" | "rejected" | "paused" | "under_review" | "offboarded" | "awaiting_ubo" | "incomplete" | "not_started" + has_accepted_terms_of_service?: string created_at: string updated_at: string first_name?: string @@ -88,44 +90,53 @@ export interface KycLink { } // Extended payment rails to include Tron -export type PaymentRail = - | "solana" - | "ethereum" - | "polygon" - | "base" - | "tron" - | "ach_push" - | "ach_pull" - | "wire" - | "sepa" - | "spei" - | "pix" +export type PaymentRail = "ach" | "wire" | "ach_push" | "ach_same_day" | "arbitrum" | "avalanche_c_chain" | "base" | "bre_b" | "co_bank_transfer" | "celo" | "ethereum" | "faster_payments" | "optimism" | "pix" | "polygon" | "sepa" | "solana" | "spei" | "stellar" | "swift" | "tempo" | "tron"; -// Extended currencies to include USDT -export type Currency = +export type VirtualAccountDestinationPaymentRail = "arbitrum" | "avalanche_c_chain" | "base" | "celo" | "ethereum" | "optimism" | "polygon" | "solana" | "stellar" | "tempo" | "tron" + + +export type SourceCurrency = | "usd" | "eur" | "mxn" | "brl" | "gbp" - | "usdc" - | "usdb" - | "eurc" - | "usdt" + | "cop" + +// Extended currencies to include USDT +export type Currency = "usdb" | "usdt" | "dai" | "pyusd" | "usdc" | "eurc" export interface CreateVirtualAccountRequest { developer_fee_percent?: string source: { - currency: "usd" | "eur" | "mxn" | "brl" | "gbp" + currency: SourceCurrency } destination: { currency: Currency - payment_rail: PaymentRail + payment_rail: VirtualAccountDestinationPaymentRail address?: string + blockchain_memo?: string bridge_wallet_id?: string } } +export interface CreateExternalAccountRequest { + account_owner_name: string + address: { + street_line_1: string + city: string + country: string + } + account_type: string | "us" | "iban" | "unknown" | "clabe" | "pix" | "gb" + currency: 'usd' | 'gbp' | 'brl' | 'eur' | string + account: { + account_number: string + routing_number: string + checking_or_savings?: "checking" | "savings" + } + bank_name?: string +} + export interface VirtualAccount { id: string status: string @@ -134,18 +145,17 @@ export interface VirtualAccount { source_deposit_instructions: { currency: string payment_rails: string[] - bank_name?: string - bank_address?: string - bank_beneficiary_name?: string - bank_account_number?: string - bank_routing_number?: string - iban?: string - bic?: string + bank_name: string + bank_beneficiary_address: string + bank_beneficiary_name: string + bank_account_number: string + bank_routing_number: string } destination: { currency: string payment_rail: string address?: string + blockchain_memo?: string bridge_wallet_id?: string } created_at: string @@ -154,6 +164,7 @@ export interface VirtualAccount { export interface ExternalAccount { id: string customer_id: string + account_owner_name: string account_type: string currency: string bank_name?: string @@ -174,6 +185,7 @@ export interface ListResponse { cursor?: string } +export type TrasfertSourceCurrency = "brl" | "cop" | "dai" | "eur" | "eurc" | "gbp" | "mxn" | "pyusd" | "usd" | "usdb" | "usdc" | "usdt" export interface CreateTransferRequest { amount?: string currency?: string @@ -182,7 +194,7 @@ export interface CreateTransferRequest { developer_fee_percent?: string source: { payment_rail: PaymentRail | "bridge_wallet" - currency: string + currency: TrasfertSourceCurrency from_address?: string external_account_id?: string bridge_wallet_id?: string @@ -240,6 +252,13 @@ export interface Transfer { updated_at: string } + +export interface BridgeIntiateKyc { + email: string + type: "individual" | "business" + full_name?: string +} + // ============ Bridge Client ============ export class BridgeClient { @@ -248,7 +267,7 @@ export class BridgeClient { constructor() { this.apiKey = BridgeConfig.apiKey - this.baseUrl = BridgeConfig.baseUrl || "https://api.bridge.xyz/v0" + this.baseUrl = BridgeConfig.baseUrl || "https://api.sandbox.bridge.xyz/v0" } private async request( @@ -265,8 +284,11 @@ export class BridgeClient { if (idempotencyKey) { headers["Idempotency-Key"] = idempotencyKey + } else { + headers["Idempotency-Key"] = crypto.randomUUID() } + const response = await fetch(url, { method, headers, @@ -301,8 +323,12 @@ export class BridgeClient { // ============ KYC ============ - async createKycLink(customerId: BridgeCustomerId): Promise { - return this.request("POST", "/kyc_links", { customer_id: customerId }) + async createKycLink(request: BridgeIntiateKyc, idempotencyKey?: string): Promise { + return this.request("POST", "/kyc_links", request, idempotencyKey) + } + + async getKycLatestLink(customerId: BridgeCustomerId): Promise { + return this.request("GET", `/customers/${customerId}/kyc_links/latest`) } // ============ Virtual Accounts ============ @@ -322,6 +348,18 @@ export class BridgeClient { // ============ External Accounts ============ + async createExternalAccount( + customerId: BridgeCustomerId, + data: CreateExternalAccountRequest, + idempotencyKey: string, + ): Promise { + return this.request( + "POST", + `/customers/${customerId}/external_accounts`, + data, + idempotencyKey); + } + async getExternalAccountLinkUrl( customerId: BridgeCustomerId, ): Promise { diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 737869fba..69340d4b0 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -57,6 +57,18 @@ export class BridgeAccountLevelError extends BridgeError { } } +export class BridgeBelowMinimumWithdrawalError extends BridgeError { + constructor(minimum: number) { + super(`Withdrawal amount is below the minimum of ${minimum} USDT`) + } +} + +export class BridgeInvalidAmountError extends BridgeError { + constructor(message: string = "Amount must be strictly positive with at most 6 decimal places") { + super(message) + } +} + export class BridgeDisabledError extends BridgeError { constructor(message: string = "Bridge integration is currently disabled") { super(message) diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index fca80b89d..ff73924bd 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -4,6 +4,8 @@ * for USD on/off-ramp functionality via Bridge.xyz */ +import crypto from "crypto" + import { BridgeConfig } from "@config" import BridgeClient, { KycLink, @@ -16,6 +18,7 @@ import { AccountsRepository } from "@services/mongoose/accounts" import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" import { + BridgeError, BridgeDisabledError, BridgeAccountLevelError, BridgeKycPendingError, @@ -24,11 +27,18 @@ import { } from "./errors" import { RepositoryError } from "@domain/errors" import { toBridgeCustomerId, toBridgeExternalAccountId } from "@domain/primitives/bridge" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletsRepository } from "@services/mongoose/wallets" +import { BridgeInsufficientFundsError } from "./errors" +import { IdentityRepository } from "@services/kratos" +import IbexClient from "@services/ibex/client" // ============ Types ============ type InitiateKycResult = { kycLink: string + customerId: string tosLink: string } @@ -36,6 +46,7 @@ type CreateVirtualAccountResult = { virtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string } @@ -65,6 +76,7 @@ type VirtualAccountResult = { bridgeVirtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string } | null @@ -75,8 +87,14 @@ type ExternalAccountResult = { status: "pending" | "verified" | "failed" } +// ============ Helpers ============ + +export const deriveWithdrawalIdempotencyKey = (rowId: string): string => + crypto.createHash("sha256").update(`withdrawal:${rowId}`).digest("hex") + // ============ Guards ============ + const checkBridgeEnabled = (): true | BridgeDisabledError => { if (!BridgeConfig.enabled) { return new BridgeDisabledError() @@ -102,7 +120,7 @@ const checkAccountLevel = async ( * - Creates Bridge customer if not exists * - Returns KYC and TOS links */ -const initiateKyc = async (accountId: AccountId): Promise => { +const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: AccountId, email: string, type?: "individual" | "business", full_name: string }): Promise => { baseLogger.info({ accountId, operation: "initiateKyc" }, "Bridge operation started") const enabledCheck = checkBridgeEnabled() @@ -111,39 +129,45 @@ const initiateKyc = async (accountId: AccountId): Promise w.currency === WalletCurrency.Usdt) + if (!usdtWallet) { + return new BridgeInsufficientFundsError("No USDT wallet found on account") + } + const balance = await getBalanceForWallet({ + walletId: usdtWallet.id, + currency: WalletCurrency.Usdt, + }) + if (balance instanceof Error) return balance + + if (!(balance instanceof USDTAmount)) { + return new BridgeInsufficientFundsError("Invalid balance type") + } + const withdrawalAmount = parseFloat(amount) + if (isNaN(withdrawalAmount) || withdrawalAmount <= 0) { + return new BridgeInsufficientFundsError("Invalid withdrawal amount") + } + + const availableBalance = balance.toIbex(); + if (availableBalance < withdrawalAmount) { + baseLogger.warn( + { accountId, availableBalance, withdrawalAmount, operation: "initiateWithdrawal" }, + "Insufficient USDT balance for withdrawal", + ) + return new BridgeInsufficientFundsError( + `Insufficient USDT balance: available ${availableBalance}, requested ${withdrawalAmount}`, + ) } - // Verify external account exists + // CRIT-2 (ENG-281): Verify caller owns this external account (ownership enforced here + // and at DB level via compound index — see schema.ts BridgeExternalAccountSchema) const externalAccounts = await BridgeAccountsRepo.findExternalAccountsByAccountId( accountId as string, ) @@ -346,38 +438,41 @@ const initiateWithdrawal = async ( (acc) => acc.bridgeExternalAccountId === externalAccountId, ) if (!targetAccount) { + // Do not leak existence — return same error regardless of whether account exists return new Error("External account not found") } if (targetAccount.status !== "verified") { return new Error("External account is not verified") } + + // Store withdrawal record + const pendingWithdrawal = await BridgeAccountsRepo.createWithdrawal({ + accountId: accountId as string, + amount: amount, + currency: "usdt", + externalAccountId, + status: "pending", + }) + if (pendingWithdrawal instanceof Error) return pendingWithdrawal + + const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) + // Create transfer via Bridge const transfer = await BridgeClient.createTransfer(customerId, { amount, on_behalf_of: customerId, source: { - payment_rail: "tron", + payment_rail: "ethereum", currency: "usdt", - from_address: tronAddress, + from_address: ethereumAddress, }, destination: { payment_rail: "ach", currency: "usd", external_account_id: externalAccountId, }, - }) - - // Store withdrawal record - const withdrawalResult = await BridgeAccountsRepo.createWithdrawal({ - accountId: accountId as string, - bridgeTransferId: transfer.id, - amount: transfer.amount, - currency: transfer.currency, - externalAccountId, - status: "pending", - }) - if (withdrawalResult instanceof Error) return withdrawalResult + }, idempotencyKey) const result: InitiateWithdrawalResult = { transferId: transfer.id, @@ -386,6 +481,15 @@ const initiateWithdrawal = async ( state: transfer.state, } + const withdrawalResult = await BridgeAccountsRepo.updateWithdrawalTransferId( + pendingWithdrawal.id, + transfer.id, + transfer.amount, + transfer.currency, + ) + + if (withdrawalResult instanceof Error) return withdrawalResult + baseLogger.info( { accountId, operation: "initiateWithdrawal", transferId: transfer.id }, "Bridge operation completed", @@ -455,9 +559,10 @@ const getVirtualAccount = async ( } const result: VirtualAccountResult = { - bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId, + bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId!, bankName: virtualAccount.bankName, routingNumber: virtualAccount.routingNumber, + accountNumber: virtualAccount.accountNumber, accountNumberLast4: virtualAccount.accountNumberLast4, } @@ -465,7 +570,7 @@ const getVirtualAccount = async ( { accountId, operation: "getVirtualAccount", - virtualAccountId: result.bridgeVirtualAccountId, + virtualAccountId: result!.bridgeVirtualAccountId, }, "Bridge operation completed", ) @@ -545,13 +650,15 @@ const getWithdrawals = async ( ) if (withdrawals instanceof Error) return withdrawals - const result: WithdrawalResult[] = withdrawals.map((w) => ({ - transferId: w.bridgeTransferId, - amount: w.amount, - currency: w.currency, - state: w.status, - createdAt: w.createdAt.toISOString(), - })) + const result: WithdrawalResult[] = withdrawals + .filter((w) => w.bridgeTransferId !== null || w.bridgeTransferId !== undefined) + .map((w) => ({ + transferId: w.bridgeTransferId!, + amount: w.amount, + currency: w.currency, + state: w.status, + createdAt: w.createdAt.toISOString(), + })) baseLogger.info( { accountId, operation: "getWithdrawals", count: result.length }, diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts index b00961288..c4a100ecb 100644 --- a/src/services/bridge/webhook-server/middleware/verify-signature.ts +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -30,12 +30,17 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf return res.status(401).json({ error: "Invalid signature format" }) } - const timestamp = timestampPart.split("=")[1] - const sig = signaturePart.split("=")[1] + const timestamp = timestampPart.slice("t=".length) + const sig = signaturePart.slice("v0=".length) // Check timestamp skew (default 5 minutes) const now = Date.now() const timestampMs = parseInt(timestamp, 10) + if (isNaN(timestampMs) || !isFinite(timestampMs)) { + baseLogger.warn({ timestamp }, "Invalid timestamp format in Bridge webhook signature") + return res.status(401).json({ error: "Invalid signature format" }) + } + const skew = Math.abs(now - timestampMs) if (skew > BridgeConfig.webhook.timestampSkewMs) { @@ -45,12 +50,21 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf // Verify signature using Bridge public key const publicKey = BridgeConfig.webhook.publicKeys[publicKeyType] - const rawBody = (req as any).rawBody || JSON.stringify(req.body) - const payload = `${timestamp}.${rawBody}` + const rawBody = (req as any).rawBody + + if (!rawBody) { + baseLogger.warn(`Missing raw body for webhook`) + return res.status(401).json({ error: "Missing raw body" }) + } + + const signedPayload = `${timestamp}.${rawBody}` + + const digest = crypto.createHash("sha256").update(signedPayload).digest() + baseLogger.debug({ signedPayload, digest: digest.toString("hex") }, "Verifying Bridge webhook signature") try { const verifier = crypto.createVerify("RSA-SHA256") - verifier.update(payload) + verifier.update(digest) const isValid = verifier.verify(publicKey, sig, "base64") if (!isValid) { diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index 79d64253a..ccc98866b 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -11,18 +11,18 @@ import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" export const depositHandler = async (req: Request, res: Response) => { - const { event, data } = req.body - const { transfer_id, amount, currency, tx_hash, customer_id } = data + const { event_id, event_object } = req.body + const { id, amount, deposit_id, currency, subtotal_amount, customer_id, receipt } = event_object - if (!transfer_id || !event) { + if (!id || !event_id) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using transfer_id as lock key - const lockKey = `bridge-deposit:${transfer_id}` + // Idempotency check using deposit_id as lock key + const lockKey = `bridge-deposit:${deposit_id}` const lockResult = await LockService().lockIdempotencyKey(lockKey as any) if (lockResult instanceof Error) { - baseLogger.info({ transfer_id }, "Duplicate Bridge deposit webhook") + baseLogger.info({ event_id, id }, "Duplicate Bridge deposit webhook") return res.status(200).json({ status: "already_processed" }) } @@ -31,19 +31,29 @@ export const depositHandler = async (req: Request, res: Response) => { // The actual balance crediting happens via IBEX crypto webhook baseLogger.info( { - transfer_id, + id, amount, + initial_amount: receipt.initial_amount, currency, - tx_hash, + deposit_id, + receipt: { + url: receipt.url, + initial_amount: receipt.initial_amount, + subtotal_amount: receipt.subtotal_amount, + final_amount: receipt.final_amount, + exchange_fee: receipt.exchange_fee, + gas_fee: receipt.gas_fee, + }, customer_id, - event, + event_id, + }, "Bridge deposit completed", ) return res.status(200).json({ status: "success" }) } catch (error) { - baseLogger.error({ error, transfer_id }, "Error processing Bridge deposit webhook") + baseLogger.error({ error, id, event_id, deposit_id }, "Error processing Bridge deposit webhook") return res.status(500).json({ error: "Internal server error" }) } } diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts index b69dfda08..7a8783742 100644 --- a/src/services/bridge/webhook-server/routes/kyc.ts +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -8,34 +8,35 @@ import { AccountsRepository } from "@services/mongoose/accounts" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { toBridgeCustomerId } from "@domain/primitives/bridge" +import BridgeService from "@services/bridge" export const kycHandler = async (req: Request, res: Response) => { - const { event, data } = req.body - const { customer_id, kyc_status, reason } = data - if (!customer_id || !event) { + const { event_id, event_object } = req.body + const { customer_id, kyc_status, rejection_reasons } = event_object + + if (!customer_id || !event_id) { return res.status(400).json({ error: "Invalid payload" }) } // Idempotency check using customer_id + event as lock key - const lockKey = `bridge-kyc:${customer_id}:${event}` + const lockKey = `bridge-kyc:${customer_id}:${event_object.kyc_status}:${event_object.id}` const lockResult = await LockService().lockIdempotencyKey(lockKey as any) if (lockResult instanceof Error) { - baseLogger.info({ customer_id, event }, "Duplicate Bridge KYC webhook") + baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") return res.status(200).json({ status: "already_processed" }) } try { const bridgeCustomerId = toBridgeCustomerId(customer_id) const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) - if (account instanceof Error) { - baseLogger.error({ customer_id }, "Account not found for Bridge customer") - return res.status(404).json({ error: "Account not found" }) + baseLogger.warn({ customer_id }, "Account not found for Bridge customer — may be a timing issue, Bridge will retry") + return res.status(503).json({ error: "Account not ready" }) } // Update KYC status based on event - if (event === "kyc.approved") { + if (kyc_status === "approved") { const result = await AccountsRepository().updateBridgeFields(account.id, { bridgeKycStatus: "approved", }) @@ -49,7 +50,20 @@ export const kycHandler = async (req: Request, res: Response) => { } baseLogger.info({ accountId: account.id, customer_id }, "Bridge KYC approved") - } else if (event === "kyc.rejected") { + + const vaResult = await BridgeService.createVirtualAccount(account.id) + if (vaResult instanceof Error) { + baseLogger.error( + { accountId: account.id, error: vaResult }, + "Failed to auto-create virtual account after KYC approval", + ) + } else { + baseLogger.info( + { accountId: account.id, virtualAccountId: vaResult.virtualAccountId }, + "Virtual account auto-created after KYC approval", + ) + } + } else if (kyc_status === "rejected") { const result = await AccountsRepository().updateBridgeFields(account.id, { bridgeKycStatus: "rejected", }) @@ -66,7 +80,7 @@ export const kycHandler = async (req: Request, res: Response) => { { accountId: account.id, customer_id, - reason, + rejection_reasons, }, "Bridge KYC rejected", ) diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 5fc2d723c..409e03570 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -223,15 +223,75 @@ const payToLnurl = async ( }).then(errorHandler) } +const getIbexToken = async (): Promise => { + const cached = await Ibex.authentication.storage.getAccessToken() + if (typeof cached === "string") return `Bearer ${cached}` + + // The SDK uses a single base URL for all calls, but the sandbox auth domain is separate + const resp = await fetch(`${IbexConfig.authUrl}/auth/signin`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), + }).catch((err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err)))) + + if (resp instanceof IbexError) return resp + if (!resp.ok) { + const body = await resp.text().catch(() => "") + return new IbexError(new Error(`IBEX sign-in failed: ${resp.status} — ${body}`)) + } + + const data = await resp.json() as { + accessToken?: string + accessTokenExpiresAt?: number + refreshToken?: string + refreshTokenExpiresAt?: number + } + if (!data.accessToken) return new IbexError(new Error("IBEX sign-in: no access token in response")) + + await Ibex.authentication.storage.setAccessToken(data.accessToken, data.accessTokenExpiresAt) + if (data.refreshToken) { + await Ibex.authentication.storage.setRefreshToken(data.refreshToken, data.refreshTokenExpiresAt) + } + + return `Bearer ${data.accessToken}` +} + +const ibexFetch = async ( + token: string, + path: string, + init: RequestInit = {}, +): Promise => { + const url = `${IbexConfig.url}${path}` + const resp = await fetch(url, { + ...init, + headers: { Authorization: token, "Content-Type": "application/json", ...init.headers }, + }) + if (!resp.ok) { + const body = await resp.text().catch(() => "") + baseLogger.error({ url, status: resp.status, body }, "IBEX request failed") + return new IbexError(new Error(`IBEX ${path} failed: ${resp.status} — ${body}`)) + } + return resp.json() as Promise +} + +const ibexGet = (token: string, path: string) => + ibexFetch(token, path, { method: "GET" }) + +const ibexPost = (token: string, path: string, body: unknown) => + ibexFetch(token, path, { method: "POST", body: JSON.stringify(body) }) + const getCryptoReceiveBalance = async ( receiveInfoId: string, ): Promise => { try { - const resp = await (Ibex as any).getCryptoReceiveBalance({ receiveInfoId }) - if (resp instanceof Error) return new IbexError(resp) - if (resp.balance === null || resp.balance === undefined) - return new UnexpectedIbexResponse("Balance not found") - const balance = USDTAmount.smallestUnits(resp.balance.toString()) + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexGet<{ balance: number }>( + token, + `/crypto/receive-infos/${receiveInfoId}/balance`, + ) + if (data instanceof IbexError) return data + const balance = USDTAmount.smallestUnits(data.balance?.toString()) if (balance instanceof Error) return new IbexError(balance) return balance } catch (err) { @@ -241,26 +301,34 @@ const getCryptoReceiveBalance = async ( const getCryptoReceiveOptions = async (): Promise => { try { - const resp = await (Ibex as any).getCryptoReceiveOptions() - if (resp instanceof Error) return new IbexError(resp) - return resp.options || [] + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexGet<{ options: CryptoReceiveOption[] }>( + token, + "/crypto/receive-infos/options", + ) + if (data instanceof IbexError) return data + return data.options || [] } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } } const createCryptoReceiveInfo = async ( - walletId: IbexAccountId, - optionId: string, + accountId: IbexAccountId, + option: Pick, ): Promise => { try { - const resp = await (Ibex as any).createCryptoReceiveInfo({ - wallet_id: walletId, - option_id: optionId, - } as CreateCryptoReceiveInfoRequest) - if (resp instanceof Error) return new IbexError(resp) - if (!resp.address) return new UnexpectedIbexResponse("Address not found") - return resp + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexPost( + token, + `/accounts/${accountId}/crypto/receive-infos`, + { name: option.name, network: option.network } as CreateCryptoReceiveInfoRequest, + ) + if (data instanceof IbexError) return data + if (!data.address) return new UnexpectedIbexResponse("Address not found") + return data } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } @@ -282,6 +350,22 @@ const getTronUsdtOption = async (): Promise => { return tronUsdt.id } +const getEthereumUsdtOption = async (): Promise => { + const options = await getCryptoReceiveOptions() + if (options instanceof IbexError) return options + + const ethereumUsdt = options.find( + (opt) => + opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "ethereum", + ) + + if (!ethereumUsdt) { + return new IbexError(new Error("Ethereum USDT option not found")) + } + + return ethereumUsdt +} + // const sendBetweenAccounts = async ( // sender: IbexAccount, // receiver: IbexAccount, @@ -323,5 +407,6 @@ export default wrapAsyncFunctionsToRunInSpan({ getCryptoReceiveOptions, createCryptoReceiveInfo, getTronUsdtOption, + getEthereumUsdtOption, }, }) diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index 145cfa1d7..4a02bf260 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -55,6 +55,6 @@ export interface CryptoReceiveInfo { } export interface CreateCryptoReceiveInfoRequest { - wallet_id: string - option_id: string + name: string + network: string } diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 7fe2283a5..914b12f7c 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -30,9 +30,9 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { const lockResult = await LockService().lockPaymentHash(tx_hash as any, async () => { try { - const account = await AccountsRepository().findByBridgeTronAddress(address) + const account = await AccountsRepository().findByBridgeEthereumAddress(address) if (account instanceof Error) { - baseLogger.error({ address, tx_hash }, "Account not found for Tron address") + baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") return { status: "error", code: "account_not_found" } as CryptoReceiveResult } diff --git a/src/services/lock/index.ts b/src/services/lock/index.ts index 9d7acea34..9b72e9d41 100644 --- a/src/services/lock/index.ts +++ b/src/services/lock/index.ts @@ -155,7 +155,12 @@ export const LockService = (): ILockService => { ): Promise => { const path = getIdempotencyKeyLockResource(idempotencyKey) - await timelock({ resource: path, duration: durationLockIdempotencyKey }) + try { + await timelock({ resource: path, duration: durationLockIdempotencyKey }) + } catch (error) { + if (error instanceof ExecutionError) return error + throw error + } } return wrapAsyncFunctionsToRunInSpan({ diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 7e658daa6..9a8842bcf 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -178,7 +178,7 @@ export const AccountsRepository = (): IAccountsRepository => { fields: { bridgeCustomerId?: BridgeCustomerId bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string }, ): Promise => { try { @@ -194,12 +194,12 @@ export const AccountsRepository = (): IAccountsRepository => { } } - const findByBridgeTronAddress = async ( + const findByBridgeEthereumAddress = async ( address: string, ): Promise => { try { - const result = await Account.findOne({ bridgeTronAddress: address }) - if (!result) return new RepositoryError("Account not found for Tron address") + const result = await Account.findOne({ bridgeEthereumAddress: address }) + if (!result) return new RepositoryError("Account not found for Ethereum address") return translateToAccount(result) } catch (error) { return parseRepositoryError(error) @@ -228,7 +228,7 @@ export const AccountsRepository = (): IAccountsRepository => { findByNpub, update, updateBridgeFields, - findByBridgeTronAddress, + findByBridgeEthereumAddress, findByBridgeCustomerId, } } @@ -293,5 +293,5 @@ const translateToAccount = (result: AccountRecord): Account => ({ displayCurrency: (result.displayCurrency || UsdDisplayCurrency) as DisplayCurrency, bridgeCustomerId: result.bridgeCustomerId as BridgeCustomerId | undefined, bridgeKycStatus: result.bridgeKycStatus, - bridgeTronAddress: result.bridgeTronAddress, + bridgeEthereumAddress: result.bridgeEthereumAddress, }) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index caeb7867b..6877d092e 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -13,10 +13,17 @@ export const createVirtualAccount = async (data: { bridgeVirtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string }) => { try { - const record = await BridgeVirtualAccount.create(data) + // Atomic upsert: if a doc for this accountId already exists (concurrent call won the + // race), $setOnInsert is skipped and we get back the winner's record — no duplicate. + const record = await BridgeVirtualAccount.findOneAndUpdate( + { accountId: data.accountId }, + { $setOnInsert: data }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) return record } catch (error) { return new RepositoryError(String(error)) @@ -89,7 +96,7 @@ export const updateExternalAccountStatus = async ( export const createWithdrawal = async (data: { accountId: string - bridgeTransferId: string + bridgeTransferId?: string amount: string currency: string externalAccountId: string @@ -103,6 +110,43 @@ export const createWithdrawal = async (data: { } } +export const findPendingWithdrawalWithoutTransfer = async ( + accountId: string, + externalAccountId: string, + amount: string, +) => { + try { + const record = await BridgeWithdrawal.findOne({ + accountId, + externalAccountId, + amount, + bridgeTransferId: { $exists: false }, + status: "pending", + }) + return record // null when no in-flight row exists + } catch (error) { + return new RepositoryError(String(error)) + } +} + +export const updateWithdrawalTransferId = async ( + id: string, + bridgeTransferId: string, + amount: string, + currency: string, +) => { + try { + const record = await BridgeWithdrawal.findByIdAndUpdate( + id, + { bridgeTransferId, amount, currency, updatedAt: new Date() }, + { new: true }, + ) + return record || new RepositoryError("Withdrawal not found") + } catch (error) { + return new RepositoryError(String(error)) + } +} + export const findWithdrawalsByAccountId = async (accountId: string) => { try { const records = await BridgeWithdrawal.find({ accountId }).sort({ createdAt: -1 }) diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index d13cccada..dd0b9aac9 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -23,6 +23,7 @@ interface IBridgeVirtualAccountRecord { bridgeVirtualAccountId: string bankName: string routingNumber: string + accountNumber: string accountNumberLast4: string createdAt: Date } @@ -38,7 +39,7 @@ interface IBridgeExternalAccountRecord { interface IBridgeWithdrawalRecord { accountId: string - bridgeTransferId: string + bridgeTransferId?: string amount: string currency: string status: "pending" | "completed" | "failed" @@ -335,7 +336,7 @@ const AccountSchema = new Schema( enum: ["pending", "approved", "rejected"], required: false, }, - bridgeTronAddress: { + bridgeEthereumAddress: { type: String, required: false, }, @@ -348,7 +349,7 @@ AccountSchema.index({ coordinates: 1, }) -AccountSchema.index({ bridgeTronAddress: 1 }, { sparse: true }) +AccountSchema.index({ bridgeEthereumAddress: 1 }, { sparse: true }) export const Account = mongoose.model("Account", AccountSchema) @@ -614,10 +615,12 @@ export const WalletOnChainPendingReceive = ) const BridgeVirtualAccountSchema = new Schema({ - accountId: { type: String, required: true, index: true }, + // unique: true enforces one VA per account at the DB layer — idempotency guard + accountId: { type: String, required: true, unique: true }, bridgeVirtualAccountId: { type: String, required: true, unique: true }, bankName: { type: String, required: true }, routingNumber: { type: String, required: true }, + accountNumber: { type: String, required: true }, accountNumberLast4: { type: String, required: true }, createdAt: { type: Date, default: Date.now }, }) @@ -631,9 +634,17 @@ const BridgeExternalAccountSchema = new Schema({ createdAt: { type: Date, default: Date.now }, }) +// CRIT-2 (ENG-281): Compound index enforces that a given bridgeExternalAccountId +// can only be associated with one accountId at the DB layer, preventing cross-account +// withdrawal attacks even if application-layer ownership checks are bypassed. +BridgeExternalAccountSchema.index( + { accountId: 1, bridgeExternalAccountId: 1 }, + { unique: true }, +) + const BridgeWithdrawalSchema = new Schema({ accountId: { type: String, required: true, index: true }, - bridgeTransferId: { type: String, required: true, unique: true }, + bridgeTransferId: { type: String, unique: true, sparse: true }, amount: { type: String, required: true }, currency: { type: String, required: true }, status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 57016ec0a..51c86757c 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -97,7 +97,7 @@ interface AccountRecord { // Bridge integration: bridgeCustomerId?: string bridgeKycStatus?: "pending" | "approved" | "rejected" - bridgeTronAddress?: string + bridgeEthereumAddress?: string // mongoose in-built functions save: () => Promise diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts new file mode 100644 index 000000000..20aa5f9ad --- /dev/null +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -0,0 +1,260 @@ +import crypto from "crypto" + +jest.mock("@services/tracing", () => ({ + wrapAsyncFunctionsToRunInSpan: ({ + fns, + }: { + namespace: string + fns: Record unknown> + }) => fns, +})) + +jest.mock("@config", () => ({ + BridgeConfig: { enabled: true, minWithdrawalAmount: 10 }, +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-accounts", () => ({ + createWithdrawal: jest.fn(), + findPendingWithdrawalWithoutTransfer: jest.fn(), + findExternalAccountsByAccountId: jest.fn(), + updateWithdrawalTransferId: jest.fn(), +})) + +jest.mock("@services/bridge/client", () => ({ + __esModule: true, + default: { createTransfer: jest.fn() }, +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/wallets", () => ({ + WalletsRepository: jest.fn(), +})) + +jest.mock("@app/wallets/get-balance-for-wallet", () => ({ + getBalanceForWallet: jest.fn(), +})) + +jest.mock("@services/kratos", () => ({ + IdentityRepository: jest.fn(), +})) + +jest.mock("@domain/primitives/bridge", () => ({ + toBridgeCustomerId: (id: string) => id, + toBridgeExternalAccountId: (id: string) => id, +})) + +// USDTAmount is not re-exported from @domain/shared — provide a minimal stand-in so the +// service's `instanceof USDTAmount` guard is satisfied during tests. +// The class is defined inside the factory because jest.mock factories are hoisted before +// variable declarations; access it at runtime via require("@domain/shared").USDTAmount. +// USDTAmount is not re-exported from @domain/shared/index.ts (pre-existing issue). +// Spread the real module and inject a minimal stand-in so the service's +// `instanceof USDTAmount` guard is satisfied without breaking other domain exports. +jest.mock("@domain/shared", () => { + class USDTAmount { + constructor(private readonly ibexValue: number) {} + toIbex() { + return this.ibexValue + } + } + return { ...jest.requireActual("@domain/shared"), USDTAmount } +}) + +import BridgeService, { deriveWithdrawalIdempotencyKey } from "@services/bridge" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import BridgeClient from "@services/bridge/client" +import { AccountsRepository } from "@services/mongoose/accounts" +import { WalletsRepository } from "@services/mongoose/wallets" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const ACCOUNT_ID = "account-001" as AccountId +const EXTERNAL_ACCOUNT_ID = "ext-account-001" +const AMOUNT = "50" +const CUSTOMER_ID = "cust-001" +const ETHEREUM_ADDRESS = "ETH_ADDR_001" +const TRANSFER_ID = "transfer-bridge-001" + +const mockAccount = { + id: ACCOUNT_ID, + level: 2, + bridgeCustomerId: CUSTOMER_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + bridgeKycStatus: "approved", + kratosUserId: "kratos-001", +} + +const makeRow = (id: string) => ({ + id, + accountId: ACCOUNT_ID as string, + amount: AMOUNT, + currency: "usdt", + externalAccountId: EXTERNAL_ACCOUNT_ID, + status: "pending" as const, + bridgeTransferId: undefined, +}) + +const mockTransfer = { + id: TRANSFER_ID, + amount: AMOUNT, + currency: "usd", + state: "pending", +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const setupGuards = () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const { USDTAmount } = require("@domain/shared") + const balance = new USDTAmount(1000) // 1000 USDT — well above minWithdrawalAmount + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn().mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), + }) + ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) + ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ + { bridgeExternalAccountId: EXTERNAL_ACCOUNT_ID, status: "verified" }, + ]) + ;(BridgeAccountsRepo.updateWithdrawalTransferId as jest.Mock).mockResolvedValue({ + ...makeRow("any"), + bridgeTransferId: TRANSFER_ID, + }) + ;(BridgeClient.createTransfer as jest.Mock).mockResolvedValue(mockTransfer) +} + +// ── Tests ───────────────────────────────────────────────────────────────────── + +describe("deriveWithdrawalIdempotencyKey", () => { + it("returns sha256(\"withdrawal:\") as a hex string", () => { + const rowId = "507f1f77bcf86cd799439011" + const expected = crypto + .createHash("sha256") + .update(`withdrawal:${rowId}`) + .digest("hex") + + expect(deriveWithdrawalIdempotencyKey(rowId)).toBe(expected) + }) + + it("produces distinct keys for distinct row IDs", () => { + expect(deriveWithdrawalIdempotencyKey("id-alpha")).not.toBe( + deriveWithdrawalIdempotencyKey("id-beta"), + ) + }) + + it("is deterministic — same input always returns same output", () => { + const rowId = "507f1f77bcf86cd799439011" + expect(deriveWithdrawalIdempotencyKey(rowId)).toBe(deriveWithdrawalIdempotencyKey(rowId)) + }) + + it("output is a 64-character lowercase hex string (sha256)", () => { + const key = deriveWithdrawalIdempotencyKey("any-id") + expect(key).toMatch(/^[0-9a-f]{64}$/) + }) +}) + +describe("initiateWithdrawal — idempotency key wiring", () => { + beforeEach(() => { + jest.clearAllMocks() + setupGuards() + }) + + describe("fresh request (no in-flight row)", () => { + it("creates a pending withdrawal row before calling Bridge", async () => { + const row = makeRow("fresh-row-001") + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + null, + ) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const createOrder = (BridgeAccountsRepo.createWithdrawal as jest.Mock).mock + .invocationCallOrder[0] + const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock.invocationCallOrder[0] + expect(createOrder).toBeLessThan(transferOrder) + }) + + it("derives the idempotency key from the newly created row's id", async () => { + const rowId = "fresh-row-id-abc" + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + null, + ) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(makeRow(rowId)) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const expectedKey = deriveWithdrawalIdempotencyKey(rowId) + expect(BridgeClient.createTransfer).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.any(Object), + expectedKey, + ) + }) + }) + + describe("retry — in-flight row already exists", () => { + it("does not create a second withdrawal row", async () => { + const existingRow = makeRow("existing-row-001") + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + existingRow, + ) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + expect(BridgeAccountsRepo.createWithdrawal).not.toHaveBeenCalled() + }) + + it("derives the key from the existing row's id — identical to the first attempt's key", async () => { + const rowId = "existing-row-001" + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( + makeRow(rowId), + ) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const expectedKey = deriveWithdrawalIdempotencyKey(rowId) + expect(BridgeClient.createTransfer).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.any(Object), + expectedKey, + ) + }) + }) + + describe("two rapid calls for the same request", () => { + it("pass the same idempotency key to Bridge — collapsing into one transfer", async () => { + // Call 1: no in-flight row → creates row A + // Call 2: finds row A (created by call 1) → reuses its id + const rowId = "shared-row-concurrent" + const row = makeRow(rowId) + + ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock) + .mockResolvedValueOnce(null) // call 1: nothing in-flight yet + .mockResolvedValueOnce(row) // call 2: row A now visible + + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) + + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) + + const calls = (BridgeClient.createTransfer as jest.Mock).mock.calls + expect(calls).toHaveLength(2) + + const key1 = calls[0][2] + const key2 = calls[1][2] + expect(key1).toBe(key2) + expect(key1).toBe(deriveWithdrawalIdempotencyKey(rowId)) + }) + }) +}) From c0ccdccba636713738212cee0b8e3d679a0df370 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:22 -0400 Subject: [PATCH 03/22] ENG-276 feat(bridge): add reconciliation and replay tooling --- .../admin/account-update-level.bru | 2 +- .../Flash GraphQL API/environments/local.bru | 4 +- dev/config/base-config.yaml | 1 + .../ENG-276-IMPLEMENTATION-RESUME.md | 308 ++++++++++++++++++ package.json | 6 +- src/config/schema.ts | 1 + src/config/schema.types.d.ts | 1 + src/scripts/reconcile-bridge-ibex-deposits.ts | 34 ++ src/scripts/replay-bridge-events.ts | 195 +++++++++++ src/servers/cron.ts | 11 +- src/services/bridge/client.ts | 104 +++++- src/services/bridge/reconciliation.ts | 155 +++++++++ src/services/bridge/webhook-server/index.ts | 6 + .../bridge/webhook-server/routes/deposit.ts | 66 ++-- .../bridge/webhook-server/routes/replay.ts | 239 ++++++++++++++ .../webhook-server/routes/crypto-receive.ts | 107 +++--- src/services/mongoose/bridge-deposit-log.ts | 22 ++ .../mongoose/bridge-reconciliation-orphan.ts | 27 ++ src/services/mongoose/bridge-replay-log.ts | 42 +++ .../mongoose/ibex-crypto-receive-log.ts | 51 +++ src/services/mongoose/schema.ts | 90 ++++- .../flash/unit/services/bridge/client.spec.ts | 152 +++++++++ .../bridge/webhook-server/deposit.spec.ts | 225 +++++++++++++ .../bridge/webhook-server/replay.spec.ts | 296 +++++++++++++++++ tsconfig-build.json | 8 +- 25 files changed, 2077 insertions(+), 76 deletions(-) create mode 100644 docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md create mode 100644 src/scripts/reconcile-bridge-ibex-deposits.ts create mode 100644 src/scripts/replay-bridge-events.ts create mode 100644 src/services/bridge/reconciliation.ts create mode 100644 src/services/bridge/webhook-server/routes/replay.ts create mode 100644 src/services/mongoose/bridge-deposit-log.ts create mode 100644 src/services/mongoose/bridge-reconciliation-orphan.ts create mode 100644 src/services/mongoose/bridge-replay-log.ts create mode 100644 src/services/mongoose/ibex-crypto-receive-log.ts create mode 100644 test/flash/unit/services/bridge/client.spec.ts create mode 100644 test/flash/unit/services/bridge/webhook-server/deposit.spec.ts create mode 100644 test/flash/unit/services/bridge/webhook-server/replay.spec.ts diff --git a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru index d9fb2ed1e..244ae7d0f 100644 --- a/dev/bruno/Flash GraphQL API/admin/account-update-level.bru +++ b/dev/bruno/Flash GraphQL API/admin/account-update-level.bru @@ -32,7 +32,7 @@ body:graphql { body:graphql:vars { { "input": { - "uid": "69f636d77a39a2a9a1807aa9", + "uid": "69fb995b012e5336d69a0f1f", "level": "THREE", "erpParty": "John Doe" } diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 311dcfcbf..2c8d6c6bd 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -8,6 +8,6 @@ vars { token: walletId: walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 - userEmail: maurientesol@gmail.com - userFullName: maurientesol + userEmail: maurienteso@gmail.com + userFullName: maurienteso } diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index 0b1fe24fb..3cbc37bee 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -21,6 +21,7 @@ bridge: minWithdrawalAmount: 10 webhook: port: 4009 + replaySecret: "also-not-so-secret" publicKeys: kyc: | -----BEGIN PUBLIC KEY----- diff --git a/docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md b/docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md new file mode 100644 index 000000000..52c0ef316 --- /dev/null +++ b/docs/bridge-integration/ENG-276-IMPLEMENTATION-RESUME.md @@ -0,0 +1,308 @@ +# ENG-276 Implementation Resume + +## Context + +`ENG-276` targets operational reliability for Bridge on-ramp deposits by closing the gap between: + +- Bridge transfer/deposit lifecycle events +- IBEX crypto receive settlement events +- Operator tooling needed to replay and triage failures + +The issue also folds in two related reliability needs: + +- Persist Bridge fee on every deposit log row +- Provide replay capability for stuck webhook handlers + +In practice, Flash receives information from two systems that represent different parts of the same real-world flow: + +1. Bridge emits webhook events for transfer state transitions. +2. IBEX emits crypto receive webhooks when USDT settlement is observed. + +Without reconciliation and replay tooling, operations cannot quickly identify: + +- Bridge events that never materialized in IBEX +- IBEX settlements with no corresponding Bridge event +- Stuck handlers needing replay + +--- + +## What We Are Solving + +### Primary objective (ENG-276 acceptance scope) + +1. Orphan events must be surfaced with triage context (ops visibility). +2. Replay CLI must rerun stuck handlers for a chosen transfer id. +3. Bridge fee value must be persisted on every deposit row. + +### Supporting objective + +Ensure replay handling matches Bridge webhook envelope format documented by Bridge: + +- `event_type` can be generic (`updated.status_transitioned`) +- `event_object_status` carries status transitions like `funds_received` +- `event_object` carries the resource payload +- `event_created_at` carries event timestamp + +Reference: +- [Bridge webhook event structure](https://apidocs.bridge.xyz/platform/additional-information/webhooks/structure) +- [Bridge list webhook events](https://apidocs.bridge.xyz/api-reference/webhooks/list-webhook-events) + +--- + +## How It Was Solved + +## 1) AC3: Fee persisted on every deposit row + +### Changes + +- Deposit handler now always computes `developerFee` with fallback chain: + 1. `receipt.developer_fee` + 2. `event_object.developer_fee` + 3. `"0"` +- `developerFee` made required/defaulted in persistence path. + +### Result + +Every `BridgeDepositLog` row has a fee value, including null/missing upstream fee cases. + +--- + +## 2) AC1: Orphan detection and ops surfacing + +### Changes + +- Added IBEX receive log persistence. +- Added reconciliation service comparing 24h Bridge deposits vs IBEX receives. +- Added orphan persistence model with rich triage context. +- Added reconciliation execution in cron and manual script entrypoint. + +### Result + +Ops can now see explicit orphan records with: + +- orphan type (`bridge_without_ibex` / `ibex_without_bridge`) +- correlation keys (transfer id / tx hash) +- detection window +- reason and context required for triage + +--- + +## 3) AC2: Replay by chosen transfer id + +### Changes + +- Replay CLI supports `--transfer-id`. +- Replay filtering extracts transfer id from supported object shapes. +- Replay route accepts canonical Bridge envelope and maps it to existing handlers. + +### Result + +Ops can target replay for a specific transfer id rather than replaying all events in a window. + +--- + +## 4) Bridge documentation alignment fixes + +### Event list API fix + +Bridge webhook events listing uses: + +- `GET /webhook_events` +- query params like `starting_after`, `limit`, `category` + +Client was updated to use this endpoint and parameter mapping while preserving internal caller interface. + +### GET idempotency fix + +Bridge rejects `Idempotency-Key` on certain GET endpoints (including webhook events list). +Client request logic now sends `Idempotency-Key` only on non-GET methods. + +--- + +## Reconciliation Sequence Diagram (Step-by-Step) + +```mermaid +sequenceDiagram + autonumber + participant Cron as Cron Job / Manual Script + participant Rec as reconcileBridgeAndIbexDeposits + participant BDL as BridgeDepositLog (Mongo) + participant IBL as IbexCryptoReceiveLog (Mongo) + participant ORP as BridgeReconciliationOrphan (Mongo) + participant Ops as Ops Tooling / Operator + + Cron->>Rec: Start reconciliation(window=24h) + Rec->>BDL: Query Bridge deposits in window (state=funds_received) + BDL-->>Rec: Bridge deposit set + Rec->>IBL: Query IBEX receives in same window + IBL-->>Rec: IBEX receive set + + Note over Rec: Build hash maps by tx hash for fast matching + + loop For each Bridge deposit + alt Missing destinationTxHash + Rec->>ORP: Upsert orphan (bridge_without_ibex)
reason=no destinationTxHash + else destinationTxHash not found in IBEX set + Rec->>ORP: Upsert orphan (bridge_without_ibex)
reason=no IBEX match in window + else Matched + Rec-->>Rec: No orphan + end + end + + loop For each IBEX receive + alt tx hash not found in Bridge set + Rec->>ORP: Upsert orphan (ibex_without_bridge)
reason=no Bridge match in window + else Matched + Rec-->>Rec: No orphan + end + end + + Rec-->>Cron: Return summary counts + Cron-->>Ops: Log reconciliation summary + Ops->>ORP: Inspect orphans with triage context +``` + +--- + +## Manual Validation Summary + +## Curl Samples (Manual Console Tests) + +Set base variables: + +```bash +BASE_URL="http://localhost:4009" +REPLAY_SECRET="also-not-so-secret" +``` + +AC3 Case A (receipt fee present; expected persisted `developerFee = "0.5"`): + +```bash +curl -s -X POST "$BASE_URL/internal/replay" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $REPLAY_SECRET" \ + -d '{ + "event_id":"wh_ac3_a_101", + "event_type":"updated.status_transitioned", + "event_object_status":"funds_received", + "event_object":{ + "id":"tr_ac3_a_101", + "state":"funds_received", + "amount":"10.00", + "currency":"usd", + "developer_fee":"0.2", + "on_behalf_of":"cust_a", + "receipt":{"developer_fee":"0.5","initial_amount":"10.00","subtotal_amount":"9.50","final_amount":"9.50","destination_tx_hash":"tx_ac3_a_101"} + }, + "event_created_at":"2026-05-06T10:00:00.000Z", + "operator":"manual-test", + "time_window_start":"2026-05-06T00:00:00.000Z", + "time_window_end":"2026-05-06T23:59:59.000Z", + "dry_run":false + }' +``` + +AC3 Case B (receipt fee null; fallback to `event_object.developer_fee`, expected `"0.7"`): + +```bash +curl -s -X POST "$BASE_URL/internal/replay" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $REPLAY_SECRET" \ + -d '{ + "event_id":"wh_ac3_b_101", + "event_type":"updated.status_transitioned", + "event_object_status":"funds_received", + "event_object":{ + "id":"tr_ac3_b_101", + "state":"funds_received", + "amount":"20.00", + "currency":"usd", + "developer_fee":"0.7", + "on_behalf_of":"cust_b", + "receipt":{"developer_fee":null,"initial_amount":"20.00","subtotal_amount":"19.30","final_amount":"19.30","destination_tx_hash":"tx_ac3_b_101"} + }, + "event_created_at":"2026-05-06T10:05:00.000Z", + "operator":"manual-test", + "time_window_start":"2026-05-06T00:00:00.000Z", + "time_window_end":"2026-05-06T23:59:59.000Z", + "dry_run":false + }' +``` + +AC3 Case C (both missing/null; expected default `"0"`): + +```bash +curl -s -X POST "$BASE_URL/internal/replay" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $REPLAY_SECRET" \ + -d '{ + "event_id":"wh_ac3_c_101", + "event_type":"updated.status_transitioned", + "event_object_status":"funds_received", + "event_object":{ + "id":"tr_ac3_c_101", + "state":"funds_received", + "amount":"30.00", + "currency":"usd", + "on_behalf_of":"cust_c", + "receipt":{"developer_fee":null,"initial_amount":"30.00","subtotal_amount":"30.00","final_amount":"30.00","destination_tx_hash":"tx_ac3_c_101"} + }, + "event_created_at":"2026-05-06T10:10:00.000Z", + "operator":"manual-test", + "time_window_start":"2026-05-06T00:00:00.000Z", + "time_window_end":"2026-05-06T23:59:59.000Z", + "dry_run":false + }' +``` + +AC2 replay command (transfer-id targeted replay): + +```bash +BRIDGE_WEBHOOK_REPLAY_SECRET="$REPLAY_SECRET" BRIDGE_WEBHOOK_URL="$BASE_URL" \ +yarn replay-bridge-events --configPath dev/config/base-config.yaml \ + --start 2026-05-01T00:00:00Z --end 2026-05-07T00:00:00Z \ + --event-type transfer --transfer-id tr_ac3_b_101 +``` + +AC1 reconciliation command: + +```bash +. ./.env && yarn reconcile-bridge-ibex-deposits --configPath dev/config/base-config.yaml --window-hours 24 +``` + +DB verification query (without `mongosh`, using node + mongoose): + +```bash +. ./.env && node -e "const mongoose=require('mongoose'); (async()=>{await mongoose.connect(process.env.MONGODB_CON); const db=mongoose.connection.db; const deposit=await db.collection('bridgedepositlogs').find({transferId:{\$in:['tr_ac3_a_101','tr_ac3_b_101','tr_ac3_c_101']}},{projection:{_id:0,transferId:1,developerFee:1,destinationTxHash:1,createdAt:1}}).sort({createdAt:-1}).toArray(); const orphans=await db.collection('bridgereconciliationorphans').find({txHash:{\$in:['tx_ac3_a_101','tx_ac3_b_101','tx_ac3_c_101']}},{projection:{_id:0,orphanType:1,orphanKey:1,transferId:1,txHash:1,detectedAt:1}}).sort({detectedAt:-1}).toArray(); console.log(JSON.stringify({deposit, orphans}, null, 2)); await mongoose.disconnect(); })().catch(async(e)=>{console.error(e); try{await mongoose.disconnect();}catch{} process.exit(1);});" +``` + +### AC3 checks + +Three manual replay cases confirmed persisted values: + +- Case A: receipt fee present -> persisted that receipt fee +- Case B: receipt fee null -> persisted fallback `event_object.developer_fee` +- Case C: both missing/null -> persisted `"0"` + +### AC1 checks + +Reconciliation run produced orphan rows and summary counts, confirming surfacing path. + +### AC2 checks + +Replay command path works with transfer-id filter; endpoint-level issues were resolved by: + +- switching to documented webhook events listing endpoint +- removing idempotency header on GET + +--- + +## Final State + +`ENG-276` goals are implemented with: + +- deterministic fee persistence +- reconciliation + orphan triage surface +- replay targeting for specific transfers +- Bridge API/documentation-aligned event retrieval and envelope handling + diff --git a/package.json b/package.json index da883eb60..216293363 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "start-main": ". ./.env && yarn run build && node --inspect -r ./lib/services/tracing.js ./lib/servers/graphql-main-server.js", "migrate-mongo-up": "migrate-mongo up -f './src/migrations/migrate-mongo-config.js'", "gen-test-jwt": "ts-node ./dev/bin/gen-test-jwt.ts", - "bridge-webhook": ". ./.env && ts-node --transpile-only -r tsconfig-paths/register src/servers/bridge-webhook-server.ts --configPath dev/config/base-config.yaml" + "bridge-webhook": ". ./.env && ts-node --transpile-only -r tsconfig-paths/register src/servers/bridge-webhook-server.ts --configPath dev/config/base-config.yaml", + "replay-bridge-events": "yarn build && node lib/scripts/replay-bridge-events.js", + "reconcile-bridge-ibex-deposits": "yarn build && node lib/scripts/reconcile-bridge-ibex-deposits.js" }, "engines": { "node": ">=20.18.1 <21" @@ -210,4 +212,4 @@ "**/**/mongoose": "~7.5.1" }, "private": true -} +} \ No newline at end of file diff --git a/src/config/schema.ts b/src/config/schema.ts index 2ed56f48d..c6f751f28 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -663,6 +663,7 @@ export const configSchema = { required: ["kyc", "deposit", "transfer"], }, timestampSkewMs: { type: "integer" }, + replaySecret: { type: "string" } }, required: ["port", "publicKeys", "timestampSkewMs"], }, diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index 36e6df9f5..4f480b86f 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -41,6 +41,7 @@ type BridgeWebhook = { port: number publicKeys: BridgeWebhookPublicKeys timestampSkewMs: number + replaySecret?: string } type BridgeConfig = { diff --git a/src/scripts/reconcile-bridge-ibex-deposits.ts b/src/scripts/reconcile-bridge-ibex-deposits.ts new file mode 100644 index 000000000..7e643bc0f --- /dev/null +++ b/src/scripts/reconcile-bridge-ibex-deposits.ts @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +import yargs from "yargs" +import { hideBin } from "yargs/helpers" +import { setupMongoConnection } from "@services/mongodb" +import { baseLogger } from "@services/logger" +import { reconcileBridgeAndIbexDeposits } from "@services/bridge/reconciliation" + +const args = yargs(hideBin(process.argv)) + .option("window-hours", { + type: "number", + default: 24, + describe: "Reconciliation window in hours", + }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const main = async () => { + const windowMs = Math.max(1, Math.floor(args["window-hours"])) * 60 * 60 * 1000 + const result = await reconcileBridgeAndIbexDeposits({ windowMs }) + if (result instanceof Error) throw result + baseLogger.info(result, "Bridge↔IBEX reconciliation finished") +} + +setupMongoConnection() + .then(async (mongoose) => { + await main() + await mongoose?.connection.close() + process.exit(0) + }) + .catch((error) => { + baseLogger.error({ error }, "Bridge↔IBEX reconciliation failed") + process.exit(1) + }) diff --git a/src/scripts/replay-bridge-events.ts b/src/scripts/replay-bridge-events.ts new file mode 100644 index 000000000..915e46bb0 --- /dev/null +++ b/src/scripts/replay-bridge-events.ts @@ -0,0 +1,195 @@ +#!/usr/bin/env node + +/** + * Operator tool: replay missed Bridge webhook events. + * + * Usage: + * BRIDGE_WEBHOOK_REPLAY_SECRET= BRIDGE_WEBHOOK_URL=http://localhost:4009 \ + * node lib/scripts/replay-bridge-events.js \ + * --configPath dev/config/base-config.yaml \ + * --start 2026-05-01T00:00:00Z \ + * --end 2026-05-02T00:00:00Z \ + * [--event-type kyc|deposit|transfer] \ + * [--dry-run] \ + * [--operator "ops@example.com"] + */ + +import { listAllEvents } from "@services/bridge/client" +import { baseLogger } from "@services/logger" +import { setupMongoConnection } from "@services/mongodb" +import yargs from "yargs" +import { hideBin } from "yargs/helpers" + +const args = yargs(hideBin(process.argv)) + .option("start", { type: "string", demandOption: true }) + .option("end", { type: "string", demandOption: true }) + .option("event-type", { type: "string", choices: ["kyc", "deposit", "transfer"] }) + .option("transfer-id", { + type: "string", + describe: "Replay only events for this transfer ID", + }) + .option("dry-run", { type: "boolean", default: false }) + .option("operator", { type: "string", default: "unknown" }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const REPLAY_SECRET = process.env.BRIDGE_WEBHOOK_REPLAY_SECRET +const WEBHOOK_URL = process.env.BRIDGE_WEBHOOK_URL ?? "http://localhost:4009" + +if (!REPLAY_SECRET) { + console.error("Error: BRIDGE_WEBHOOK_REPLAY_SECRET environment variable is required") + process.exit(1) +} + +const EVENT_TYPE_FILTER: Record = { + kyc: "kyc.approved", + deposit: "deposit.completed", + transfer: "transfer.completed", +} + +type BridgeReplayEventEnvelope = { + event_id: string + event_type: string + event_object: unknown + event_created_at: string +} + +const toRouteKey = (eventType: string): string | null => { + if (eventType.startsWith("kyc")) return "kyc" + if (eventType.startsWith("deposit")) return "deposit" + if (eventType.startsWith("transfer")) return "transfer" + return null +} + +const extractTransferId = (payload: unknown): string | undefined => { + if (!payload || typeof payload !== "object") return undefined + const candidate = payload as Record + const fromTransferId = candidate.transfer_id + const fromId = candidate.id + const orchestration = candidate.orchestration as Record | undefined + const fromOrchestrationTransferId = orchestration?.transfer_id + + if (typeof fromTransferId === "string") return fromTransferId + if (typeof fromId === "string") return fromId + if (typeof fromOrchestrationTransferId === "string") return fromOrchestrationTransferId + return undefined +} + +const replayEvent = async ( + event: BridgeReplayEventEnvelope, +): Promise<{ status: number; body: unknown }> => { + const routeKey = toRouteKey(event.event_type) + + if (!routeKey) { + baseLogger.warn( + { eventType: event.event_type }, + "Skipping unsupported event type for replay", + ) + return { status: 0, body: { skipped: true, reason: "unsupported event type" } } + } + + const response = await fetch(`${WEBHOOK_URL}/internal/replay`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${REPLAY_SECRET}`, + }, + body: JSON.stringify({ + event_type: event.event_type, + event_object: event.event_object, + event_created_at: event.event_created_at, + operator: args.operator, + time_window_start: args.start, + time_window_end: args.end, + dry_run: args["dry-run"], + }), + }) + + const body = await response.json().catch(() => null) + + return { status: response.status, body } +} + +const main = async () => { + let fetched = 0, + success = 0, + failed = 0, + skipped = 0 + baseLogger.info( + { + start: args.start, + end: args.end, + eventType: args["event-type"], + dryRun: args["dry-run"], + operator: args.operator, + }, + "Starting Bridge webhook replay", + ) + + const bridgeFilter = args["event-type"] + ? EVENT_TYPE_FILTER[args["event-type"] as string] + : undefined + + for await (const event of listAllEvents({ + start_date: args.start, + end_date: args.end, + event_type: bridgeFilter, + })) { + const replayEventObject: BridgeReplayEventEnvelope = { + event_id: event.id, + event_type: event.event_type, + event_created_at: event.created_at, + event_object: event.payload, + } + + if (args["transfer-id"]) { + const transferId = extractTransferId(replayEventObject.event_object) + if (transferId !== args["transfer-id"]) { + skipped++ + continue + } + } + + fetched++ + + const { status, body } = await replayEvent(replayEventObject) + + if (status >= 200 && status < 300) { + success++ + baseLogger.info( + { eventId: event.id, eventType: event.event_type, status, response: body }, + "Successfully replayed event", + ) + } else if (status === 0) { + skipped++ + baseLogger.info( + { eventId: event.id, eventType: event.event_type, status, response: body }, + "Skipped event", + ) + } else { + failed++ + baseLogger.error( + { eventId: event.id, eventType: event.event_type, status, response: body }, + "Failed to replay event", + ) + } + + await new Promise((resolve) => setTimeout(resolve, 100)) // small delay to avoid overwhelming the webhook server + } + + baseLogger.info( + { fetched, success, failed, skipped }, + "Completed Bridge webhook replay", + ) +} + +setupMongoConnection() + .then(async (mongoose) => { + await main() + await mongoose?.connection.close() + process.exit(0) + }) + .catch((error) => { + baseLogger.error({ error }, "Error in Bridge webhook replay") + process.exit(1) + }) diff --git a/src/servers/cron.ts b/src/servers/cron.ts index e139fd212..19511890e 100644 --- a/src/servers/cron.ts +++ b/src/servers/cron.ts @@ -1,6 +1,6 @@ import { OnChain, Lightning, Wallets, Payments, Swap } from "@app" -import { getCronConfig, TWO_MONTHS_IN_MS } from "@config" +import { BridgeConfig, getCronConfig, TWO_MONTHS_IN_MS } from "@config" import { ErrorLevel } from "@domain/shared" import { OperationInterruptedError } from "@domain/errors" @@ -20,6 +20,7 @@ import { import { baseLogger } from "@services/logger" import { setupMongoConnection } from "@services/mongodb" import { activateLndHealthCheck, checkAllLndHealth } from "@services/lnd/health" +import { reconcileBridgeAndIbexDeposits } from "@services/bridge/reconciliation" import { elapsedSinceTimestamp, sleep } from "@utils" import { rebalancingInternalChannels } from "@services/lnd/rebalancing" @@ -64,6 +65,13 @@ const swapOutJob = async () => { if (swapResult instanceof Error) throw swapResult } +const reconcileBridgeDepositsJob = async () => { + if (!BridgeConfig.enabled) return + + const result = await reconcileBridgeAndIbexDeposits() + if (result instanceof Error) throw result +} + const main = async () => { console.log("cronjob started") const start = new Date() @@ -84,6 +92,7 @@ const main = async () => { updateLegacyOnChainReceipt, ...(cronConfig.rebalanceEnabled ? [rebalance] : []), ...(cronConfig.swapEnabled ? [swapOutJob] : []), + reconcileBridgeDepositsJob, deleteExpiredPaymentFlows, deleteExpiredInvoices, deleteLndPaymentsBefore2Months, diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index 6052cc983..51f4ec882 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -259,6 +259,39 @@ export interface BridgeIntiateKyc { full_name?: string } +export type BridgeWebhookEventType = "kyc" | "transfer" | "virtual_account" | "external_account" + +export interface BridgeWebhookEvent { + id: string + event_type: string + payload: unknown + created_at: string +} + +export interface ListEventsParams { + start_date?: string + end_date?: string + event_type?: string + after?: string + page_size?: number +} + +type WebhookEventsApiResponse = { + data: Array<{ + event_id?: string + event_type?: string + event_created_at?: string + event_object?: unknown + id?: string + created_at?: string + payload?: unknown + }> + count?: number + has_more?: boolean + cursor?: string +} + + // ============ Bridge Client ============ export class BridgeClient { @@ -282,10 +315,13 @@ export class BridgeClient { "Content-Type": "application/json", } - if (idempotencyKey) { - headers["Idempotency-Key"] = idempotencyKey - } else { - headers["Idempotency-Key"] = crypto.randomUUID() + // Bridge rejects Idempotency-Key on some GET endpoints (e.g. /webhook_events). + if (method.toUpperCase() !== "GET") { + if (idempotencyKey) { + headers["Idempotency-Key"] = idempotencyKey + } else { + headers["Idempotency-Key"] = crypto.randomUUID() + } } @@ -400,6 +436,66 @@ export class BridgeClient { // Note: Bridge API uses /transfers/{id} not /customers/{id}/transfers/{id} return this.request("GET", `/transfers/${transferId}`) } + + + // ============ List Events ============ + + async listEvents(params?: ListEventsParams): Promise> { + const queryParams = new URLSearchParams() + + // Bridge webhook events endpoint uses cursor pagination via starting_after. + if (params?.after) queryParams.append("starting_after", params.after) + if (params?.page_size) queryParams.append("limit", params.page_size.toString()) + // Preserve call-site compatibility: derive category from event_type when possible. + if (params?.event_type) { + const category = params.event_type.split(".")[0] + if (category) queryParams.append("category", category) + } + + const suffix = queryParams.toString() ? `?${queryParams.toString()}` : "" + + const response = await this.request( + "GET", + `/webhook_events${suffix}`, + ) + + const mappedData: BridgeWebhookEvent[] = (response.data ?? []).map((event) => ({ + id: event.event_id ?? event.id ?? "", + event_type: event.event_type ?? "", + created_at: event.event_created_at ?? event.created_at ?? "", + payload: event.event_object ?? event.payload ?? {}, + })) + + // Keep legacy ListResponse shape for existing callers. + const limit = params?.page_size ?? 100 + const hasMoreFromCount = typeof response.count === "number" && response.count >= limit + const hasMore = response.has_more ?? hasMoreFromCount + const cursor = response.cursor ?? mappedData[mappedData.length - 1]?.id + + return { + data: mappedData, + has_more: Boolean(hasMore && cursor), + cursor, + } + } + } export default new BridgeClient() + +export async function* listAllEvents( + params?: Omit, +): AsyncGenerator { + const client = new BridgeClient() + + let cursor: string | undefined + do { + const page = await client.listEvents({ ...params, after: cursor, page_size: 100 }) + + for (const event of page.data) { + yield event + } + + cursor = page.has_more ? page.cursor : undefined + } while (cursor) +} \ No newline at end of file diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts new file mode 100644 index 000000000..ec964ba00 --- /dev/null +++ b/src/services/bridge/reconciliation.ts @@ -0,0 +1,155 @@ +import { baseLogger } from "@services/logger" +import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +import { upsertBridgeReconciliationOrphan } from "@services/mongoose/bridge-reconciliation-orphan" +import { BridgeDepositLog } from "@services/mongoose/schema" + +const ONE_DAY_MS = 24 * 60 * 60 * 1000 + +type BridgeDepositLike = { + eventId: string + transferId: string + customerId: string + amount: string + currency: string + destinationTxHash?: string + state: string + createdAt: Date +} + +type IbexReceiveLike = { + txHash: string + address: string + amount: string + currency: string + network: string + accountId?: string + receivedAt: Date +} + +const toOrphanKey = (prefix: string, value: string) => `${prefix}:${value.toLowerCase()}` + +export const reconcileBridgeAndIbexDeposits = async ({ + windowMs = ONE_DAY_MS, +}: { + windowMs?: number +} = {}): Promise< + | { + scannedBridge: number + scannedIbex: number + bridgeWithoutIbex: number + ibexWithoutBridge: number + } + | Error +> => { + try { + const now = new Date() + const since = new Date(now.getTime() - windowMs) + + const bridgeDeposits = (await BridgeDepositLog.find({ + createdAt: { $gte: since, $lte: now }, + state: "funds_received", + }) + .lean() + .exec()) as BridgeDepositLike[] + + const ibexReceivesResult = await findIbexCryptoReceiveLogsSince({ since, until: now }) + if (ibexReceivesResult instanceof Error) return ibexReceivesResult + const ibexReceives = ibexReceivesResult as IbexReceiveLike[] + + const ibexByTxHash = new Map() + for (const record of ibexReceives) { + ibexByTxHash.set(record.txHash.toLowerCase(), record) + } + + const bridgeByTxHash = new Map() + for (const deposit of bridgeDeposits) { + if (!deposit.destinationTxHash) continue + bridgeByTxHash.set(deposit.destinationTxHash.toLowerCase(), deposit) + } + + let bridgeWithoutIbex = 0 + let ibexWithoutBridge = 0 + + for (const deposit of bridgeDeposits) { + if (!deposit.destinationTxHash) { + bridgeWithoutIbex++ + await upsertBridgeReconciliationOrphan({ + orphanKey: toOrphanKey("bridge-no-tx", deposit.transferId), + orphanType: "bridge_without_ibex", + transferId: deposit.transferId, + bridgeEventId: deposit.eventId, + customerId: deposit.customerId, + amount: deposit.amount, + currency: deposit.currency, + triageContext: { + reason: "Bridge funds_received has no destinationTxHash", + windowStart: since.toISOString(), + windowEnd: now.toISOString(), + depositState: deposit.state, + createdAt: deposit.createdAt.toISOString(), + }, + }) + continue + } + + const matchedIbex = ibexByTxHash.get(deposit.destinationTxHash.toLowerCase()) + if (matchedIbex) continue + + bridgeWithoutIbex++ + await upsertBridgeReconciliationOrphan({ + orphanKey: toOrphanKey("bridge", deposit.destinationTxHash), + orphanType: "bridge_without_ibex", + transferId: deposit.transferId, + txHash: deposit.destinationTxHash, + bridgeEventId: deposit.eventId, + customerId: deposit.customerId, + amount: deposit.amount, + currency: deposit.currency, + triageContext: { + reason: + "No IBEX crypto.receive found for Bridge destinationTxHash within 24h window", + windowStart: since.toISOString(), + windowEnd: now.toISOString(), + depositState: deposit.state, + createdAt: deposit.createdAt.toISOString(), + }, + }) + } + + for (const receive of ibexReceives) { + const matchedBridge = bridgeByTxHash.get(receive.txHash.toLowerCase()) + if (matchedBridge) continue + + ibexWithoutBridge++ + await upsertBridgeReconciliationOrphan({ + orphanKey: toOrphanKey("ibex", receive.txHash), + orphanType: "ibex_without_bridge", + txHash: receive.txHash, + amount: receive.amount, + currency: receive.currency, + triageContext: { + reason: + "No Bridge deposit funds_received found for IBEX tx hash within 24h window", + windowStart: since.toISOString(), + windowEnd: now.toISOString(), + address: receive.address, + network: receive.network, + accountId: receive.accountId, + receivedAt: receive.receivedAt.toISOString(), + }, + }) + } + + const summary = { + scannedBridge: bridgeDeposits.length, + scannedIbex: ibexReceives.length, + bridgeWithoutIbex, + ibexWithoutBridge, + } + + baseLogger.info(summary, "Bridge↔IBEX reconciliation completed") + return summary + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts index 79b8778a5..22e75154c 100644 --- a/src/services/bridge/webhook-server/index.ts +++ b/src/services/bridge/webhook-server/index.ts @@ -13,6 +13,7 @@ import { verifyBridgeSignature } from "./middleware/verify-signature" import { kycHandler } from "./routes/kyc" import { depositHandler } from "./routes/deposit" import { transferHandler } from "./routes/transfer" +import { replayAuthMiddleware, replayHandler } from "./routes/replay" export const startBridgeWebhookServer = () => { const app = express() @@ -35,6 +36,11 @@ export const startBridgeWebhookServer = () => { app.post("/kyc", verifyBridgeSignature("kyc"), kycHandler) app.post("/deposit", verifyBridgeSignature("deposit"), depositHandler) app.post("/transfer", verifyBridgeSignature("transfer"), transferHandler) + app.post("/internal/replay", replayAuthMiddleware, replayHandler) + + if (!BridgeConfig.webhook.replaySecret && !process.env.BRIDGE_WEBHOOK_REPLAY_SECRET) { + baseLogger.warn("replaySecret not configured (neither BridgeConfig.webhook.replaySecret nor BRIDGE_WEBHOOK_REPLAY_SECRET) — /internal/replay will reject all requests with 503") + } // Start server const port = BridgeConfig.webhook.port diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index ccc98866b..e2217c7b7 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -1,6 +1,6 @@ /** * Bridge Deposit Webhook Handler - * Handles deposit.completed events from Bridge.xyz + * Handles transfer state-transition events (deposit flow) from Bridge.xyz * * NOTE: This handler only logs the deposit event. * The actual balance crediting happens when IBEX sends its crypto.received webhook. @@ -9,51 +9,77 @@ import { Request, Response } from "express" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" +import { createBridgeDepositLog } from "@services/mongoose/bridge-deposit-log" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body - const { id, amount, deposit_id, currency, subtotal_amount, customer_id, receipt } = event_object + const { id, state, amount, currency, on_behalf_of, receipt } = event_object ?? {} if (!id || !event_id) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using deposit_id as lock key - const lockKey = `bridge-deposit:${deposit_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + // Idempotency: lock on the transfer id + state so each state transition is processed once + const lockKey = `bridge-deposit:${id}:${state}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { - baseLogger.info({ event_id, id }, "Duplicate Bridge deposit webhook") + baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") return res.status(200).json({ status: "already_processed" }) } try { - // Log deposit event - // The actual balance crediting happens via IBEX crypto webhook baseLogger.info( { id, + state, amount, - initial_amount: receipt.initial_amount, currency, - deposit_id, + on_behalf_of, receipt: { - url: receipt.url, - initial_amount: receipt.initial_amount, - subtotal_amount: receipt.subtotal_amount, - final_amount: receipt.final_amount, - exchange_fee: receipt.exchange_fee, - gas_fee: receipt.gas_fee, + initial_amount: receipt?.initial_amount, + subtotal_amount: receipt?.subtotal_amount, + final_amount: receipt?.final_amount, + developer_fee: receipt?.developer_fee, + destination_tx_hash: receipt?.destination_tx_hash, }, - customer_id, event_id, - }, - "Bridge deposit completed", + "Bridge deposit event", ) + const depositLog = await createBridgeDepositLog({ + eventId: event_id, + transferId: id, + customerId: on_behalf_of ?? "", + state, + amount: String(amount), + currency, + developerFee: + receipt?.developer_fee != null + ? String(receipt.developer_fee) + : event_object?.developer_fee != null + ? String(event_object.developer_fee) + : "0", + subtotalAmount: + receipt?.subtotal_amount != null ? String(receipt.subtotal_amount) : undefined, + initialAmount: + receipt?.initial_amount != null ? String(receipt.initial_amount) : undefined, + finalAmount: + receipt?.final_amount != null ? String(receipt.final_amount) : undefined, + destinationTxHash: receipt?.destination_tx_hash, + }) + + if (depositLog instanceof Error) { + baseLogger.error( + { error: depositLog, event_id, id }, + "Failed to persist bridge deposit log", + ) + return res.status(500).json({ error: "Failed to persist deposit log" }) + } + return res.status(200).json({ status: "success" }) } catch (error) { - baseLogger.error({ error, id, event_id, deposit_id }, "Error processing Bridge deposit webhook") + baseLogger.error({ error, id, event_id }, "Error processing Bridge deposit webhook") return res.status(500).json({ error: "Internal server error" }) } } diff --git a/src/services/bridge/webhook-server/routes/replay.ts b/src/services/bridge/webhook-server/routes/replay.ts new file mode 100644 index 000000000..6316e2c8c --- /dev/null +++ b/src/services/bridge/webhook-server/routes/replay.ts @@ -0,0 +1,239 @@ +import crypto from "crypto" + +import { Request, Response } from "express" + +import { BridgeConfig } from "@config" + +import { baseLogger } from "@services/logger" + +import { createBridgeReplayLog } from "@services/mongoose/bridge-replay-log" + +import { depositHandler } from "./deposit" +import { kycHandler } from "./kyc" +import { transferHandler } from "./transfer" +type RouteKey = "kyc" | "deposit" | "transfer" + +const HANDLERS: Record Promise> = { + kyc: kycHandler, + deposit: depositHandler, + transfer: transferHandler, +} + +const DEPOSIT_EVENT_TYPES = new Set([ + "funds_scheduled", + "funds_received", + "payment_submitted", + "payment_processed", + "in_review", + "microdeposit", + "refund_in_flight", + "refunded", + "refund_failed", +]) + +const toRouteKey = (bridgeEventType: string): RouteKey | null => { + if (bridgeEventType.startsWith("kyc")) return "kyc" + if (bridgeEventType.startsWith("transfer")) return "transfer" + if (DEPOSIT_EVENT_TYPES.has(bridgeEventType)) return "deposit" + return null +} + +const resolveReplayEventType = ({ + eventType, + eventObjectStatus, +}: { + eventType: string + eventObjectStatus?: string +}): string => { + const routeFromEventType = toRouteKey(eventType) + if (routeFromEventType) return eventType + + if (eventObjectStatus && DEPOSIT_EVENT_TYPES.has(eventObjectStatus)) { + return eventObjectStatus + } + + if (eventObjectStatus === "approved" || eventObjectStatus === "rejected") { + return `kyc.${eventObjectStatus}` + } + + if (eventObjectStatus === "completed" || eventObjectStatus === "failed") { + return `transfer.${eventObjectStatus}` + } + + return eventType +} + +const toHandlerBody = ({ + routeKey, + eventId, + eventType, + eventObject, +}: { + routeKey: RouteKey + eventId: string + eventType: string + eventObject: Record +}): Record => { + if (routeKey === "transfer") { + return { + event: eventType, + data: { + transfer_id: eventObject.transfer_id ?? eventObject.id, + state: eventObject.state, + amount: eventObject.amount, + currency: eventObject.currency, + }, + } + } + + return { + event_id: eventId, + event_object: eventObject, + } +} + +export const replayAuthMiddleware = (req: Request, res: Response, next: () => void) => { + const secret = + BridgeConfig.webhook.replaySecret ?? process.env.BRIDGE_WEBHOOK_REPLAY_SECRET + if (!secret) { + baseLogger.warn("Replay secret not configured, rejecting replay request") + return res.status(503).json({ error: "Replay secret not configured" }) + } + + const token = (req.headers.authorization ?? "").replace(/^Bearer /, "") + + const valid = + token.length === secret.length && + crypto.timingSafeEqual(Buffer.from(token), Buffer.from(secret)) + + if (!valid) { + baseLogger.warn("Invalid replay token provided") + return res.status(401).json({ error: "Unauthorized" }) + } + + next() +} + +export const replayHandler = async (req: Request, res: Response) => { + const { + event_id, + event_type, + event_object_status, + event_object, + event_created_at, + operator, + time_window_start, + time_window_end, + dry_run = false, + } = req.body + + if (!event_type || !event_object || !event_created_at) { + return res.status(400).json({ + error: + "Missing required fields: event_type, event_object, event_created_at, operator, time_window_start, time_window_end", + }) + } + + if (typeof event_object !== "object" || event_object === null) { + return res.status(400).json({ error: "event_object must be an object" }) + } + + const normalizedEventType = resolveReplayEventType({ + eventType: event_type, + eventObjectStatus: + typeof event_object_status === "string" ? event_object_status : undefined, + }) + + const routeKey = toRouteKey(normalizedEventType) + + if (!routeKey) { + return res.status(400).json({ error: "Unsupported event_type for replay" }) + } + + const eventObjectTyped = event_object as Record + const eventId: string = + typeof event_id === "string" + ? event_id + : typeof eventObjectTyped.event_id === "string" + ? eventObjectTyped.event_id + : typeof eventObjectTyped.id === "string" + ? eventObjectTyped.id + : crypto.randomUUID() + + const logBase = { + eventId, + eventType: routeKey, + eventPayload: event_object, + bridgeEventCreatedAt: new Date(event_created_at), + replayedAt: new Date(), + operator, + timeWindowStart: new Date(time_window_start), + timeWindowEnd: new Date(time_window_end), + dryRun: dry_run, + } + + if (dry_run) { + const dryRunLog = await createBridgeReplayLog({ + ...logBase, + httpStatus: 0, + httpResponse: { dry_run: true }, + }) + if (dryRunLog instanceof Error) { + baseLogger.error( + { error: dryRunLog }, + "Failed to log bridge dry-run replay attempt", + ) + return res.status(500).json({ error: "Failed to log dry-run replay attempt" }) + } + return res.status(200).json({ + message: "Dry run successful, event not replayed", + log: logBase, + event_id: eventId, + }) + } + + let handlerStatus = 500 + let handlerBody: Record = {} + + const fakeReq = { + body: toHandlerBody({ + routeKey, + eventId, + eventType: normalizedEventType, + eventObject: eventObjectTyped, + }), + headers: {}, + } as unknown as Request + const fakeRes = { + status: function (code: number) { + handlerStatus = code + return this + }, + json: function (body: unknown) { + handlerBody = body as Record + return this + }, + } as unknown as Response + + await HANDLERS[routeKey](fakeReq, fakeRes) + + const logResult = await createBridgeReplayLog({ + ...logBase, + httpStatus: handlerStatus, + httpResponse: handlerBody, + }) + + if (logResult instanceof Error) { + baseLogger.error({ error: logResult }, "Failed to log bridge replay attempt") + return res + .status(500) + .json({ error: "Failed to log replay attempt", details: logResult.message }) + } + return res.status(handlerStatus).json({ + status: "replayed", + event_id: eventId, + handler_status: handlerStatus, + handler_response: handlerBody, + log_id: logResult.id, + }) +} diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 914b12f7c..e104a52df 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -1,9 +1,11 @@ import express, { Request, Response } from "express" import { AccountsRepository } from "@services/mongoose/accounts" +import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" import { listWalletsByAccountId } from "@app/wallets" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" + import { authenticate, logRequest } from "../middleware" const paths = { @@ -28,52 +30,71 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "Invalid payload" }) } - const lockResult = await LockService().lockPaymentHash(tx_hash as any, async () => { - try { - const account = await AccountsRepository().findByBridgeEthereumAddress(address) - if (account instanceof Error) { - baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") - return { status: "error", code: "account_not_found" } as CryptoReceiveResult - } - - const wallets = await listWalletsByAccountId(account.id) - if (wallets instanceof Error) { - baseLogger.error( - { accountId: account.id, error: wallets }, - "Failed to list wallets", + const lockResult = await LockService().lockPaymentHash( + tx_hash as PaymentHash, + async () => { + try { + const account = await AccountsRepository().findByBridgeEthereumAddress(address) + if (account instanceof Error) { + baseLogger.error({ address, tx_hash }, "Account not found for Ethereum address") + return { status: "error", code: "account_not_found" } as CryptoReceiveResult + } + + const ibexLog = await createIbexCryptoReceiveLog({ + txHash: String(tx_hash), + address: String(address), + amount: String(amount), + currency: String(currency), + network: String(network), + accountId: account.id, + }) + if (ibexLog instanceof Error) { + baseLogger.error( + { error: ibexLog, tx_hash }, + "Failed to persist IBEX crypto receive log", + ) + return { status: "error", code: "internal_error" } as CryptoReceiveResult + } + + const wallets = await listWalletsByAccountId(account.id) + if (wallets instanceof Error) { + baseLogger.error( + { accountId: account.id, error: wallets }, + "Failed to list wallets", + ) + return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult + } + + const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) + if (!usdtWallet) { + baseLogger.error({ accountId: account.id }, "USDT wallet not found") + return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult + } + + const usdtAmount = USDTAmount.fromNumber(amount) + if (usdtAmount instanceof Error) { + baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") + return { status: "error", code: "invalid_amount" } as CryptoReceiveResult + } + + baseLogger.info( + { + accountId: account.id, + walletId: usdtWallet.id, + amount: usdtAmount.asNumber(), + tx_hash, + address, + }, + "USDT deposit received", ) - return { status: "error", code: "wallet_list_failed" } as CryptoReceiveResult - } - const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) - if (!usdtWallet) { - baseLogger.error({ accountId: account.id }, "USDT wallet not found") - return { status: "error", code: "usdt_wallet_not_found" } as CryptoReceiveResult + return { status: "success" } as CryptoReceiveResult + } catch (error) { + baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") + return { status: "error", code: "internal_error" } as CryptoReceiveResult } - - const usdtAmount = USDTAmount.fromNumber(amount) - if (usdtAmount instanceof Error) { - baseLogger.error({ amount, error: usdtAmount }, "Invalid USDT amount") - return { status: "error", code: "invalid_amount" } as CryptoReceiveResult - } - - baseLogger.info( - { - accountId: account.id, - walletId: usdtWallet.id, - amount: usdtAmount.asNumber(), - tx_hash, - address, - }, - "USDT deposit received", - ) - - return { status: "success" } as CryptoReceiveResult - } catch (error) { - baseLogger.error({ error, tx_hash }, "Error processing crypto receive webhook") - return { status: "error", code: "internal_error" } as CryptoReceiveResult - } - }) + }, + ) if (lockResult instanceof Error) { baseLogger.warn( diff --git a/src/services/mongoose/bridge-deposit-log.ts b/src/services/mongoose/bridge-deposit-log.ts new file mode 100644 index 000000000..f71f37abd --- /dev/null +++ b/src/services/mongoose/bridge-deposit-log.ts @@ -0,0 +1,22 @@ +import { BridgeDepositLog } from "./schema" + +export const createBridgeDepositLog = async (data: { + eventId: string + transferId: string + customerId: string + state: string + amount: string + currency: string + developerFee: string + subtotalAmount?: string + initialAmount?: string + finalAmount?: string + destinationTxHash?: string +}): Promise<{ id: string } | Error> => { + try { + const log = await BridgeDepositLog.create(data) + return { id: log._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/bridge-reconciliation-orphan.ts b/src/services/mongoose/bridge-reconciliation-orphan.ts new file mode 100644 index 000000000..0150333d2 --- /dev/null +++ b/src/services/mongoose/bridge-reconciliation-orphan.ts @@ -0,0 +1,27 @@ +import { BridgeReconciliationOrphan } from "./schema" + +type OrphanType = "bridge_without_ibex" | "ibex_without_bridge" + +export const upsertBridgeReconciliationOrphan = async (data: { + orphanKey: string + orphanType: OrphanType + transferId?: string + txHash?: string + bridgeEventId?: string + customerId?: string + amount?: string + currency?: string + triageContext: Record +}): Promise<{ id: string } | Error> => { + try { + const orphan = await BridgeReconciliationOrphan.findOneAndUpdate( + { orphanKey: data.orphanKey }, + { ...data, detectedAt: new Date() }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) + + return { id: orphan._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/bridge-replay-log.ts b/src/services/mongoose/bridge-replay-log.ts new file mode 100644 index 000000000..5782471cf --- /dev/null +++ b/src/services/mongoose/bridge-replay-log.ts @@ -0,0 +1,42 @@ +import { BridgeReplayLog } from "./schema" + +export const createBridgeReplayLog = async (data: { + eventId: string + eventType: string, + eventPayload: Record, + bridgeEventCreatedAt: Date, + replayedAt: Date, + operator: string, + timeWindowStart: Date, + timeWindowEnd: Date, + httpStatus: number, + httpResponse: Record, + dryRun?: boolean +}): Promise<{ id: string } | Error> => { + + try { + const log = await BridgeReplayLog.create(data) + + return { id: log._id.toString() } + + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} + + +export const findBridgeReplayLogs = async (filter?: { + eventType?: string + dryRun?: boolean + limit?: number +}): Promise => { + try { + const queryFilter: Record = {} + if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType + if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun + + return await BridgeReplayLog.find(queryFilter).sort({ replayedAt: -1 }).limit(filter?.limit ?? 100).lean() + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/ibex-crypto-receive-log.ts b/src/services/mongoose/ibex-crypto-receive-log.ts new file mode 100644 index 000000000..e657c9e89 --- /dev/null +++ b/src/services/mongoose/ibex-crypto-receive-log.ts @@ -0,0 +1,51 @@ +import { IbexCryptoReceiveLog } from "./schema" + +export const createIbexCryptoReceiveLog = async (data: { + txHash: string + address: string + amount: string + currency: string + network: string + accountId?: string +}): Promise<{ id: string } | Error> => { + try { + const log = await IbexCryptoReceiveLog.findOneAndUpdate( + { txHash: data.txHash }, + { ...data, receivedAt: new Date() }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) + + return { id: log._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} + +export const findIbexCryptoReceiveLogsSince = async ({ + since, + until = new Date(), +}: { + since: Date + until?: Date +}): Promise< + | Array<{ + txHash: string + address: string + amount: string + currency: string + network: string + accountId?: string + receivedAt: Date + }> + | Error +> => { + try { + return await IbexCryptoReceiveLog.find({ + receivedAt: { $gte: since, $lte: until }, + }) + .sort({ receivedAt: -1 }) + .lean() + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index dd0b9aac9..2feb91d87 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -1,6 +1,11 @@ import crypto from "crypto" -import { getDefaultAccountsConfig, getFeesConfig, getDefaultFCMTopics, Levels } from "@config" +import { + getDefaultAccountsConfig, + getFeesConfig, + getDefaultFCMTopics, + Levels, +} from "@config" import { AccountStatus, UsernameRegex } from "@domain/accounts" import { WalletIdRegex, WalletType } from "@domain/wallets" import { WalletCurrency } from "@domain/shared" @@ -653,6 +658,89 @@ const BridgeWithdrawalSchema = new Schema({ updatedAt: { type: Date, default: Date.now }, }) +const BridgeDepositLogSchema = new Schema({ + eventId: { type: String, required: true, unique: true }, + transferId: { type: String, required: true }, + customerId: { type: String, required: true }, + state: { type: String, required: true }, + amount: { type: String, required: true }, + currency: { type: String, required: true }, + subtotalAmount: { type: String }, + developerFee: { type: String, required: true, default: "0" }, + initialAmount: { type: String }, + finalAmount: { type: String }, + destinationTxHash: { type: String }, + createdAt: { type: Date, default: Date.now }, +}) + +BridgeDepositLogSchema.index({ transferId: 1 }) +BridgeDepositLogSchema.index({ customerId: 1, createdAt: -1 }) + +export const BridgeDepositLog = mongoose.model("BridgeDepositLog", BridgeDepositLogSchema) + +const IbexCryptoReceiveLogSchema = new Schema({ + txHash: { type: String, required: true, unique: true }, + address: { type: String, required: true }, + amount: { type: String, required: true }, + currency: { type: String, required: true }, + network: { type: String, required: true }, + accountId: { type: String }, + receivedAt: { type: Date, default: Date.now }, +}) + +IbexCryptoReceiveLogSchema.index({ receivedAt: -1 }) +IbexCryptoReceiveLogSchema.index({ address: 1, receivedAt: -1 }) + +export const IbexCryptoReceiveLog = mongoose.model( + "IbexCryptoReceiveLog", + IbexCryptoReceiveLogSchema, +) + +const BridgeReconciliationOrphanSchema = new Schema({ + orphanKey: { type: String, required: true, unique: true }, + orphanType: { + type: String, + enum: ["bridge_without_ibex", "ibex_without_bridge"], + required: true, + }, + transferId: { type: String }, + txHash: { type: String }, + bridgeEventId: { type: String }, + customerId: { type: String }, + amount: { type: String }, + currency: { type: String }, + triageContext: { type: Schema.Types.Mixed, required: true }, + detectedAt: { type: Date, default: Date.now }, +}) + +BridgeReconciliationOrphanSchema.index({ orphanType: 1, detectedAt: -1 }) +BridgeReconciliationOrphanSchema.index({ detectedAt: -1 }) + +export const BridgeReconciliationOrphan = mongoose.model( + "BridgeReconciliationOrphan", + BridgeReconciliationOrphanSchema, +) + +const BridgeReplayLogSchema = new Schema({ + eventId: { type: String, required: true }, + eventType: { type: String, required: true }, + eventPayload: { type: Schema.Types.Mixed, required: true }, + bridgeEventCreatedAt: { type: Date, required: true }, + replayedAt: { type: Date, required: true, default: Date.now }, + operator: { type: String, required: true }, + timeWindowStart: { type: Date, required: true }, + timeWindowEnd: { type: Date, required: true }, + httpStatus: { type: Number, required: true }, + httpResponse: { type: Schema.Types.Mixed, required: true }, + dryRun: { type: Boolean, required: true, default: false }, +}) + +BridgeReplayLogSchema.index({ eventId: 1 }) +BridgeReplayLogSchema.index({ replayedAt: -1 }) +BridgeReplayLogSchema.index({ eventType: 1, replayedAt: -1 }) + +export const BridgeReplayLog = mongoose.model("BridgeReplayLog", BridgeReplayLogSchema) + export const BridgeVirtualAccount = mongoose.model( "BridgeVirtualAccount", BridgeVirtualAccountSchema, diff --git a/test/flash/unit/services/bridge/client.spec.ts b/test/flash/unit/services/bridge/client.spec.ts new file mode 100644 index 000000000..a4fc194f5 --- /dev/null +++ b/test/flash/unit/services/bridge/client.spec.ts @@ -0,0 +1,152 @@ +// AC1: listAllEvents surfaces orphan events for ops triage tooling + +jest.mock("@config", () => ({ + BridgeConfig: { + apiKey: "test-api-key", + baseUrl: "https://api.sandbox.bridge.xyz/v0", + }, +})) + +import { BridgeClient, listAllEvents, BridgeWebhookEvent } from "@services/bridge/client" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const makeEvent = (id: string): BridgeWebhookEvent => ({ + id, + event_type: "transfer.completed", + payload: { transfer_id: id }, + created_at: "2026-05-01T10:00:00Z", +}) + +// ── listAllEvents ───────────────────────────────────────────────────────────── + +describe("listAllEvents", () => { + let listEventsSpy: jest.SpyInstance + + beforeEach(() => { + listEventsSpy = jest.spyOn(BridgeClient.prototype, "listEvents") + }) + + afterEach(() => { + listEventsSpy.mockRestore() + }) + + it("yields all events from a single page", async () => { + listEventsSpy.mockResolvedValue({ + data: [makeEvent("e1"), makeEvent("e2")], + has_more: false, + cursor: undefined, + }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(2) + expect(events.map((e) => e.id)).toEqual(["e1", "e2"]) + expect(listEventsSpy).toHaveBeenCalledTimes(1) + }) + + it("paginates across multiple pages until has_more is false", async () => { + listEventsSpy + .mockResolvedValueOnce({ + data: [makeEvent("e1"), makeEvent("e2")], + has_more: true, + cursor: "cursor-page-2", + }) + .mockResolvedValueOnce({ + data: [makeEvent("e3"), makeEvent("e4")], + has_more: true, + cursor: "cursor-page-3", + }) + .mockResolvedValueOnce({ + data: [makeEvent("e5")], + has_more: false, + cursor: undefined, + }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(5) + expect(events.map((e) => e.id)).toEqual(["e1", "e2", "e3", "e4", "e5"]) + expect(listEventsSpy).toHaveBeenCalledTimes(3) + }) + + it("passes the cursor from page N as 'after' on page N+1", async () => { + listEventsSpy + .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "cur-abc" }) + .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + + for await (const _ of listAllEvents()) { /* drain */ } + + expect(listEventsSpy).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ after: undefined }), + ) + expect(listEventsSpy).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ after: "cur-abc" }), + ) + }) + + it("always requests page_size 100", async () => { + listEventsSpy.mockResolvedValue({ data: [], has_more: false, cursor: undefined }) + + for await (const _ of listAllEvents()) { /* drain */ } + + expect(listEventsSpy).toHaveBeenCalledWith( + expect.objectContaining({ page_size: 100 }), + ) + }) + + it("forwards start_date, end_date and event_type filters to every page", async () => { + listEventsSpy + .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "c1" }) + .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + + const params = { + start_date: "2026-05-01T00:00:00Z", + end_date: "2026-05-02T00:00:00Z", + event_type: "transfer.completed", + } + + for await (const _ of listAllEvents(params)) { /* drain */ } + + expect(listEventsSpy).toHaveBeenCalledTimes(2) + for (const call of listEventsSpy.mock.calls) { + expect(call[0]).toMatchObject(params) + } + }) + + it("yields nothing and makes one call when the first page is empty", async () => { + listEventsSpy.mockResolvedValue({ data: [], has_more: false, cursor: undefined }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(0) + expect(listEventsSpy).toHaveBeenCalledTimes(1) + }) + + it("stops immediately if has_more is false even when cursor is present", async () => { + listEventsSpy.mockResolvedValue({ + data: [makeEvent("e1")], + has_more: false, + cursor: "stale-cursor", + }) + + const events: BridgeWebhookEvent[] = [] + for await (const e of listAllEvents()) { + events.push(e) + } + + expect(events).toHaveLength(1) + expect(listEventsSpy).toHaveBeenCalledTimes(1) + }) +}) diff --git a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts new file mode 100644 index 000000000..fe2756e3d --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts @@ -0,0 +1,225 @@ +// AC3: Bridge fee value persisted on every deposit row + +jest.mock("@services/lock", () => ({ + LockService: jest.fn(), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-deposit-log", () => ({ + createBridgeDepositLog: jest.fn(), +})) + +import { Request, Response } from "express" +import { LockService } from "@services/lock" +import * as DepositLog from "@services/mongoose/bridge-deposit-log" +import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (body: Record) => + ({ body } as unknown as Request) + +// Real Bridge deposit event shape +const VALID_EVENT_OBJECT = { + id: "tr_xyz789abc123", + state: "funds_received", + amount: "1.00", + currency: "usd", + developer_fee: "0.0", + on_behalf_of: "cust_bob", + source: { + currency: "usdb", + payment_rail: "bridge_wallet", + bridge_wallet_id: "wallet_bob_usdb", + }, + receipt: { + developer_fee: "0.0", + initial_amount: "1.00", + subtotal_amount: "1.00", + final_amount: "1.00", + destination_tx_hash: "4gJH6oXpZUNgC1QLh8mXNPF92LtLKzHZj5eHuQrdQAgB", + }, + created_at: "2025-06-11T21:27:00.000Z", + updated_at: "2025-06-11T21:27:01.000Z", +} + +const VALID_BODY = { + api_version: "v0", + event_id: "wh_789xyz654mno", + event_category: "transfer", + event_type: "updated.status_transitioned", + event_object_id: "tr_xyz789abc123", + event_object_status: "funds_received", + event_object: VALID_EVENT_OBJECT, + event_object_changes: { state: ["payment_submitted", "funds_received"] }, + event_created_at: "2025-06-11T21:27:00.000Z", +} + +// ── Setup ───────────────────────────────────────────────────────────────────── + +const mockLockService = (acquired = true) => { + ;(LockService as jest.Mock).mockReturnValue({ + lockIdempotencyKey: jest + .fn() + .mockResolvedValue(acquired ? {} : new Error("already locked")), + }) +} + +beforeEach(() => { + jest.clearAllMocks() + mockLockService(true) +}) + +// ── Invalid payload ─────────────────────────────────────────────────────────── + +describe("depositHandler — invalid payload", () => { + it("returns 400 when event_id is missing", async () => { + const res = makeRes() + const { event_id: _, ...body } = VALID_BODY + await depositHandler(makeReq(body), res) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) + expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + }) + + it("returns 400 when event_object.id is missing", async () => { + const res = makeRes() + const { id: _, ...objWithoutId } = VALID_EVENT_OBJECT + await depositHandler(makeReq({ ...VALID_BODY, event_object: objWithoutId }), res) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) + expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + }) +}) + +// ── Idempotency ─────────────────────────────────────────────────────────────── + +describe("depositHandler — idempotency", () => { + it("returns already_processed without persisting on duplicate state transition", async () => { + mockLockService(false) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.json as jest.Mock).toHaveBeenCalledWith({ status: "already_processed" }) + expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() + }) + + it("locks on transfer id + state so different states for same transfer are processed", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ id: "log-1" }) + + const lockFn = jest.fn().mockResolvedValue({}) + ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) + + await depositHandler(makeReq(VALID_BODY), makeRes()) + expect(lockFn).toHaveBeenCalledWith("bridge-deposit:tr_xyz789abc123:funds_received") + + await depositHandler( + makeReq({ + ...VALID_BODY, + event_object: { ...VALID_EVENT_OBJECT, state: "payment_processed" }, + }), + makeRes(), + ) + expect(lockFn).toHaveBeenCalledWith( + "bridge-deposit:tr_xyz789abc123:payment_processed", + ) + }) +}) + +// ── AC3: Fee persistence ────────────────────────────────────────────────────── + +describe("depositHandler — fee persistence (AC3)", () => { + it("persists developer_fee from the receipt on every deposit event", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-001", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledTimes(1) + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect.objectContaining({ + developerFee: "0.0", + }), + ) + }) + + it("persists the full deposit record with transfer id, customer, state and receipt breakdown", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-002", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect.objectContaining({ + eventId: "wh_789xyz654mno", + transferId: "tr_xyz789abc123", + customerId: "cust_bob", + state: "funds_received", + amount: "1.00", + currency: "usd", + subtotalAmount: "1.00", + initialAmount: "1.00", + finalAmount: "1.00", + destinationTxHash: "4gJH6oXpZUNgC1QLh8mXNPF92LtLKzHZj5eHuQrdQAgB", + }), + ) + }) + + it("returns 200 success after persisting the deposit log", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-003", + }) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect(res.json as jest.Mock).toHaveBeenCalledWith({ status: "success" }) + }) + + it("returns 500 when log persistence fails", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue( + new Error("mongo timeout"), + ) + + const res = makeRes() + await depositHandler(makeReq(VALID_BODY), res) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) + }) + + it("handles null developer_fee gracefully", async () => { + ;(DepositLog.createBridgeDepositLog as jest.Mock).mockResolvedValue({ + id: "log-fee-004", + }) + + const body = { + ...VALID_BODY, + event_object: { + ...VALID_EVENT_OBJECT, + receipt: { ...VALID_EVENT_OBJECT.receipt, developer_fee: null }, + }, + } + + const res = makeRes() + await depositHandler(makeReq(body), res) + + expect(DepositLog.createBridgeDepositLog).toHaveBeenCalledWith( + expect.objectContaining({ developerFee: "0.0" }), + ) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + }) +}) diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts new file mode 100644 index 000000000..32d6e7302 --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -0,0 +1,296 @@ +import crypto from "crypto" + +// AC1: Orphan event surfaces in ops tooling with triage context +// AC2: Replay CLI re-runs a stuck handler against a chosen transfer-id + +jest.mock("@config", () => ({ + BridgeConfig: { + webhook: { replaySecret: "super-secret-replay-token-xyz" }, + }, +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-replay-log", () => ({ + createBridgeReplayLog: jest.fn(), +})) + +jest.mock("@services/bridge/webhook-server/routes/deposit", () => ({ + depositHandler: jest.fn(), +})) +jest.mock("@services/bridge/webhook-server/routes/kyc", () => ({ + kycHandler: jest.fn(), +})) +jest.mock("@services/bridge/webhook-server/routes/transfer", () => ({ + transferHandler: jest.fn(), +})) + +import { Request, Response } from "express" +import { replayAuthMiddleware, replayHandler } from "@services/bridge/webhook-server/routes/replay" +import * as ReplayLog from "@services/mongoose/bridge-replay-log" +import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" +import { kycHandler } from "@services/bridge/webhook-server/routes/kyc" +import { transferHandler } from "@services/bridge/webhook-server/routes/transfer" + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (body: Record = {}, headers: Record = {}) => + ({ body, headers } as unknown as Request) + +const BASE_BODY = { + event_type: "funds_received", + event_object: { id: "evt-001", transfer_id: "xfer-001" }, + event_created_at: "2026-05-01T12:00:00Z", + operator: "ops@example.com", + time_window_start: "2026-05-01T00:00:00Z", + time_window_end: "2026-05-01T23:59:59Z", +} + +// ── replayAuthMiddleware ────────────────────────────────────────────────────── + +describe("replayAuthMiddleware", () => { + beforeEach(() => jest.clearAllMocks()) + + it("returns 503 when replaySecret is not configured", () => { + const { BridgeConfig } = jest.requireMock("@config") + const saved = BridgeConfig.webhook.replaySecret + BridgeConfig.webhook.replaySecret = undefined + + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware(makeReq({}, {}), res, next) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(503) + expect(next).not.toHaveBeenCalled() + + BridgeConfig.webhook.replaySecret = saved + }) + + it("returns 401 for a wrong token", () => { + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware( + makeReq({}, { authorization: "Bearer wrong-token" }), + res, + next, + ) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(next).not.toHaveBeenCalled() + }) + + it("returns 401 when Authorization header is absent", () => { + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware(makeReq({}, {}), res, next) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(next).not.toHaveBeenCalled() + }) + + it("calls next() for the correct Bearer token", () => { + const res = makeRes() + const next = jest.fn() + replayAuthMiddleware( + makeReq({}, { authorization: "Bearer super-secret-replay-token-xyz" }), + res, + next, + ) + + expect(next).toHaveBeenCalledTimes(1) + expect((res.status as jest.Mock)).not.toHaveBeenCalled() + }) + + it("uses timing-safe comparison (different-length token is rejected)", () => { + const res = makeRes() + const next = jest.fn() + // A prefix of the real secret — same content up to length, but different length + replayAuthMiddleware( + makeReq({}, { authorization: "Bearer super-secret-replay-token-xy" }), + res, + next, + ) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(next).not.toHaveBeenCalled() + }) +}) + +// ── replayHandler ───────────────────────────────────────────────────────────── + +describe("replayHandler", () => { + beforeEach(() => jest.clearAllMocks()) + + describe("input validation", () => { + it("returns 400 when event_type is missing", async () => { + const res = makeRes() + const { event_type: _et, ...body } = BASE_BODY + await replayHandler(makeReq(body), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + + it("returns 400 when event_object is missing", async () => { + const res = makeRes() + const { event_object: _eo, ...body } = BASE_BODY + await replayHandler(makeReq(body), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + + it("returns 400 when event_created_at is missing", async () => { + const res = makeRes() + const { event_created_at: _ec, ...body } = BASE_BODY + await replayHandler(makeReq(body), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + + it("returns 400 for an unrecognised event_type", async () => { + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, event_type: "unknown_event" }), res) + expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + }) + }) + + describe("event_type → handler routing", () => { + const cases: Array<[string, jest.Mock]> = [ + ["funds_received", depositHandler as jest.Mock], + ["funds_scheduled", depositHandler as jest.Mock], + ["payment_processed", depositHandler as jest.Mock], + ["kyc.approved", kycHandler as jest.Mock], + ["kyc.rejected", kycHandler as jest.Mock], + ["transfer.completed", transferHandler as jest.Mock], + ["transfer.failed", transferHandler as jest.Mock], + ] + + beforeEach(() => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) + }) + + test.each(cases)("%s is routed to the correct handler", async (eventType, handler) => { + handler.mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, event_type: eventType }), res) + + expect(handler).toHaveBeenCalledTimes(1) + }) + }) + + describe("dry_run mode", () => { + it("returns 200 without calling any handler", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-001" }) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) + + expect(depositHandler).not.toHaveBeenCalled() + expect(kycHandler).not.toHaveBeenCalled() + expect(transferHandler).not.toHaveBeenCalled() + expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + }) + + it("persists a dry-run log entry with httpStatus 0", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-002" }) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) + + expect(ReplayLog.createBridgeReplayLog).toHaveBeenCalledWith( + expect.objectContaining({ httpStatus: 0, dryRun: true }), + ) + }) + + it("returns 500 when dry-run log creation fails", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("db error")) + + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + }) + }) + + describe("live replay", () => { + beforeEach(() => { + ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) + }) + + it("returns the handler's status code and response body", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-001" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + const jsonArg = (res.json as jest.Mock).mock.calls[0][0] + expect(jsonArg).toMatchObject({ status: "replayed", handler_status: 200 }) + }) + + it("persists a replay log with triage context (operator + time window)", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-002" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect(ReplayLog.createBridgeReplayLog).toHaveBeenCalledWith( + expect.objectContaining({ + operator: "ops@example.com", + timeWindowStart: new Date("2026-05-01T00:00:00Z"), + timeWindowEnd: new Date("2026-05-01T23:59:59Z"), + eventId: "evt-001", + httpStatus: 200, + dryRun: false, + }), + ) + }) + + it("includes log_id in the response so ops can trace the replay", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-trace-007" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + const jsonArg = (res.json as jest.Mock).mock.calls[0][0] + expect(jsonArg.log_id).toBe("log-trace-007") + }) + + it("returns 500 when log creation fails after a successful handler run", async () => { + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("mongo down")) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + }) + + it("propagates a handler 4xx back to the caller", async () => { + ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(422) + ;(res.json as jest.Mock)({ error: "Unprocessable" }) + return Promise.resolve(res) + }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-4xx" }) + + const res = makeRes() + await replayHandler(makeReq(BASE_BODY), res) + + expect((res.status as jest.Mock)).toHaveBeenCalledWith(422) + }) + }) +}) diff --git a/tsconfig-build.json b/tsconfig-build.json index aa3e79ccb..9ce1b13ac 100644 --- a/tsconfig-build.json +++ b/tsconfig-build.json @@ -1,7 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["src/servers/**/*.ts", "src/**/*.d.ts"], + "include": [ + "src/servers/**/*.ts", + "src/scripts/**/*.ts", + "src/**/*.d.ts" + ], "compilerOptions": { "noImplicitAny": true } -} +} \ No newline at end of file From 573d6c97effc9976ae3640eae147d580df69b98a Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:26 -0400 Subject: [PATCH 04/22] ENG-394 feat(accounts): create ETH-USDT Cash Wallet for new accounts --- src/app/accounts/create-account.ts | 26 ++-- src/app/wallets/get-balance-for-wallet.ts | 16 +-- src/config/schema.ts | 4 +- src/domain/shared/MoneyAmount.ts | 11 +- src/domain/wallets/index.types.d.ts | 15 ++- src/services/ibex/client.ts | 91 +++++++------ src/services/ibex/types.ts | 8 +- src/services/mongoose/wallets.ts | 31 +++-- .../unit/app/accounts/create-account.spec.ts | 122 ++++++++++++++++++ .../wallets/get-balance-for-wallet.spec.ts | 38 ++++++ .../unit/services/mongoose/wallets.spec.ts | 82 ++++++++++++ 11 files changed, 368 insertions(+), 76 deletions(-) create mode 100644 test/flash/unit/app/accounts/create-account.spec.ts create mode 100644 test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts create mode 100644 test/flash/unit/services/mongoose/wallets.spec.ts diff --git a/src/app/accounts/create-account.ts b/src/app/accounts/create-account.ts index 67b28fc04..17ea2c1f2 100644 --- a/src/app/accounts/create-account.ts +++ b/src/app/accounts/create-account.ts @@ -10,8 +10,13 @@ import { } from "@services/mongoose" import { recordExceptionInCurrentSpan } from "@services/tracing" -import { ErrorLevel } from "@domain/shared" -import { RepositoryError } from "@domain/errors" +import { ErrorLevel, WalletCurrency } from "@domain/shared" + +const requiredCashWalletCurrencies: WalletCurrency[] = [ + WalletCurrency.Usd, + WalletCurrency.Usdt, +] +const defaultCashWalletCurrency = WalletCurrency.Usdt const initializeCreatedAccount = async ({ account, @@ -29,24 +34,29 @@ const initializeCreatedAccount = async ({ currency, }) - const walletsEnabledConfig = config.initialWallets + const walletsEnabledConfig = Array.from( + new Set([...config.initialWallets, ...requiredCashWalletCurrencies]), + ) // Create all wallets const enabledWallets: Partial> = {} for (const currency of walletsEnabledConfig) { const wallet = await newWallet(currency) - if (wallet instanceof RepositoryError) { + if (wallet instanceof Error) { recordExceptionInCurrentSpan({ error: wallet, level: ErrorLevel.Critical, - attributes: { accountId: account.id } + attributes: { accountId: account.id, currency }, }) + if (requiredCashWalletCurrencies.includes(currency)) return wallet + continue } - else enabledWallets[currency] = wallet + + enabledWallets[currency] = wallet } - // Set default wallet to USD - const defaultWalletId = enabledWallets[walletsEnabledConfig[0]]?.id + // Set ETH-USDT as the active Cash Wallet while preserving USD for migration. + const defaultWalletId = enabledWallets[defaultCashWalletCurrency]?.id if (defaultWalletId === undefined) { return new ConfigError("NoWalletsEnabledInConfigError") diff --git a/src/app/wallets/get-balance-for-wallet.ts b/src/app/wallets/get-balance-for-wallet.ts index 30aaff7a4..46ddf6cad 100644 --- a/src/app/wallets/get-balance-for-wallet.ts +++ b/src/app/wallets/get-balance-for-wallet.ts @@ -11,19 +11,11 @@ export const getBalanceForWallet = async ({ currency?: WalletCurrency }): Promise => { try { - if (currency === WalletCurrency.Usdt) { - const resp = await Ibex.getCryptoReceiveBalance(walletId) - if (resp instanceof IbexError) { - if (resp.httpCode === 404) return USDTAmount.ZERO - return resp - } - if (resp === undefined) return new UnexpectedIbexResponse("Balance not found") - return resp - } - - const resp = await Ibex.getAccountDetails(walletId) + const resp = await Ibex.getAccountDetails(walletId, currency) if (resp instanceof IbexError) { - if (resp.httpCode === 404) return USDAmount.ZERO + if (resp.httpCode === 404) { + return currency === WalletCurrency.Usdt ? USDTAmount.ZERO : USDAmount.ZERO + } return resp } if (resp.balance === undefined) return new UnexpectedIbexResponse("Balance not found") diff --git a/src/config/schema.ts b/src/config/schema.ts index c6f751f28..0d673fd2b 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -385,7 +385,7 @@ export const configSchema = { additionalProperties: false, default: { initialStatus: "active", - initialWallets: ["USD"], + initialWallets: ["USD", "USDT"], enablePhoneCheck: false, enableIpCheck: false, enableIpProxyCheck: false, @@ -663,7 +663,7 @@ export const configSchema = { required: ["kyc", "deposit", "transfer"], }, timestampSkewMs: { type: "integer" }, - replaySecret: { type: "string" } + replaySecret: { type: "string" }, }, required: ["port", "publicKeys", "timestampSkewMs"], }, diff --git a/src/domain/shared/MoneyAmount.ts b/src/domain/shared/MoneyAmount.ts index 8818c20aa..7ad965663 100644 --- a/src/domain/shared/MoneyAmount.ts +++ b/src/domain/shared/MoneyAmount.ts @@ -1,6 +1,6 @@ import { Money, Round, PRECISION_M } from "./bigint-money" import { BigIntConversionError, UnsupportedCurrencyError } from "./errors" -import { ExchangeCurrencyUnit, WalletCurrency } from "./primitives" +import { WalletCurrency } from "./primitives" export abstract class MoneyAmount { readonly money: Money @@ -60,7 +60,8 @@ export abstract class MoneyAmount { static from(amount: number | string, currency: WalletCurrency): MoneyAmount | Error { if (currency === WalletCurrency.Usd) return USDAmount.cents(amount.toString()) else if (currency === WalletCurrency.Jmd) return JMDAmount.cents(amount.toString()) - else if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(amount.toString()) + else if (currency === WalletCurrency.Usdt) + return USDTAmount.smallestUnits(amount.toString()) else return new UnsupportedCurrencyError(`Could not read currency: ${currency}`) } } @@ -177,7 +178,9 @@ export class BtcAmount extends MoneyAmount { try { return new BtcAmount(c) } catch (error) { - return new BigIntConversionError(error instanceof Error ? error.message : String(error)) + return new BigIntConversionError( + error instanceof Error ? error.message : String(error), + ) } } @@ -191,7 +194,7 @@ export class BtcAmount extends MoneyAmount { } export class USDTAmount extends MoneyAmount { - static currencyId: IbexCurrencyId = 4 as IbexCurrencyId + static currencyId: IbexCurrencyId = 29 as IbexCurrencyId private constructor(amount: Money | bigint | string | number) { super(amount, WalletCurrency.Usdt) diff --git a/src/domain/wallets/index.types.d.ts b/src/domain/wallets/index.types.d.ts index 6aa876f61..ee5e1fb23 100644 --- a/src/domain/wallets/index.types.d.ts +++ b/src/domain/wallets/index.types.d.ts @@ -189,7 +189,7 @@ interface IWalletsRepository { accountId, type, currency, - }: NewWalletInfo): Promise + }: NewWalletInfo): Promise findById(walletId: WalletId): Promise listByAccountId(accountId: AccountId): Promise @@ -200,7 +200,7 @@ interface IWalletsRepository { walletCurrency: WalletCurrency, ): Promise - upsertExternal({ accountId, currency, lnurlp }: { accountId: AccountId, currency: WalletCurrency, lnurlp: Lnurl }): Promise + upsertExternal({ accountId, currency, lnurlp }: { accountId: AccountId, currency?: WalletCurrency, lnurlp: Lnurl }): Promise findExternalByAccountId(accountId: AccountId): Promise } @@ -239,3 +239,14 @@ type OnChainFeeCalculator = { } intraLedgerFees(): PaymentAmountInAllCurrencies } + +type PaymentInputValidatorConfig = ( + walletId: WalletId, +) => Promise + +type PaymentInputValidator = { + validatePaymentInput: ( + args: ValidatePaymentInputArgs, + ) => Promise | ValidationError | RepositoryError> +} + diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 409e03570..2eb0a65fa 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -1,7 +1,4 @@ -import IbexClient, { GetFeeEstimateResponse200, IbexClientError } from "ibex-client" -import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" -import { IbexConfig } from "@config" -import { +import IbexClient, { AddInvoiceBodyParam, AddInvoiceResponse201, CreateAccountResponse201, @@ -9,32 +6,29 @@ import { CreateLnurlPayResponse201, DecodeLnurlMetadataParam, DecodeLnurlResponse200, - EstimateFeeCopyMetadataParam, EstimateFeeCopyResponse200, - GenerateBitcoinAddressBodyParam, GenerateBitcoinAddressResponse201, - GetAccountDetailsMetadataParam, - GetAccountDetailsResponse200, - GetFeeEstimationMetadataParam, - GetFeeEstimationResponse200, - GetTransactionDetails1MetadataParam, GetTransactionDetails1Response200, GMetadataParam, GResponse200, - InvoiceFromHashMetadataParam, InvoiceFromHashResponse200, PayInvoiceV2BodyParam, PayInvoiceV2Response200, - PayToALnurlPayBodyParam, PayToALnurlPayResponse201, SendToAddressCopyBodyParam, SendToAddressCopyResponse200, } from "ibex-client" + +import { IbexConfig } from "@config" import { addAttributesToCurrentSpan, wrapAsyncFunctionsToRunInSpan, } from "@services/tracing" -import WebhookServer from "./webhook-server" + +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +import { baseLogger } from "@services/logger" + import { Redis } from "./cache" import { GetFeeEstimateArgs, @@ -46,8 +40,9 @@ import { CryptoReceiveInfo, CreateCryptoReceiveInfoRequest, } from "./types" -import { USDAmount, USDTAmount } from "@domain/shared" -import { baseLogger } from "@services/logger" + +import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" +import WebhookServer from "./webhook-server" const Ibex = new IbexClient( IbexConfig.url, @@ -62,22 +57,33 @@ const createAccount = async ( return Ibex.createAccount({ name, currencyId }).then(errorHandler) } +const parseAccountBalance = ( + balance: number | undefined, + currency: WalletCurrency, +): IbexAccountDetails["balance"] => { + if (balance === undefined) return undefined + + const amount = + currency === WalletCurrency.Usdt + ? USDTAmount.fromNumber(balance.toString()) + : USDAmount.dollars(balance.toString()) + + return amount instanceof Error ? undefined : amount +} + const getAccountDetails = async ( accountId: IbexAccountId, + currency: WalletCurrency = WalletCurrency.Usd, ): Promise => { return Ibex.getAccountDetails({ accountId }) - .then((r) => { - if (r instanceof Error) return r - else { - let balance = - r.balance !== undefined ? USDAmount.dollars(r.balance.toString()) : undefined - if (balance instanceof Error) balance = undefined - return { - id: r.id, - userId: r.userId, - name: r.name, - balance, - } + .then((resp) => { + if (resp instanceof Error) return resp + + return { + id: resp.id, + userId: resp.userId, + name: resp.name, + balance: parseAccountBalance(resp.balance, currency), } }) .then(errorHandler) @@ -143,9 +149,9 @@ const getLnFeeEstimation = async ( else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) return new UnexpectedIbexResponse("invoiceAmount not found.") else { - let fee = USDAmount.dollars(resp.amount) + const fee = USDAmount.dollars(resp.amount) if (fee instanceof Error) return new ParseError(fee) - let invoiceAmount = USDAmount.dollars(resp.invoiceAmount) + const invoiceAmount = USDAmount.dollars(resp.invoiceAmount) if (invoiceAmount instanceof Error) return new ParseError(invoiceAmount) return { fee, @@ -232,7 +238,9 @@ const getIbexToken = async (): Promise => { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), - }).catch((err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err)))) + }).catch( + (err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err))), + ) if (resp instanceof IbexError) return resp if (!resp.ok) { @@ -240,17 +248,24 @@ const getIbexToken = async (): Promise => { return new IbexError(new Error(`IBEX sign-in failed: ${resp.status} — ${body}`)) } - const data = await resp.json() as { + const data = (await resp.json()) as { accessToken?: string accessTokenExpiresAt?: number refreshToken?: string refreshTokenExpiresAt?: number } - if (!data.accessToken) return new IbexError(new Error("IBEX sign-in: no access token in response")) + if (!data.accessToken) + return new IbexError(new Error("IBEX sign-in: no access token in response")) - await Ibex.authentication.storage.setAccessToken(data.accessToken, data.accessTokenExpiresAt) + await Ibex.authentication.storage.setAccessToken( + data.accessToken, + data.accessTokenExpiresAt, + ) if (data.refreshToken) { - await Ibex.authentication.storage.setRefreshToken(data.refreshToken, data.refreshTokenExpiresAt) + await Ibex.authentication.storage.setRefreshToken( + data.refreshToken, + data.refreshTokenExpiresAt, + ) } return `Bearer ${data.accessToken}` @@ -264,7 +279,11 @@ const ibexFetch = async ( const url = `${IbexConfig.url}${path}` const resp = await fetch(url, { ...init, - headers: { Authorization: token, "Content-Type": "application/json", ...init.headers }, + headers: { + "Authorization": token, + "Content-Type": "application/json", + ...init.headers, + }, }) if (!resp.ok) { const body = await resp.text().catch(() => "") diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index 4a02bf260..ec3d55015 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -1,4 +1,4 @@ -import { USDAmount } from "@domain/shared" +import { USDAmount, USDTAmount } from "@domain/shared" export type PayInvoiceArgs = { accountId: IbexAccountId @@ -7,8 +7,8 @@ export type PayInvoiceArgs = { } export type SendOnchainArgs = { - accountId: IbexAccountId, // source of funds - address: OnChainAddress, // destination + accountId: IbexAccountId // source of funds + address: OnChainAddress // destination amount: USDAmount } @@ -27,7 +27,7 @@ export type IbexAccountDetails = { id: string | undefined userId: string | undefined name: string | undefined - balance: USDAmount | undefined + balance: USDAmount | USDTAmount | undefined } export type IbexInvoiceArgs = { diff --git a/src/services/mongoose/wallets.ts b/src/services/mongoose/wallets.ts index 30f5444f5..300be6fbd 100644 --- a/src/services/mongoose/wallets.ts +++ b/src/services/mongoose/wallets.ts @@ -16,11 +16,13 @@ import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" +import { recordExceptionInCurrentSpan } from "@services/tracing" + +import { ErrorLevel, USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + import { toObjectId, fromObjectId, parseRepositoryError } from "./utils" import { Wallet } from "./schema" import { AccountsRepository } from "./accounts" -import { recordExceptionInCurrentSpan } from "@services/tracing" -import { ErrorLevel, USDAmount, WalletCurrency } from "@domain/shared" import { WalletType } from "@domain/wallets" export interface WalletRecord { @@ -32,6 +34,19 @@ export interface WalletRecord { lnurlp: string } +const getIbexCurrencyId = ( + currency: WalletCurrency, +): IbexCurrencyId | UnsupportedCurrencyError => { + switch (currency) { + case WalletCurrency.Usd: + return USDAmount.currencyId + case WalletCurrency.Usdt: + return USDTAmount.currencyId + default: + return new UnsupportedCurrencyError(`Unsupported IBEX wallet currency: ${currency}`) + } +} + export const WalletsRepository = (): IWalletsRepository => { const persistNew = async ({ accountId, @@ -42,7 +57,8 @@ export const WalletsRepository = (): IWalletsRepository => { if (account instanceof Error) return account try { - let currencyId = USDAmount.currencyId + const currencyId = getIbexCurrencyId(currency) + if (currencyId instanceof Error) return currencyId const resp = await Ibex.createAccount(accountId, currencyId) if (resp instanceof IbexError) return resp @@ -63,8 +79,7 @@ export const WalletsRepository = (): IWalletsRepository => { ibexAccountId, }, }) - } - else lnurlp = lnurlResp.lnurl + } else lnurlp = lnurlResp.lnurl } const wallet = new Wallet({ @@ -72,7 +87,7 @@ export const WalletsRepository = (): IWalletsRepository => { id: ibexAccountId, type, currency, - lnurlp + lnurlp, }) await wallet.save() return resultToWallet(wallet) @@ -162,7 +177,7 @@ export const WalletsRepository = (): IWalletsRepository => { lnurlp, }: { accountId: AccountId - currency: WalletCurrency + currency?: WalletCurrency lnurlp: Lnurl }): Promise => { if (!lnurlp.toLowerCase().startsWith("lnurl1")) { @@ -173,7 +188,7 @@ export const WalletsRepository = (): IWalletsRepository => { { _accountId: toObjectId(accountId), type: WalletType.External }, { $set: { lnurlp }, - $setOnInsert: { id: randomUUID(), currency: currency }, + $setOnInsert: { id: randomUUID(), currency: currency ?? WalletCurrency.Btc }, }, { upsert: true, new: true }, ) diff --git a/test/flash/unit/app/accounts/create-account.spec.ts b/test/flash/unit/app/accounts/create-account.spec.ts new file mode 100644 index 000000000..4c006e9d1 --- /dev/null +++ b/test/flash/unit/app/accounts/create-account.spec.ts @@ -0,0 +1,122 @@ +import { createAccountWithPhoneIdentifier } from "@app/accounts/create-account" +import { AccountLevel } from "@domain/accounts" +import { WalletCurrency } from "@domain/shared" +import { PersistError } from "@domain/errors" +import { WalletType } from "@domain/wallets" +import { + AccountsRepository, + UsersRepository, + WalletsRepository, +} from "@services/mongoose" + +jest.mock("@config", () => ({ + getAdminAccounts: jest.fn(() => []), +})) + +jest.mock("@services/tracing", () => ({ + recordExceptionInCurrentSpan: jest.fn(), +})) + +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(), + UsersRepository: jest.fn(), + WalletsRepository: jest.fn(), +})) + +const mockedAccountsRepository = AccountsRepository as jest.MockedFunction< + typeof AccountsRepository +> +const mockedUsersRepository = UsersRepository as jest.MockedFunction< + typeof UsersRepository +> +const mockedWalletsRepository = WalletsRepository as jest.MockedFunction< + typeof WalletsRepository +> + +describe("createAccountWithPhoneIdentifier", () => { + let persistNew: jest.Mock + + const account = { + id: "account-id" as AccountId, + defaultWalletId: undefined, + } as unknown as Account + + const config = { + initialWallets: [WalletCurrency.Usd], + initialStatus: "active", + initialLevel: AccountLevel.One, + } as AccountsConfig + + beforeEach(() => { + jest.clearAllMocks() + + mockedUsersRepository.mockReturnValue({ + update: jest.fn().mockResolvedValue({ id: "user-id" }), + } as unknown as ReturnType) + + mockedAccountsRepository.mockReturnValue({ + persistNew: jest.fn().mockResolvedValue({ ...account }), + update: jest + .fn() + .mockImplementation(async (updatedAccount: Account) => updatedAccount), + } as unknown as ReturnType) + + persistNew = jest.fn().mockImplementation(async ({ accountId, type, currency }) => ({ + id: `${currency}-wallet-id`, + accountId, + type, + currency, + })) + + mockedWalletsRepository.mockReturnValue({ + persistNew, + } as unknown as ReturnType) + }) + + it("creates both USD and USDT cash wallets and defaults new accounts to USDT", async () => { + const result = await createAccountWithPhoneIdentifier({ + newAccountInfo: { + kratosUserId: "kratos-user-id" as UserId, + phone: "+15551234567" as PhoneNumber, + }, + config, + }) + + expect(result).not.toBeInstanceOf(Error) + + expect(persistNew).toHaveBeenCalledWith({ + accountId: account.id, + type: WalletType.Checking, + currency: WalletCurrency.Usd, + }) + expect(persistNew).toHaveBeenCalledWith({ + accountId: account.id, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + expect(persistNew).toHaveBeenCalledTimes(2) + expect((result as Account).defaultWalletId).toBe(`${WalletCurrency.Usdt}-wallet-id`) + }) + + it("does not create an account with a USD fallback default if the USDT wallet is missing", async () => { + persistNew.mockImplementation(async ({ accountId, type, currency }) => { + if (currency === WalletCurrency.Usdt) return new PersistError("USDT wallet failed") + return { + id: `${currency}-wallet-id`, + accountId, + type, + currency, + } + }) + + const result = await createAccountWithPhoneIdentifier({ + newAccountInfo: { + kratosUserId: "kratos-user-id" as UserId, + phone: "+15551234567" as PhoneNumber, + }, + config, + }) + + expect(result).toBeInstanceOf(Error) + }) +}) diff --git a/test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts b/test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts new file mode 100644 index 000000000..4c7fe2680 --- /dev/null +++ b/test/flash/unit/app/wallets/get-balance-for-wallet.spec.ts @@ -0,0 +1,38 @@ +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { USDTAmount, WalletCurrency } from "@domain/shared" +import Ibex from "@services/ibex/client" + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getAccountDetails: jest.fn(), + getCryptoReceiveBalance: jest.fn(), + }, +})) + +describe("getBalanceForWallet", () => { + beforeEach(() => { + jest.mocked(Ibex.getAccountDetails).mockReset() + jest.mocked(Ibex.getCryptoReceiveBalance).mockReset() + }) + + it("loads USDT balances from the IBEX account id, not a crypto receive-info id", async () => { + const balance = USDTAmount.ZERO + jest.mocked(Ibex.getAccountDetails).mockResolvedValue({ + id: "ibex-account-id", + balance, + } as never) + + const result = await getBalanceForWallet({ + walletId: "ibex-account-id" as WalletId, + currency: WalletCurrency.Usdt, + }) + + expect(Ibex.getAccountDetails).toHaveBeenCalledWith( + "ibex-account-id", + WalletCurrency.Usdt, + ) + expect(Ibex.getCryptoReceiveBalance).not.toHaveBeenCalled() + expect(result).toBe(balance) + }) +}) diff --git a/test/flash/unit/services/mongoose/wallets.spec.ts b/test/flash/unit/services/mongoose/wallets.spec.ts new file mode 100644 index 000000000..9707694cd --- /dev/null +++ b/test/flash/unit/services/mongoose/wallets.spec.ts @@ -0,0 +1,82 @@ +import { UnsupportedCurrencyError } from "@domain/errors" +import { WalletCurrency } from "@domain/shared" +import Ibex from "@services/ibex/client" +import { AccountsRepository } from "@services/mongoose/accounts" +import { WalletsRepository } from "@services/mongoose/wallets" + +const save = jest.fn() +const walletConstructor = jest.fn().mockImplementation((record) => ({ + ...record, + save, +})) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + createAccount: jest.fn(), + createLnurlPay: jest.fn(), + }, +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/schema", () => ({ + Wallet: jest.fn().mockImplementation((record) => walletConstructor(record)), +})) + +jest.mock("@services/mongoose/utils", () => ({ + toObjectId: jest.fn((id) => id), + fromObjectId: jest.fn((id) => id), + parseRepositoryError: jest.fn((err) => err), +})) + +describe("WalletsRepository.persistNew", () => { + beforeEach(() => { + save.mockReset().mockResolvedValue(undefined) + walletConstructor.mockClear() + jest.mocked(AccountsRepository).mockReturnValue({ + findById: jest.fn().mockResolvedValue({ id: "account-id" }), + } as never) + jest + .mocked(Ibex.createAccount) + .mockReset() + .mockResolvedValue({ + id: "ibex-account-id", + } as never) + jest + .mocked(Ibex.createLnurlPay) + .mockReset() + .mockResolvedValue({ + lnurl: "lnurlp", + } as never) + }) + + it("rejects currencies without an IBEX account currency id", async () => { + const result = await WalletsRepository().persistNew({ + accountId: "account-id" as AccountId, + type: "checking" as WalletType, + currency: WalletCurrency.Btc, + }) + + expect(result).toBeInstanceOf(UnsupportedCurrencyError) + expect(Ibex.createAccount).not.toHaveBeenCalled() + expect(Ibex.createLnurlPay).not.toHaveBeenCalled() + }) + + it("creates USDT wallets as IBEX currency 29 accounts", async () => { + const result = await WalletsRepository().persistNew({ + accountId: "account-id" as AccountId, + type: "checking" as WalletType, + currency: WalletCurrency.Usdt, + }) + + expect(result).not.toBeInstanceOf(Error) + expect(Ibex.createAccount).toHaveBeenCalledWith("account-id", 29) + expect(Ibex.createLnurlPay).toHaveBeenCalledWith({ + accountId: "ibex-account-id", + currencyId: 29, + }) + }) +}) From 4c70b8fa141957e773f03eb447cb173e081483ab Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:02:31 -0400 Subject: [PATCH 05/22] ENG-297 docs: document Bridge rebase PR-readiness plan --- ...026-05-13-bridge-rebase-pr-ready-design.md | 30 ++++ .../2026-05-13-bridge-rebase-pr-ready.md | 130 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md create mode 100644 docs/plans/2026-05-13-bridge-rebase-pr-ready.md diff --git a/docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md b/docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md new file mode 100644 index 000000000..dee60cd28 --- /dev/null +++ b/docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md @@ -0,0 +1,30 @@ +# Bridge Rebase PR-Ready Design + +## Goal +Prepare the rebased Bridge integration branch for review by making the history Linear-scoped, applying only intentional cleanup, and verifying branch-owned changes without expanding lint scope to unrelated code. + +## Constraints +- Every commit in the PR history must reference at least one Linear issue key. +- If multiple commits belong to one Linear issue, squash them unless separation materially improves reviewability. +- Do not lint code that this branch did not change or create. +- Preserve the current rebased branch as a safety point. +- Do not import the broad ForgeMini scratch formatting/dependency churn; cherry-pick only intentional fixes. + +## Approach +Work from a new safety branch, reset the index against `origin/main`, and recommit the branch diff into Linear-scoped slices. Use existing Bridge Linear issues for core feature, security-audit fixes, reconciliation/replay tooling, account wallet creation, and PR-readiness cleanup. After history rewrite, apply the proven Bridge unit/idempotency fix selectively and verify build/unit/focused tests plus scoped lint only on files changed by the branch. + +## Commit grouping +- `ENG-297`: core Bridge parity/integration surface and docs that support the launch path. +- `ENG-276`: deposit reconciliation and replay/backfill tooling. +- `ENG-348`: ERPNext audit-row writer support, if present in the diff. +- `ENG-376`: webhook/request idempotency hardening and retry-safety fixes. +- `ENG-394`: ETH-USDT Cash Wallet creation for raw account creation. +- Existing audit issues (`ENG-278`, `ENG-280`, `ENG-281`, `ENG-282`, `ENG-283`, `ENG-284`, `ENG-285`, `ENG-349`, `ENG-363`, etc.) stay mapped in commit messages where their changes are separable. + +## Verification +- `yarn build` +- full unit suite if available in this worktree +- focused Bridge unit suites +- typecheck, with baseline failures documented separately if reproduced on `origin/main` +- scoped ESLint only for branch-created or branch-modified files, never unrelated files +- SDL diff check and commit regenerated schema/supergraph output only when branch-owned changes require it diff --git a/docs/plans/2026-05-13-bridge-rebase-pr-ready.md b/docs/plans/2026-05-13-bridge-rebase-pr-ready.md new file mode 100644 index 000000000..43f3a608f --- /dev/null +++ b/docs/plans/2026-05-13-bridge-rebase-pr-ready.md @@ -0,0 +1,130 @@ +# Bridge Rebase PR-Ready Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Make the rebased Bridge integration branch PR-ready with Linear-scoped commits and scoped verification. + +**Architecture:** Preserve the completed rebase as a safety point, then use a cleanup branch to rewrite the diff into issue-scoped commits. Apply only targeted fixes from scratch worktrees and verify branch-owned behavior without linting unrelated code. + +**Tech Stack:** TypeScript, GraphQL, Jest, Yarn, Git, Linear issue keys, Bridge.xyz integration code. + +--- + +### Task 1: Create safety branch and planning docs + +**Files:** +- Create: `docs/plans/2026-05-13-bridge-rebase-pr-ready-design.md` +- Create: `docs/plans/2026-05-13-bridge-rebase-pr-ready.md` + +**Step 1:** Switch to a cleanup branch at current rebased HEAD. + +Run: `git switch -c tmp/bridge-rebase-pr-ready` +Expected: branch points to the rebased candidate HEAD. + +**Step 2:** Add the design and implementation plan docs. + +Run: `git status --short` +Expected: only the two new docs are untracked. + +**Step 3:** Commit with a Linear key. + +Run: `git add docs/plans && git commit -m "ENG-297 docs: document Bridge rebase PR-readiness plan"` +Expected: commit succeeds and includes only plan docs. + +### Task 2: Rewrite existing branch history into Linear-scoped commits + +**Files:** +- Modify: all files in the current `origin/main..HEAD` diff. + +**Step 1:** Capture current HEAD. + +Run: `git branch safety/bridge-rebase-before-history-rewrite HEAD` +Expected: safety ref exists. + +**Step 2:** Soft reset to `origin/main`. + +Run: `git reset --soft origin/main && git restore --staged .` +Expected: full branch diff is unstaged working tree changes. + +**Step 3:** Stage and commit logical groups. + +Use path-based staging and `git add -p` where needed. Each commit message must start with or include a Linear issue key. + +Expected commit groups: +- `ENG-297 feat(bridge): add core Bridge parity integration` +- `ENG-276 feat(bridge): add reconciliation and replay tooling` +- `ENG-348 feat(bridge): add ERPNext audit rows for Bridge movements` +- `ENG-376 fix(bridge): harden Bridge request idempotency` +- `ENG-394 feat(accounts): create ETH-USDT Cash Wallet for new accounts` +- Audit fixes mapped to their issue keys where separable. + +**Step 4:** Verify all commits have Linear keys. + +Run: `git log --format='%h %s' origin/main..HEAD | awk '!/ENG-[0-9]+|OPS-[0-9]+|COM-[0-9]+/ {print}'` +Expected: no output. + +### Task 3: Apply targeted Bridge unit/idempotency fix + +**Files:** +- Modify: `src/services/bridge/index.ts` +- Modify: `test/flash/unit/services/bridge/index.spec.ts` + +**Step 1:** Cherry-pick only intentional hunks from ForgeMini scratch worktree. + +Expected changes: +- Mock `@services/ibex/client` in Bridge service unit test. +- Before creating a new withdrawal row, call `BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer(accountId, externalAccountId, amount)`. +- Reuse an existing pending withdrawal row to derive the same idempotency key on retry. + +**Step 2:** Run focused test. + +Run: `yarn jest test/flash/unit/services/bridge/index.spec.ts --runInBand` +Expected: Bridge service unit suite passes. + +**Step 3:** Commit. + +Run: `git add src/services/bridge/index.ts test/flash/unit/services/bridge/index.spec.ts && git commit -m "ENG-376 fix(bridge): reuse pending withdrawal idempotency rows"` +Expected: commit succeeds. + +### Task 4: Verify PR readiness without unrelated lint + +**Files:** +- Test/build commands operate on the branch. +- Lint command must be limited to files changed by `origin/main..HEAD`. + +**Step 1:** Run build. + +Run: `yarn build` +Expected: pass, or document branch-independent baseline failure. + +**Step 2:** Run focused Bridge tests. + +Run: focused Jest commands for Bridge service/client/webhook suites. +Expected: pass. + +**Step 3:** Run full unit suite if dependencies are present. + +Run: repository unit test command. +Expected: pass, or document failing suites with ownership. + +**Step 4:** Run scoped lint only on branch-owned files. + +Run: construct changed-file list with `git diff --name-only origin/main...HEAD` and pass only supported TS/JS files to ESLint. +Expected: no branch-owned lint failures, or documented remaining branch-owned fixes. + +**Step 5:** Check SDL/schema drift. + +Run: repository SDL generation/check command. +Expected: either clean, or commit required generated schema artifacts with the relevant ENG key. + +### Task 5: Prepare PR branch + +**Step 1:** Show final summary. + +Run: `git status --short --branch`, `git log --oneline origin/main..HEAD`, and `git diff --shortstat origin/main...HEAD`. +Expected: clean worktree and Linear-scoped commits. + +**Step 2:** Push only after explicit approval if force-updating `feature/bridge-integration`. + +Run when approved: `git push --force-with-lease origin HEAD:feature/bridge-integration` +Expected: remote branch updates safely. From 30064566812769a300196c36856551c90dfb359a Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:03:43 -0400 Subject: [PATCH 06/22] ENG-376 fix(bridge): reuse pending withdrawal idempotency rows --- src/services/bridge/index.ts | 26 ++++++++++++------- test/flash/unit/services/bridge/index.spec.ts | 8 ++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index ff73924bd..fb308bdbd 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -416,7 +416,7 @@ const initiateWithdrawal = async ( return new BridgeInsufficientFundsError("Invalid withdrawal amount") } - const availableBalance = balance.toIbex(); + const availableBalance = balance.toIbex() if (availableBalance < withdrawalAmount) { baseLogger.warn( { accountId, availableBalance, withdrawalAmount, operation: "initiateWithdrawal" }, @@ -445,15 +445,23 @@ const initiateWithdrawal = async ( return new Error("External account is not verified") } - - // Store withdrawal record - const pendingWithdrawal = await BridgeAccountsRepo.createWithdrawal({ - accountId: accountId as string, - amount: amount, - currency: "usdt", + const existingWithdrawal = await BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer( + accountId as string, externalAccountId, - status: "pending", - }) + amount, + ) + if (existingWithdrawal instanceof Error) return existingWithdrawal + + // Store withdrawal record, or reuse the in-flight row for a retry of the same request. + const pendingWithdrawal = + existingWithdrawal || + (await BridgeAccountsRepo.createWithdrawal({ + accountId: accountId as string, + amount, + currency: "usdt", + externalAccountId, + status: "pending", + })) if (pendingWithdrawal instanceof Error) return pendingWithdrawal const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 20aa5f9ad..4324021d2 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -29,6 +29,14 @@ jest.mock("@services/bridge/client", () => ({ default: { createTransfer: jest.fn() }, })) +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getEthereumUsdtOption: jest.fn(), + createCryptoReceiveInfo: jest.fn(), + }, +})) + jest.mock("@services/mongoose/accounts", () => ({ AccountsRepository: jest.fn(), })) From 7d21cf85881e5606cafd79939866114288401196 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 14:13:08 -0400 Subject: [PATCH 07/22] ENG-297 chore(bridge): satisfy scoped lint on branch changes --- src/app/offers/Validator.ts | 54 +++++-- src/config/yaml.ts | 18 ++- src/domain/wallets/index.types.d.ts | 11 +- src/graphql/error-map.ts | 10 +- src/graphql/public/queries.ts | 1 + .../root/mutation/bridge-initiate-kyc.ts | 7 +- .../mutation/bridge-initiate-withdrawal.ts | 6 +- .../types/object/bridge-virtual-account.ts | 5 +- .../shared/types/object/usdt-wallet.ts | 18 ++- ...bridge-virtual-account-unique-accountid.ts | 1 + src/servers/graphql-admin-server.ts | 39 +++-- src/servers/graphql-server.ts | 4 +- src/services/bridge/client.ts | 110 +++++++++----- src/services/bridge/errors.ts | 4 +- src/services/bridge/index.ts | 113 ++++++++------ src/services/bridge/webhook-server/index.ts | 15 +- .../middleware/verify-signature.ts | 17 ++- .../bridge/webhook-server/routes/kyc.ts | 8 +- .../bridge/webhook-server/routes/transfer.ts | 2 +- src/services/ibex/webhook-server/index.ts | 1 + src/services/mongoose/accounts.ts | 1 - src/services/mongoose/bridge-accounts.ts | 5 +- src/services/mongoose/bridge-replay-log.ts | 62 ++++---- src/services/mongoose/wallets.ts | 6 +- .../flash/unit/services/bridge/client.spec.ts | 36 ++++- test/flash/unit/services/bridge/index.spec.ts | 51 ++++--- .../bridge/webhook-server/deposit.spec.ts | 9 +- .../bridge/webhook-server/replay.spec.ts | 139 ++++++++++-------- 28 files changed, 478 insertions(+), 275 deletions(-) diff --git a/src/app/offers/Validator.ts b/src/app/offers/Validator.ts index e16b97735..3b113edc9 100644 --- a/src/app/offers/Validator.ts +++ b/src/app/offers/Validator.ts @@ -1,9 +1,22 @@ -import { getBalanceForWallet } from "@app/wallets"; -import { Cashout } from "@config"; -import { AccountValidator, hasErpParty, isActiveAccount, walletBelongsToAccount } from "@domain/accounts"; -import { JMDAmount, USDAmount, USDTAmount, ValidationError, ValidationFn, validator } from "@domain/shared"; -import { ValidationInputs } from "./types"; -import ErpNext from "@services/frappe/ErpNext"; +import { getBalanceForWallet } from "@app/wallets" +import { Cashout } from "@config" +import { + AccountValidator, + hasErpParty, + isActiveAccount, + walletBelongsToAccount, +} from "@domain/accounts" +import { + JMDAmount, + USDAmount, + USDTAmount, + ValidationError, + ValidationFn, + validator, +} from "@domain/shared" +import ErpNext from "@services/frappe/ErpNext" + +import { ValidationInputs } from "./types" const config = Cashout.validations @@ -21,7 +34,9 @@ const cashoutMin = async (o: ValidationInputs): Promise else return true } -const cashoutMax: ValidationFn = async (o: ValidationInputs): Promise => { +const cashoutMax: ValidationFn = async ( + o: ValidationInputs, +): Promise => { const max = USDAmount.cents(config.maximum.amount) if (max instanceof Error) return new ValidationError(max) if (o.payment.amount.isGreaterThan(max)) @@ -35,13 +50,14 @@ const isUsd = async (o: ValidationInputs) => { return true } -const hasSufficientBalance = async (o: ValidationInputs): Promise => { +const hasSufficientBalance = async ( + o: ValidationInputs, +): Promise => { const balance = await getBalanceForWallet({ walletId: o.wallet.id, currency: o.wallet.currency, }) - if (balance instanceof Error) - return new ValidationError(balance) + if (balance instanceof Error) return new ValidationError(balance) if (balance instanceof USDTAmount) return new ValidationError("Cash out only supports withdrawals from USD wallets") else if (o.payment.amount.isGreaterThan(balance)) @@ -53,17 +69,23 @@ const accountLevel = async (o: ValidationInputs) => { return AccountValidator(o.account).isLevel(config.accountLevel) } -// Much of this logic is checked server-side in erpnext, but we want to catch it as early as possible -const verifyBankAccount = async (o: ValidationInputs): Promise => { +// Much of this logic is checked server-side in erpnext, but we want to catch it as early as possible +const verifyBankAccount = async ( + o: ValidationInputs, +): Promise => { const erpParty = o.account.erpParty - if (!erpParty) return new ValidationError("Account does not have an associated erpParty") + if (!erpParty) + return new ValidationError("Account does not have an associated erpParty") const banks = await ErpNext.getBankAccountsByCustomer(erpParty) - if (banks instanceof Error) return new ValidationError("Could not confirm bank account for user") - const bankAccount = banks.find(b => b.name === o.payout.bankAccountId) + if (banks instanceof Error) + return new ValidationError("Could not confirm bank account for user") + const bankAccount = banks.find((b) => b.name === o.payout.bankAccountId) if (!bankAccount) return new ValidationError("Bank account does not belong to user") const payoutCurrency = o.payout.amount instanceof JMDAmount ? "JMD" : "USD" if (bankAccount.currency !== payoutCurrency) - return new ValidationError(`Bank account currency (${bankAccount.currency}) does not match payout currency (${payoutCurrency})`) + return new ValidationError( + `Bank account currency (${bankAccount.currency}) does not match payout currency (${payoutCurrency})`, + ) return true } diff --git a/src/config/yaml.ts b/src/config/yaml.ts index 136300441..4d0058c9c 100644 --- a/src/config/yaml.ts +++ b/src/config/yaml.ts @@ -3,7 +3,7 @@ import fs from "fs" import path from "path" import Ajv from "ajv" -import yaml from "js-yaml" +import { load as loadYaml } from "js-yaml" import { I18n } from "i18n" import { baseLogger } from "@services/logger" @@ -21,19 +21,19 @@ import { AccountLevel } from "@domain/accounts" import mergeWith from "lodash.mergewith" +import yargs from "yargs" + import { configSchema } from "./schema" import { ConfigError } from "./error" -import yargs from "yargs" - -const argv: any = +const argv = // .help() yargs(process.argv.slice(2)).option("configPath", { alias: "c", type: "array", description: "Paths to YAML configuration files", demandOption: true, - }).argv + }).argv as { configPath: string[] } // replaces array with override const merge = (defaultConfig: unknown, customConfig: unknown) => @@ -46,7 +46,7 @@ export const mergeYamls = (filePaths: string[]): Record => { try { const resolvedPath = path.resolve(filePath) const fileContent = fs.readFileSync(resolvedPath, "utf8") - const parsedConfig = yaml.load(fileContent) as Record + const parsedConfig = loadYaml(fileContent) as Record merge(mergedConfig, parsedConfig) @@ -268,8 +268,10 @@ export const getTestAccounts = (config = yamlConfig): TestAccount[] => export const getCronConfig = (config = yamlConfig): CronConfig => config.cronConfig -export const getDefaultFCMTopics = (config = yamlConfig): string[] => config.fcmTopics.filter(t => t.default).map(t => t.name) -export const getFCMTopics = (config = yamlConfig): string[] => config.fcmTopics.map(t => t.name) +export const getDefaultFCMTopics = (config = yamlConfig): string[] => + config.fcmTopics.filter((t) => t.default).map((t) => t.name) +export const getFCMTopics = (config = yamlConfig): string[] => + config.fcmTopics.map((t) => t.name) export const getCaptcha = (config = yamlConfig): CaptchaConfig => config.captcha diff --git a/src/domain/wallets/index.types.d.ts b/src/domain/wallets/index.types.d.ts index ee5e1fb23..d86dcbc21 100644 --- a/src/domain/wallets/index.types.d.ts +++ b/src/domain/wallets/index.types.d.ts @@ -200,7 +200,15 @@ interface IWalletsRepository { walletCurrency: WalletCurrency, ): Promise - upsertExternal({ accountId, currency, lnurlp }: { accountId: AccountId, currency?: WalletCurrency, lnurlp: Lnurl }): Promise + upsertExternal({ + accountId, + currency, + lnurlp, + }: { + accountId: AccountId + currency?: WalletCurrency + lnurlp: Lnurl + }): Promise findExternalByAccountId(accountId: AccountId): Promise } @@ -249,4 +257,3 @@ type PaymentInputValidator = { args: ValidatePaymentInputArgs, ) => Promise | ValidationError | RepositoryError> } - diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 95a461d66..c53a4b37f 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -40,7 +40,7 @@ import { } from "@graphql/error" import { baseLogger } from "@services/logger" -const assertUnreachable = (x: any): never => { +const assertUnreachable = (x: unknown): never => { throw new Error(`This should never compile with ${x}`) } @@ -477,7 +477,8 @@ export const mapError = (error: ApplicationError): CustomApolloError => { }) case "BridgeInvalidAmountError": - message = error.message || "Amount must be strictly positive with at most 6 decimal places" + message = + error.message || "Amount must be strictly positive with at most 6 decimal places" return new ValidationInternalError({ message, logger: baseLogger }) case "BridgeBelowMinimumWithdrawalError": @@ -803,17 +804,16 @@ export const apolloErrorResponse = (e: CustomApolloError): { errors: IError[] } path: e.path, code: e.extensions.code, }, - ] + ], } } - export const mapAndParseErrorForGqlResponse = (err: ApplicationError): IError => { const mappedError = mapError(err) return { message: mappedError.message, path: mappedError.path, - code: mappedError.extensions.code + code: mappedError.extensions.code, } } diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 2d95858b7..4596d6e48 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -16,6 +16,7 @@ import BusinessMapMarkersQuery from "@graphql/public/root/query/business-map-mar import AccountDefaultWalletQuery from "@graphql/public/root/query/account-default-wallet" import AccountDefaultWalletIdQuery from "@graphql/public/root/query/account-default-wallet-id" import LnInvoicePaymentStatusQuery from "@graphql/public/root/query/ln-invoice-payment-status" + import NpubByUserNameQuery from "./root/query/username-npub-query" import IsFlashNpubQuery from "./root/query/is-flash-npub-query" import TransactionDetailsQuery from "./root/query/transaction-details" diff --git a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts index 89da4f77a..3f1574c1a 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-kyc.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-kyc.ts @@ -38,7 +38,12 @@ const bridgeInitiateKyc = GT.Field({ return { errors: [mapAndParseErrorForGqlResponse(new BridgeAccountLevelError())] } } - const result = await BridgeService.initiateKyc({ accountId: domainAccount.id, email, type, full_name }) + const result = await BridgeService.initiateKyc({ + accountId: domainAccount.id, + email, + type, + full_name, + }) if (result instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(result)] } } diff --git a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts index afef326e6..945e7624c 100644 --- a/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts +++ b/src/graphql/public/root/mutation/bridge-initiate-withdrawal.ts @@ -45,7 +45,11 @@ const bridgeInitiateWithdrawal = GT.Field({ // validate the amount is greater than the minimum withdrawal amount if (parseFloat(amount) < BridgeConfig.minWithdrawalAmount) { return { - errors: [mapAndParseErrorForGqlResponse(new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount))], + errors: [ + mapAndParseErrorForGqlResponse( + new BridgeBelowMinimumWithdrawalError(BridgeConfig.minWithdrawalAmount), + ), + ], } } diff --git a/src/graphql/public/types/object/bridge-virtual-account.ts b/src/graphql/public/types/object/bridge-virtual-account.ts index f2c294b83..02a8c99b2 100644 --- a/src/graphql/public/types/object/bridge-virtual-account.ts +++ b/src/graphql/public/types/object/bridge-virtual-account.ts @@ -3,7 +3,10 @@ import { GT } from "@graphql/index" const BridgeVirtualAccount = GT.Object({ name: "BridgeVirtualAccount", fields: () => ({ - id: { type: GT.ID, resolve: (src) => src.virtualAccountId ?? src.bridgeVirtualAccountId }, + id: { + type: GT.ID, + resolve: (src) => src.virtualAccountId ?? src.bridgeVirtualAccountId, + }, bankName: { type: GT.String }, routingNumber: { type: GT.String }, accountNumber: { type: GT.String }, diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index d91a0e613..59139203e 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -9,19 +9,24 @@ import { mapError } from "@graphql/error-map" import { Wallets } from "@app" -import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" +import { + USDAmount, + WalletCurrency as WalletCurrencyDomain, + USDTAmount, +} from "@domain/shared" + +import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import IWallet from "../abstract/wallet" import WalletCurrency from "../scalar/wallet-currency" import SignedAmount from "../scalar/signed-amount" import OnChainAddress from "../scalar/on-chain-address" -import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" -import { TransactionConnection } from "./transaction" -import { baseLogger } from "@services/logger" import Lnurl from "../scalar/lnurl" +import { TransactionConnection } from "./transaction" + const UsdtWallet = GT.Object({ name: "UsdtWallet", description: @@ -58,7 +63,10 @@ const UsdtWallet = GT.Object({ if (balance instanceof USDTAmount) { return Number(balance.asSmallestUnits(8)) } - return Number((balance as any).asCents(8)) + if (balance instanceof USDAmount) { + return Number(balance.asCents(8)) + } + throw mapError(balance) }, }, pendingIncomingBalance: { diff --git a/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts index 3f6f6f71a..3b6d676b4 100644 --- a/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts +++ b/src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-nocheck /* eslint @typescript-eslint/no-var-requires: "off" */ diff --git a/src/servers/graphql-admin-server.ts b/src/servers/graphql-admin-server.ts index 3a31cae69..52d65959e 100644 --- a/src/servers/graphql-admin-server.ts +++ b/src/servers/graphql-admin-server.ts @@ -1,13 +1,14 @@ +import { createServer } from "http" + import { applyMiddleware } from "graphql-middleware" -import { and, or, rule, shield } from "graphql-shield" -import { Rule, RuleAnd, RuleOr } from "graphql-shield/typings/rules" +import { or, rule, shield } from "graphql-shield" +import { Rule, RuleOr } from "graphql-shield/typings/rules" import { baseLogger } from "@services/logger" import { setupMongoConnection } from "@services/mongodb" import { adminMutationFields, adminQueryFields, gqlAdminSchema } from "@graphql/admin" import { ADMIN_CONFIG } from "@config" import { AuthenticationError, AuthorizationError } from "@graphql/error" -import { createServer } from "http" import express from "express" import { ApolloServerPluginDrainHttpServer } from "apollo-server-core" import { ApolloError, ApolloServer, ExpressContext } from "apollo-server-express" @@ -15,14 +16,19 @@ import { GraphQLError, GraphQLSchema } from "graphql" import PinoHttp from "pino-http" import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "./plugins/complexity" + import { parseUnknownDomainErrorFromUnknown } from "@domain/shared" -import healthzHandler from "./middlewares/healthz" -import { idempotencyMiddleware } from "./middlewares/idempotency" + import requestIp from "request-ip" + import jwt from "jsonwebtoken" + import { ErpNextRole, ErpNextRoles } from "@services/frappe/Roles" +import { createComplexityPlugin } from "./plugins/complexity" +import healthzHandler from "./middlewares/healthz" +import { idempotencyMiddleware } from "./middlewares/idempotency" + const graphqlLogger = baseLogger.child({ module: "graphql" }) interface JWTPayload { @@ -46,13 +52,12 @@ function parseAuthHeader(authHeader: string | undefined): JWTPayload { } } -export const hasRole = (role: ErpNextRole) => rule({ cache: "contextual" })(( - parent, - args, - ctx: GraphQLAdminContext, -) => { - return ctx.user.roles.includes(role) ? true : new AuthorizationError({ logger: graphqlLogger }) -}) +export const hasRole = (role: ErpNextRole) => + rule({ cache: "contextual" })((parent, args, ctx: GraphQLAdminContext) => { + return ctx.user.roles.includes(role) + ? true + : new AuthorizationError({ logger: graphqlLogger }) + }) // // const ipString = UNSECURE_IP_FROM_REQUEST_OBJECT // // ? req.ip @@ -218,15 +223,17 @@ const startAdminServer = async ({ } export async function startApolloServerForAdminSchema() { - const defaultRule = or(hasRole(ErpNextRoles.SystemManager), hasRole(ErpNextRoles.AccountsManager)) + const defaultRule = or( + hasRole(ErpNextRoles.SystemManager), + hasRole(ErpNextRoles.AccountsManager), + ) const authedQueryFields: { [key: string]: RuleOr } = {} for (const key of Object.keys(adminQueryFields.authed)) { authedQueryFields[key] = defaultRule } - - const mutationRoleOverrides: { [key: string]: Rule } = { + const mutationRoleOverrides: { [key: string]: Rule } = { // sendNotification: hasRole(ErpNextRoles.SystemManager) } diff --git a/src/servers/graphql-server.ts b/src/servers/graphql-server.ts index 6ddc84da6..b630d0cee 100644 --- a/src/servers/graphql-server.ts +++ b/src/servers/graphql-server.ts @@ -16,12 +16,12 @@ import { mapError } from "@graphql/error-map" import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity" -import { createComplexityPlugin } from "./plugins/complexity" - import jwksRsa from "jwks-rsa" import { parseUnknownDomainErrorFromUnknown } from "@domain/shared" +import { createComplexityPlugin } from "./plugins/complexity" + import authRouter from "./authorization" import kratosCallback from "./event-handlers/kratos" import healthzHandler from "./middlewares/healthz" diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index 51f4ec882..e5503fccf 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -4,18 +4,10 @@ */ import crypto from "crypto" + import { BridgeConfig } from "@config" -import { - BridgeCustomerId, - BridgeVirtualAccountId, - BridgeExternalAccountId, - BridgeTransferId, - toBridgeCustomerId, - toBridgeVirtualAccountId, - toBridgeExternalAccountId, - toBridgeTransferId, -} from "@domain/primitives/bridge" +import { BridgeCustomerId, BridgeTransferId } from "@domain/primitives/bridge" // ============ Error Handling ============ @@ -73,7 +65,16 @@ export type CreateCustomerRequest = export interface Customer { id: string type: "individual" | "business" - status?: "active" | "awaiting_questionnaire" | "rejected" | "paused" | "under_review" | "offboarded" | "awaiting_ubo" | "incomplete" | "not_started" + status?: + | "active" + | "awaiting_questionnaire" + | "rejected" + | "paused" + | "under_review" + | "offboarded" + | "awaiting_ubo" + | "incomplete" + | "not_started" has_accepted_terms_of_service?: string created_at: string updated_at: string @@ -90,18 +91,44 @@ export interface KycLink { } // Extended payment rails to include Tron -export type PaymentRail = "ach" | "wire" | "ach_push" | "ach_same_day" | "arbitrum" | "avalanche_c_chain" | "base" | "bre_b" | "co_bank_transfer" | "celo" | "ethereum" | "faster_payments" | "optimism" | "pix" | "polygon" | "sepa" | "solana" | "spei" | "stellar" | "swift" | "tempo" | "tron"; - -export type VirtualAccountDestinationPaymentRail = "arbitrum" | "avalanche_c_chain" | "base" | "celo" | "ethereum" | "optimism" | "polygon" | "solana" | "stellar" | "tempo" | "tron" - - -export type SourceCurrency = - | "usd" - | "eur" - | "mxn" - | "brl" - | "gbp" - | "cop" +export type PaymentRail = + | "ach" + | "wire" + | "ach_push" + | "ach_same_day" + | "arbitrum" + | "avalanche_c_chain" + | "base" + | "bre_b" + | "co_bank_transfer" + | "celo" + | "ethereum" + | "faster_payments" + | "optimism" + | "pix" + | "polygon" + | "sepa" + | "solana" + | "spei" + | "stellar" + | "swift" + | "tempo" + | "tron" + +export type VirtualAccountDestinationPaymentRail = + | "arbitrum" + | "avalanche_c_chain" + | "base" + | "celo" + | "ethereum" + | "optimism" + | "polygon" + | "solana" + | "stellar" + | "tempo" + | "tron" + +export type SourceCurrency = "usd" | "eur" | "mxn" | "brl" | "gbp" | "cop" // Extended currencies to include USDT export type Currency = "usdb" | "usdt" | "dai" | "pyusd" | "usdc" | "eurc" @@ -128,7 +155,7 @@ export interface CreateExternalAccountRequest { country: string } account_type: string | "us" | "iban" | "unknown" | "clabe" | "pix" | "gb" - currency: 'usd' | 'gbp' | 'brl' | 'eur' | string + currency: "usd" | "gbp" | "brl" | "eur" | string account: { account_number: string routing_number: string @@ -185,7 +212,19 @@ export interface ListResponse { cursor?: string } -export type TrasfertSourceCurrency = "brl" | "cop" | "dai" | "eur" | "eurc" | "gbp" | "mxn" | "pyusd" | "usd" | "usdb" | "usdc" | "usdt" +export type TrasfertSourceCurrency = + | "brl" + | "cop" + | "dai" + | "eur" + | "eurc" + | "gbp" + | "mxn" + | "pyusd" + | "usd" + | "usdb" + | "usdc" + | "usdt" export interface CreateTransferRequest { amount?: string currency?: string @@ -252,14 +291,17 @@ export interface Transfer { updated_at: string } - export interface BridgeIntiateKyc { email: string type: "individual" | "business" full_name?: string } -export type BridgeWebhookEventType = "kyc" | "transfer" | "virtual_account" | "external_account" +export type BridgeWebhookEventType = + | "kyc" + | "transfer" + | "virtual_account" + | "external_account" export interface BridgeWebhookEvent { id: string @@ -291,7 +333,6 @@ type WebhookEventsApiResponse = { cursor?: string } - // ============ Bridge Client ============ export class BridgeClient { @@ -324,7 +365,6 @@ export class BridgeClient { } } - const response = await fetch(url, { method, headers, @@ -359,7 +399,10 @@ export class BridgeClient { // ============ KYC ============ - async createKycLink(request: BridgeIntiateKyc, idempotencyKey?: string): Promise { + async createKycLink( + request: BridgeIntiateKyc, + idempotencyKey?: string, + ): Promise { return this.request("POST", "/kyc_links", request, idempotencyKey) } @@ -393,7 +436,8 @@ export class BridgeClient { "POST", `/customers/${customerId}/external_accounts`, data, - idempotencyKey); + idempotencyKey, + ) } async getExternalAccountLinkUrl( @@ -437,7 +481,6 @@ export class BridgeClient { return this.request("GET", `/transfers/${transferId}`) } - // ============ List Events ============ async listEvents(params?: ListEventsParams): Promise> { @@ -478,7 +521,6 @@ export class BridgeClient { cursor, } } - } export default new BridgeClient() @@ -498,4 +540,4 @@ export async function* listAllEvents( cursor = page.has_more ? page.cursor : undefined } while (cursor) -} \ No newline at end of file +} diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 69340d4b0..6f6572b0c 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -64,7 +64,9 @@ export class BridgeBelowMinimumWithdrawalError extends BridgeError { } export class BridgeInvalidAmountError extends BridgeError { - constructor(message: string = "Amount must be strictly positive with at most 6 decimal places") { + constructor( + message: string = "Amount must be strictly positive with at most 6 decimal places", + ) { super(message) } } diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index fb308bdbd..ecd4891c2 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -7,17 +7,23 @@ import crypto from "crypto" import { BridgeConfig } from "@config" -import BridgeClient, { - KycLink, - VirtualAccount, - ExternalAccountLinkUrl, - Transfer, -} from "./client" + import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { AccountsRepository } from "@services/mongoose/accounts" import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" + +import { RepositoryError } from "@domain/errors" +import { toBridgeCustomerId } from "@domain/primitives/bridge" +import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import { USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletsRepository } from "@services/mongoose/wallets" + +import { IdentityRepository } from "@services/kratos" +import IbexClient from "@services/ibex/client" + import { + BridgeInsufficientFundsError, BridgeError, BridgeDisabledError, BridgeAccountLevelError, @@ -25,14 +31,7 @@ import { BridgeKycRejectedError, BridgeCustomerNotFoundError, } from "./errors" -import { RepositoryError } from "@domain/errors" -import { toBridgeCustomerId, toBridgeExternalAccountId } from "@domain/primitives/bridge" -import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" -import { USDTAmount, WalletCurrency } from "@domain/shared" -import { WalletsRepository } from "@services/mongoose/wallets" -import { BridgeInsufficientFundsError } from "./errors" -import { IdentityRepository } from "@services/kratos" -import IbexClient from "@services/ibex/client" +import BridgeApiClient from "./client" // ============ Types ============ @@ -94,7 +93,6 @@ export const deriveWithdrawalIdempotencyKey = (rowId: string): string => // ============ Guards ============ - const checkBridgeEnabled = (): true | BridgeDisabledError => { if (!BridgeConfig.enabled) { return new BridgeDisabledError() @@ -120,7 +118,17 @@ const checkAccountLevel = async ( * - Creates Bridge customer if not exists * - Returns KYC and TOS links */ -const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: AccountId, email: string, type?: "individual" | "business", full_name: string }): Promise => { +const initiateKyc = async ({ + accountId, + email, + type, + full_name, +}: { + accountId: AccountId + email: string + type?: "individual" | "business" + full_name: string +}): Promise => { baseLogger.info({ accountId, operation: "initiateKyc" }, "Bridge operation started") const enabledCheck = checkBridgeEnabled() @@ -133,19 +141,18 @@ const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: A return new BridgeError("KYC already approved for this account") } - const identity = await IdentityRepository().getIdentity(account.kratosUserId); + const identity = await IdentityRepository().getIdentity(account.kratosUserId) if (identity instanceof Error) return identity - const useremail = identity.email; + const useremail = identity.email try { - // Create KYC link - const kycLink = await BridgeClient.createKycLink({ + const kycLink = await BridgeApiClient.createKycLink({ email: useremail || email, type: type || "individual", - full_name: full_name || account.username + full_name: full_name || account.username, }) const result: InitiateKycResult = { @@ -154,12 +161,12 @@ const initiateKyc = async ({ accountId, email, type, full_name }: { accountId: A tosLink: kycLink.tos_link, } - // link the customer Id to the bridge account - const customerId = toBridgeCustomerId(kycLink.customer_id); + // link the customer Id to the bridge account + const customerId = toBridgeCustomerId(kycLink.customer_id) const updateResult = await AccountsRepository().updateBridgeFields(accountId, { bridgeCustomerId: customerId, - bridgeKycStatus: "pending" + bridgeKycStatus: "pending", }) if (updateResult instanceof Error) { @@ -235,7 +242,8 @@ const createVirtualAccount = async ( } // Get or create Ethereum address - let ethereumAddress = account.bridgeEthereumAddress || "0xaF095D35bfDd462165eA7eCF8AC75351a93d72bD" + let ethereumAddress = + account.bridgeEthereumAddress || "0xaF095D35bfDd462165eA7eCF8AC75351a93d72bD" if (!ethereumAddress) { const option = await IbexClient.getEthereumUsdtOption() @@ -263,7 +271,7 @@ const createVirtualAccount = async ( .digest("hex") // Create Bridge virtual account - const virtualAccount = await BridgeClient.createVirtualAccount( + const virtualAccount = await BridgeApiClient.createVirtualAccount( customerId, { source: { currency: "usd" }, @@ -276,7 +284,8 @@ const createVirtualAccount = async ( vaIdempotencyKey, ) - const fullAccountNumber = virtualAccount.source_deposit_instructions.bank_account_number || "" + const fullAccountNumber = + virtualAccount.source_deposit_instructions.bank_account_number || "" // Store virtual account in repository const repoResult = await BridgeAccountsRepo.createVirtualAccount({ @@ -341,7 +350,7 @@ const addExternalAccount = async ( ) } - const linkUrl = await BridgeClient.getExternalAccountLinkUrl(customerId) + const linkUrl = await BridgeApiClient.getExternalAccountLinkUrl(customerId) const result: AddExternalAccountResult = { linkUrl: linkUrl.link_url, @@ -419,7 +428,12 @@ const initiateWithdrawal = async ( const availableBalance = balance.toIbex() if (availableBalance < withdrawalAmount) { baseLogger.warn( - { accountId, availableBalance, withdrawalAmount, operation: "initiateWithdrawal" }, + { + accountId, + availableBalance, + withdrawalAmount, + operation: "initiateWithdrawal", + }, "Insufficient USDT balance for withdrawal", ) return new BridgeInsufficientFundsError( @@ -445,11 +459,12 @@ const initiateWithdrawal = async ( return new Error("External account is not verified") } - const existingWithdrawal = await BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer( - accountId as string, - externalAccountId, - amount, - ) + const existingWithdrawal = + await BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer( + accountId as string, + externalAccountId, + amount, + ) if (existingWithdrawal instanceof Error) return existingWithdrawal // Store withdrawal record, or reuse the in-flight row for a retry of the same request. @@ -467,20 +482,24 @@ const initiateWithdrawal = async ( const idempotencyKey = deriveWithdrawalIdempotencyKey(pendingWithdrawal.id) // Create transfer via Bridge - const transfer = await BridgeClient.createTransfer(customerId, { - amount, - on_behalf_of: customerId, - source: { - payment_rail: "ethereum", - currency: "usdt", - from_address: ethereumAddress, - }, - destination: { - payment_rail: "ach", - currency: "usd", - external_account_id: externalAccountId, + const transfer = await BridgeApiClient.createTransfer( + customerId, + { + amount, + on_behalf_of: customerId, + source: { + payment_rail: "ethereum", + currency: "usdt", + from_address: ethereumAddress, + }, + destination: { + payment_rail: "ach", + currency: "usd", + external_account_id: externalAccountId, + }, }, - }, idempotencyKey) + idempotencyKey, + ) const result: InitiateWithdrawalResult = { transferId: transfer.id, diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts index 22e75154c..2cf97522a 100644 --- a/src/services/bridge/webhook-server/index.ts +++ b/src/services/bridge/webhook-server/index.ts @@ -9,20 +9,27 @@ import express from "express" import { BridgeConfig } from "@config" import { baseLogger } from "@services/logger" + import { verifyBridgeSignature } from "./middleware/verify-signature" import { kycHandler } from "./routes/kyc" import { depositHandler } from "./routes/deposit" import { transferHandler } from "./routes/transfer" import { replayAuthMiddleware, replayHandler } from "./routes/replay" +type RawBodyRequest = express.Request & { rawBody?: string } + export const startBridgeWebhookServer = () => { const app = express() // Middleware - MUST capture raw body for signature verification app.use( express.json({ - verify: (req: any, res, buf) => { - req.rawBody = buf.toString("utf8") + verify: (req, res, buf) => { + if (res.writableEnded) { + return + } + const rawReq = req as RawBodyRequest + rawReq.rawBody = buf.toString("utf8") }, }), ) @@ -39,7 +46,9 @@ export const startBridgeWebhookServer = () => { app.post("/internal/replay", replayAuthMiddleware, replayHandler) if (!BridgeConfig.webhook.replaySecret && !process.env.BRIDGE_WEBHOOK_REPLAY_SECRET) { - baseLogger.warn("replaySecret not configured (neither BridgeConfig.webhook.replaySecret nor BRIDGE_WEBHOOK_REPLAY_SECRET) — /internal/replay will reject all requests with 503") + baseLogger.warn( + "replaySecret not configured (neither BridgeConfig.webhook.replaySecret nor BRIDGE_WEBHOOK_REPLAY_SECRET) — /internal/replay will reject all requests with 503", + ) } // Start server diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts index c4a100ecb..03b75494f 100644 --- a/src/services/bridge/webhook-server/middleware/verify-signature.ts +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -6,11 +6,14 @@ * Signature is computed over: . */ -import { Request, Response, NextFunction } from "express" import crypto from "crypto" + +import { Request, Response, NextFunction } from "express" import { BridgeConfig } from "@config" import { baseLogger } from "@services/logger" +type RawBodyRequest = Request & { rawBody?: string } + export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer") => { return (req: Request, res: Response, next: NextFunction) => { const signature = req.headers["x-webhook-signature"] as string @@ -37,7 +40,10 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf const now = Date.now() const timestampMs = parseInt(timestamp, 10) if (isNaN(timestampMs) || !isFinite(timestampMs)) { - baseLogger.warn({ timestamp }, "Invalid timestamp format in Bridge webhook signature") + baseLogger.warn( + { timestamp }, + "Invalid timestamp format in Bridge webhook signature", + ) return res.status(401).json({ error: "Invalid signature format" }) } @@ -50,7 +56,7 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf // Verify signature using Bridge public key const publicKey = BridgeConfig.webhook.publicKeys[publicKeyType] - const rawBody = (req as any).rawBody + const rawBody = (req as RawBodyRequest).rawBody if (!rawBody) { baseLogger.warn(`Missing raw body for webhook`) @@ -60,7 +66,10 @@ export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transf const signedPayload = `${timestamp}.${rawBody}` const digest = crypto.createHash("sha256").update(signedPayload).digest() - baseLogger.debug({ signedPayload, digest: digest.toString("hex") }, "Verifying Bridge webhook signature") + baseLogger.debug( + { signedPayload, digest: digest.toString("hex") }, + "Verifying Bridge webhook signature", + ) try { const verifier = crypto.createVerify("RSA-SHA256") diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts index 7a8783742..2706b7499 100644 --- a/src/services/bridge/webhook-server/routes/kyc.ts +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -11,7 +11,6 @@ import { toBridgeCustomerId } from "@domain/primitives/bridge" import BridgeService from "@services/bridge" export const kycHandler = async (req: Request, res: Response) => { - const { event_id, event_object } = req.body const { customer_id, kyc_status, rejection_reasons } = event_object @@ -21,7 +20,7 @@ export const kycHandler = async (req: Request, res: Response) => { // Idempotency check using customer_id + event as lock key const lockKey = `bridge-kyc:${customer_id}:${event_object.kyc_status}:${event_object.id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") return res.status(200).json({ status: "already_processed" }) @@ -31,7 +30,10 @@ export const kycHandler = async (req: Request, res: Response) => { const bridgeCustomerId = toBridgeCustomerId(customer_id) const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) if (account instanceof Error) { - baseLogger.warn({ customer_id }, "Account not found for Bridge customer — may be a timing issue, Bridge will retry") + baseLogger.warn( + { customer_id }, + "Account not found for Bridge customer — may be a timing issue, Bridge will retry", + ) return res.status(503).json({ error: "Account not ready" }) } diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index e08b8fa10..0863a074b 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -19,7 +19,7 @@ export const transferHandler = async (req: Request, res: Response) => { // Idempotency check using transfer_id as lock key const lockKey = `bridge-transfer:${transfer_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as any) + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) if (lockResult instanceof Error) { baseLogger.info({ transfer_id }, "Duplicate Bridge transfer webhook") return res.status(200).json({ status: "already_processed" }) diff --git a/src/services/ibex/webhook-server/index.ts b/src/services/ibex/webhook-server/index.ts index 0bd7aa7f8..b190516c4 100644 --- a/src/services/ibex/webhook-server/index.ts +++ b/src/services/ibex/webhook-server/index.ts @@ -1,6 +1,7 @@ import express, { Request, Response } from "express" import { IbexConfig } from "@config" import { baseLogger as logger } from "@services/logger" + import { onPay, onReceive, cryptoReceive } from "./routes" const start = () => { diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 9a8842bcf..853a7a93b 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -82,7 +82,6 @@ export const AccountsRepository = (): IAccountsRepository => { if (!result) { return new CouldNotFindAccountFromUsernameError(npub) } - let account = translateToAccount(result) return translateToAccount(result) } catch (err) { return parseRepositoryError(err) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 6877d092e..1828e3650 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -1,4 +1,3 @@ -import { BridgeVirtualAccount, BridgeExternalAccount, BridgeWithdrawal } from "./schema" import { BridgeVirtualAccountId, BridgeExternalAccountId, @@ -6,6 +5,8 @@ import { } from "@domain/primitives/bridge" import { RepositoryError } from "@domain/errors" +import { BridgeVirtualAccount, BridgeExternalAccount, BridgeWithdrawal } from "./schema" + // ============ Virtual Accounts ============ export const createVirtualAccount = async (data: { @@ -123,7 +124,7 @@ export const findPendingWithdrawalWithoutTransfer = async ( bridgeTransferId: { $exists: false }, status: "pending", }) - return record // null when no in-flight row exists + return record // null when no in-flight row exists } catch (error) { return new RepositoryError(String(error)) } diff --git a/src/services/mongoose/bridge-replay-log.ts b/src/services/mongoose/bridge-replay-log.ts index 5782471cf..d1842be8c 100644 --- a/src/services/mongoose/bridge-replay-log.ts +++ b/src/services/mongoose/bridge-replay-log.ts @@ -1,42 +1,42 @@ import { BridgeReplayLog } from "./schema" export const createBridgeReplayLog = async (data: { - eventId: string - eventType: string, - eventPayload: Record, - bridgeEventCreatedAt: Date, - replayedAt: Date, - operator: string, - timeWindowStart: Date, - timeWindowEnd: Date, - httpStatus: number, - httpResponse: Record, - dryRun?: boolean + eventId: string + eventType: string + eventPayload: Record + bridgeEventCreatedAt: Date + replayedAt: Date + operator: string + timeWindowStart: Date + timeWindowEnd: Date + httpStatus: number + httpResponse: Record + dryRun?: boolean }): Promise<{ id: string } | Error> => { + try { + const log = await BridgeReplayLog.create(data) - try { - const log = await BridgeReplayLog.create(data) - - return { id: log._id.toString() } - - } catch (error) { - return error instanceof Error ? error : new Error(String(error)) - } + return { id: log._id.toString() } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } } - export const findBridgeReplayLogs = async (filter?: { - eventType?: string - dryRun?: boolean - limit?: number + eventType?: string + dryRun?: boolean + limit?: number }): Promise => { - try { - const queryFilter: Record = {} - if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType - if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun + try { + const queryFilter: Record = {} + if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType + if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun - return await BridgeReplayLog.find(queryFilter).sort({ replayedAt: -1 }).limit(filter?.limit ?? 100).lean() - } catch (error) { - return error instanceof Error ? error : new Error(String(error)) - } + return await BridgeReplayLog.find(queryFilter) + .sort({ replayedAt: -1 }) + .limit(filter?.limit ?? 100) + .lean() + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } } diff --git a/src/services/mongoose/wallets.ts b/src/services/mongoose/wallets.ts index 300be6fbd..0a2da5d61 100644 --- a/src/services/mongoose/wallets.ts +++ b/src/services/mongoose/wallets.ts @@ -1,3 +1,5 @@ +import { randomUUID } from "crypto" + import { CouldNotFindWalletFromIdError, CouldNotFindWalletFromOnChainAddressError, @@ -9,7 +11,6 @@ import { UnsupportedCurrencyError, } from "@domain/errors" import { Types } from "mongoose" -import { randomUUID } from "crypto" // FLASH FORK: import IBEX routes and helper import Ibex from "@services/ibex/client" @@ -20,10 +21,11 @@ import { recordExceptionInCurrentSpan } from "@services/tracing" import { ErrorLevel, USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + import { toObjectId, fromObjectId, parseRepositoryError } from "./utils" import { Wallet } from "./schema" import { AccountsRepository } from "./accounts" -import { WalletType } from "@domain/wallets" export interface WalletRecord { id: string diff --git a/test/flash/unit/services/bridge/client.spec.ts b/test/flash/unit/services/bridge/client.spec.ts index a4fc194f5..ef9c4c203 100644 --- a/test/flash/unit/services/bridge/client.spec.ts +++ b/test/flash/unit/services/bridge/client.spec.ts @@ -78,11 +78,23 @@ describe("listAllEvents", () => { it("passes the cursor from page N as 'after' on page N+1", async () => { listEventsSpy - .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "cur-abc" }) - .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + .mockResolvedValueOnce({ + data: [makeEvent("e1")], + has_more: true, + cursor: "cur-abc", + }) + .mockResolvedValueOnce({ + data: [makeEvent("e2")], + has_more: false, + cursor: undefined, + }) - for await (const _ of listAllEvents()) { /* drain */ } + const drained: BridgeWebhookEvent[] = [] + for await (const event of listAllEvents()) { + drained.push(event) + } + expect(drained).toHaveLength(2) expect(listEventsSpy).toHaveBeenNthCalledWith( 1, expect.objectContaining({ after: undefined }), @@ -96,8 +108,12 @@ describe("listAllEvents", () => { it("always requests page_size 100", async () => { listEventsSpy.mockResolvedValue({ data: [], has_more: false, cursor: undefined }) - for await (const _ of listAllEvents()) { /* drain */ } + const drained: BridgeWebhookEvent[] = [] + for await (const event of listAllEvents()) { + drained.push(event) + } + expect(drained).toHaveLength(0) expect(listEventsSpy).toHaveBeenCalledWith( expect.objectContaining({ page_size: 100 }), ) @@ -106,7 +122,11 @@ describe("listAllEvents", () => { it("forwards start_date, end_date and event_type filters to every page", async () => { listEventsSpy .mockResolvedValueOnce({ data: [makeEvent("e1")], has_more: true, cursor: "c1" }) - .mockResolvedValueOnce({ data: [makeEvent("e2")], has_more: false, cursor: undefined }) + .mockResolvedValueOnce({ + data: [makeEvent("e2")], + has_more: false, + cursor: undefined, + }) const params = { start_date: "2026-05-01T00:00:00Z", @@ -114,8 +134,12 @@ describe("listAllEvents", () => { event_type: "transfer.completed", } - for await (const _ of listAllEvents(params)) { /* drain */ } + const drained: BridgeWebhookEvent[] = [] + for await (const event of listAllEvents(params)) { + drained.push(event) + } + expect(drained).toHaveLength(2) expect(listEventsSpy).toHaveBeenCalledTimes(2) for (const call of listEventsSpy.mock.calls) { expect(call[0]).toMatchObject(params) diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 4324021d2..635200766 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -67,7 +67,9 @@ jest.mock("@domain/primitives/bridge", () => ({ // `instanceof USDTAmount` guard is satisfied without breaking other domain exports. jest.mock("@domain/shared", () => { class USDTAmount { - constructor(private readonly ibexValue: number) {} + constructor(private readonly ibexValue: number) { + // Parameter property initializes ibexValue. + } toIbex() { return this.ibexValue } @@ -120,15 +122,18 @@ const mockTransfer = { // ── Helpers ─────────────────────────────────────────────────────────────────── const setupGuards = () => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const { USDTAmount } = require("@domain/shared") + const { USDTAmount } = jest.requireMock("@domain/shared") as { + USDTAmount: new (ibexValue: number) => { toIbex: () => number } + } const balance = new USDTAmount(1000) // 1000 USDT — well above minWithdrawalAmount ;(AccountsRepository as jest.Mock).mockReturnValue({ findById: jest.fn().mockResolvedValue(mockAccount), }) ;(WalletsRepository as jest.Mock).mockReturnValue({ - listByAccountId: jest.fn().mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), + listByAccountId: jest + .fn() + .mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), }) ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ @@ -144,7 +149,7 @@ const setupGuards = () => { // ── Tests ───────────────────────────────────────────────────────────────────── describe("deriveWithdrawalIdempotencyKey", () => { - it("returns sha256(\"withdrawal:\") as a hex string", () => { + it('returns sha256("withdrawal:") as a hex string', () => { const rowId = "507f1f77bcf86cd799439011" const expected = crypto .createHash("sha256") @@ -162,7 +167,9 @@ describe("deriveWithdrawalIdempotencyKey", () => { it("is deterministic — same input always returns same output", () => { const rowId = "507f1f77bcf86cd799439011" - expect(deriveWithdrawalIdempotencyKey(rowId)).toBe(deriveWithdrawalIdempotencyKey(rowId)) + expect(deriveWithdrawalIdempotencyKey(rowId)).toBe( + deriveWithdrawalIdempotencyKey(rowId), + ) }) it("output is a 64-character lowercase hex string (sha256)", () => { @@ -180,25 +187,28 @@ describe("initiateWithdrawal — idempotency key wiring", () => { describe("fresh request (no in-flight row)", () => { it("creates a pending withdrawal row before calling Bridge", async () => { const row = makeRow("fresh-row-001") - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - null, - ) + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(null) ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) const createOrder = (BridgeAccountsRepo.createWithdrawal as jest.Mock).mock .invocationCallOrder[0] - const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock.invocationCallOrder[0] + const transferOrder = (BridgeClient.createTransfer as jest.Mock).mock + .invocationCallOrder[0] expect(createOrder).toBeLessThan(transferOrder) }) it("derives the idempotency key from the newly created row's id", async () => { const rowId = "fresh-row-id-abc" - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - null, + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(null) + ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue( + makeRow(rowId), ) - ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(makeRow(rowId)) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) @@ -214,9 +224,9 @@ describe("initiateWithdrawal — idempotency key wiring", () => { describe("retry — in-flight row already exists", () => { it("does not create a second withdrawal row", async () => { const existingRow = makeRow("existing-row-001") - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - existingRow, - ) + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(existingRow) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) @@ -225,9 +235,9 @@ describe("initiateWithdrawal — idempotency key wiring", () => { it("derives the key from the existing row's id — identical to the first attempt's key", async () => { const rowId = "existing-row-001" - ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock).mockResolvedValue( - makeRow(rowId), - ) + ;( + BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock + ).mockResolvedValue(makeRow(rowId)) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) @@ -249,8 +259,7 @@ describe("initiateWithdrawal — idempotency key wiring", () => { ;(BridgeAccountsRepo.findPendingWithdrawalWithoutTransfer as jest.Mock) .mockResolvedValueOnce(null) // call 1: nothing in-flight yet - .mockResolvedValueOnce(row) // call 2: row A now visible - + .mockResolvedValueOnce(row) // call 2: row A now visible ;(BridgeAccountsRepo.createWithdrawal as jest.Mock).mockResolvedValue(row) await BridgeService.initiateWithdrawal(ACCOUNT_ID, AMOUNT, EXTERNAL_ACCOUNT_ID) diff --git a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts index fe2756e3d..961b1f24c 100644 --- a/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/deposit.spec.ts @@ -26,8 +26,7 @@ const makeRes = () => { return res } -const makeReq = (body: Record) => - ({ body } as unknown as Request) +const makeReq = (body: Record) => ({ body }) as unknown as Request // Real Bridge deposit event shape const VALID_EVENT_OBJECT = { @@ -85,7 +84,8 @@ beforeEach(() => { describe("depositHandler — invalid payload", () => { it("returns 400 when event_id is missing", async () => { const res = makeRes() - const { event_id: _, ...body } = VALID_BODY + const body = { ...VALID_BODY } + delete body.event_id await depositHandler(makeReq(body), res) expect(res.status as jest.Mock).toHaveBeenCalledWith(400) expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() @@ -93,7 +93,8 @@ describe("depositHandler — invalid payload", () => { it("returns 400 when event_object.id is missing", async () => { const res = makeRes() - const { id: _, ...objWithoutId } = VALID_EVENT_OBJECT + const objWithoutId = { ...VALID_EVENT_OBJECT } + delete objWithoutId.id await depositHandler(makeReq({ ...VALID_BODY, event_object: objWithoutId }), res) expect(res.status as jest.Mock).toHaveBeenCalledWith(400) expect(DepositLog.createBridgeDepositLog).not.toHaveBeenCalled() diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts index 32d6e7302..ea6c9610b 100644 --- a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -1,5 +1,3 @@ -import crypto from "crypto" - // AC1: Orphan event surfaces in ops tooling with triage context // AC2: Replay CLI re-runs a stuck handler against a chosen transfer-id @@ -28,7 +26,10 @@ jest.mock("@services/bridge/webhook-server/routes/transfer", () => ({ })) import { Request, Response } from "express" -import { replayAuthMiddleware, replayHandler } from "@services/bridge/webhook-server/routes/replay" +import { + replayAuthMiddleware, + replayHandler, +} from "@services/bridge/webhook-server/routes/replay" import * as ReplayLog from "@services/mongoose/bridge-replay-log" import { depositHandler } from "@services/bridge/webhook-server/routes/deposit" import { kycHandler } from "@services/bridge/webhook-server/routes/kyc" @@ -43,8 +44,10 @@ const makeRes = () => { return res } -const makeReq = (body: Record = {}, headers: Record = {}) => - ({ body, headers } as unknown as Request) +const makeReq = ( + body: Record = {}, + headers: Record = {}, +) => ({ body, headers }) as unknown as Request const BASE_BODY = { event_type: "funds_received", @@ -69,7 +72,7 @@ describe("replayAuthMiddleware", () => { const next = jest.fn() replayAuthMiddleware(makeReq({}, {}), res, next) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(503) + expect(res.status as jest.Mock).toHaveBeenCalledWith(503) expect(next).not.toHaveBeenCalled() BridgeConfig.webhook.replaySecret = saved @@ -78,13 +81,9 @@ describe("replayAuthMiddleware", () => { it("returns 401 for a wrong token", () => { const res = makeRes() const next = jest.fn() - replayAuthMiddleware( - makeReq({}, { authorization: "Bearer wrong-token" }), - res, - next, - ) + replayAuthMiddleware(makeReq({}, { authorization: "Bearer wrong-token" }), res, next) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) expect(next).not.toHaveBeenCalled() }) @@ -93,7 +92,7 @@ describe("replayAuthMiddleware", () => { const next = jest.fn() replayAuthMiddleware(makeReq({}, {}), res, next) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) expect(next).not.toHaveBeenCalled() }) @@ -107,7 +106,7 @@ describe("replayAuthMiddleware", () => { ) expect(next).toHaveBeenCalledTimes(1) - expect((res.status as jest.Mock)).not.toHaveBeenCalled() + expect(res.status as jest.Mock).not.toHaveBeenCalled() }) it("uses timing-safe comparison (different-length token is rejected)", () => { @@ -120,7 +119,7 @@ describe("replayAuthMiddleware", () => { next, ) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(401) + expect(res.status as jest.Mock).toHaveBeenCalledWith(401) expect(next).not.toHaveBeenCalled() }) }) @@ -133,64 +132,72 @@ describe("replayHandler", () => { describe("input validation", () => { it("returns 400 when event_type is missing", async () => { const res = makeRes() - const { event_type: _et, ...body } = BASE_BODY + const body = { ...BASE_BODY } + delete body.event_type await replayHandler(makeReq(body), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) it("returns 400 when event_object is missing", async () => { const res = makeRes() - const { event_object: _eo, ...body } = BASE_BODY + const body = { ...BASE_BODY } + delete body.event_object await replayHandler(makeReq(body), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) it("returns 400 when event_created_at is missing", async () => { const res = makeRes() - const { event_created_at: _ec, ...body } = BASE_BODY + const body = { ...BASE_BODY } + delete body.event_created_at await replayHandler(makeReq(body), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) it("returns 400 for an unrecognised event_type", async () => { const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, event_type: "unknown_event" }), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(400) + expect(res.status as jest.Mock).toHaveBeenCalledWith(400) }) }) describe("event_type → handler routing", () => { const cases: Array<[string, jest.Mock]> = [ - ["funds_received", depositHandler as jest.Mock], + ["funds_received", depositHandler as jest.Mock], ["funds_scheduled", depositHandler as jest.Mock], ["payment_processed", depositHandler as jest.Mock], - ["kyc.approved", kycHandler as jest.Mock], - ["kyc.rejected", kycHandler as jest.Mock], + ["kyc.approved", kycHandler as jest.Mock], + ["kyc.rejected", kycHandler as jest.Mock], ["transfer.completed", transferHandler as jest.Mock], - ["transfer.failed", transferHandler as jest.Mock], + ["transfer.failed", transferHandler as jest.Mock], ] beforeEach(() => { ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) }) - test.each(cases)("%s is routed to the correct handler", async (eventType, handler) => { - handler.mockImplementation((_req: Request, res: Response) => { - ;(res.status as jest.Mock)(200) - ;(res.json as jest.Mock)({ status: "success" }) - return Promise.resolve(res) - }) + test.each(cases)( + "%s is routed to the correct handler", + async (eventType, handler) => { + handler.mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) - const res = makeRes() - await replayHandler(makeReq({ ...BASE_BODY, event_type: eventType }), res) + const res = makeRes() + await replayHandler(makeReq({ ...BASE_BODY, event_type: eventType }), res) - expect(handler).toHaveBeenCalledTimes(1) - }) + expect(handler).toHaveBeenCalledTimes(1) + }, + ) }) describe("dry_run mode", () => { it("returns 200 without calling any handler", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-001" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-dry-001", + }) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) @@ -198,11 +205,13 @@ describe("replayHandler", () => { expect(depositHandler).not.toHaveBeenCalled() expect(kycHandler).not.toHaveBeenCalled() expect(transferHandler).not.toHaveBeenCalled() - expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) }) it("persists a dry-run log entry with httpStatus 0", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-dry-002" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-dry-002", + }) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) @@ -213,37 +222,45 @@ describe("replayHandler", () => { }) it("returns 500 when dry-run log creation fails", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("db error")) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue( + new Error("db error"), + ) const res = makeRes() await replayHandler(makeReq({ ...BASE_BODY, dry_run: true }), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) }) }) describe("live replay", () => { beforeEach(() => { - ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { - ;(res.status as jest.Mock)(200) - ;(res.json as jest.Mock)({ status: "success" }) - return Promise.resolve(res) - }) + ;(depositHandler as jest.Mock).mockImplementation( + (_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }, + ) }) it("returns the handler's status code and response body", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-001" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-live-001", + }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(200) + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) const jsonArg = (res.json as jest.Mock).mock.calls[0][0] expect(jsonArg).toMatchObject({ status: "replayed", handler_status: 200 }) }) it("persists a replay log with triage context (operator + time window)", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-live-002" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-live-002", + }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) @@ -261,7 +278,9 @@ describe("replayHandler", () => { }) it("includes log_id in the response so ops can trace the replay", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-trace-007" }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ + id: "log-trace-007", + }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) @@ -271,26 +290,30 @@ describe("replayHandler", () => { }) it("returns 500 when log creation fails after a successful handler run", async () => { - ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue(new Error("mongo down")) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue( + new Error("mongo down"), + ) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(500) + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) }) it("propagates a handler 4xx back to the caller", async () => { - ;(depositHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { - ;(res.status as jest.Mock)(422) - ;(res.json as jest.Mock)({ error: "Unprocessable" }) - return Promise.resolve(res) - }) + ;(depositHandler as jest.Mock).mockImplementation( + (_req: Request, res: Response) => { + ;(res.status as jest.Mock)(422) + ;(res.json as jest.Mock)({ error: "Unprocessable" }) + return Promise.resolve(res) + }, + ) ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-4xx" }) const res = makeRes() await replayHandler(makeReq(BASE_BODY), res) - expect((res.status as jest.Mock)).toHaveBeenCalledWith(422) + expect(res.status as jest.Mock).toHaveBeenCalledWith(422) }) }) }) From 04c4dd9dcee8b6ed71a32bd77e17dbfc83080bd8 Mon Sep 17 00:00:00 2001 From: Vandana Date: Wed, 13 May 2026 18:31:11 -0400 Subject: [PATCH 08/22] ENG-297 fix(graphql): add isExternal to UsdtWallet --- dev/apollo-federation/supergraph.graphql | 1 + src/graphql/public/schema.graphql | 1 + src/graphql/shared/types/object/usdt-wallet.ts | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 7f2bcb3a7..7af42dc4d 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -2041,6 +2041,7 @@ type UsdtWallet implements Wallet accountId: ID! balance: FractionalCentAmount! id: ID! + isExternal: Boolean! lnurlp: Lnurl """An unconfirmed incoming onchain balance.""" diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index a6e0a50a1..3794cf539 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1655,6 +1655,7 @@ type UsdtWallet implements Wallet { accountId: ID! balance: FractionalCentAmount! id: ID! + isExternal: Boolean! lnurlp: Lnurl """An unconfirmed incoming onchain balance.""" diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 59139203e..046e21bb7 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -14,6 +14,7 @@ import { WalletCurrency as WalletCurrencyDomain, USDTAmount, } from "@domain/shared" +import { WalletType } from "@domain/wallets" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" @@ -49,6 +50,10 @@ const UsdtWallet = GT.Object({ type: Lnurl, resolve: (source) => source.lnurlp, }, + isExternal: { + type: GT.NonNull(GT.Boolean), + resolve: (source) => source.type === WalletType.External, + }, balance: { type: GT.NonNull(FractionalCentAmount), From 9c8b8404d781c4577eb7a0051f2e4a8b50b05326 Mon Sep 17 00:00:00 2001 From: Vandana Date: Fri, 15 May 2026 08:46:48 -0400 Subject: [PATCH 09/22] fix(graphql): preserve latest account upgrade request query --- dev/apollo-federation/supergraph.graphql | 2 +- src/graphql/public/queries.ts | 4 ++-- src/graphql/public/schema.graphql | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 7af42dc4d..8c6041023 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -1644,7 +1644,7 @@ type Query @join__type(graph: PUBLIC) { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - accountUpgradeRequest: AccountUpgradeRequestPayload! + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 4596d6e48..8f8e7f46f 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -20,7 +20,7 @@ import LnInvoicePaymentStatusQuery from "@graphql/public/root/query/ln-invoice-p import NpubByUserNameQuery from "./root/query/username-npub-query" import IsFlashNpubQuery from "./root/query/is-flash-npub-query" import TransactionDetailsQuery from "./root/query/transaction-details" -import AccountUpgradeRequestQuery from "./root/query/account-upgrade-request" +import LatestAccountUpgradeRequestQuery from "./root/query/account-upgrade-request" import SupportedBanksQuery from "./root/query/supported-banks" import BridgeKycStatusQuery from "./root/query/bridge-kyc-status" import BridgeVirtualAccountQuery from "./root/query/bridge-virtual-account" @@ -49,7 +49,7 @@ export const queryFields = { atAccountLevel: { me: MeQuery, transactionDetails: TransactionDetailsQuery, - accountUpgradeRequest: AccountUpgradeRequestQuery, + latestAccountUpgradeRequest: LatestAccountUpgradeRequestQuery, bridgeKycStatus: BridgeKycStatusQuery, bridgeVirtualAccount: BridgeVirtualAccountQuery, bridgeExternalAccounts: BridgeExternalAccountsQuery, diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 3794cf539..b5d7f3c79 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1288,7 +1288,7 @@ type PublicWallet { type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - accountUpgradeRequest: AccountUpgradeRequestPayload! + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount From 9aed1f3a8913651c0f47c45b59ef5591648f51de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Sat, 16 May 2026 18:30:48 +0100 Subject: [PATCH 10/22] feat: bridge reconciliation + USDT provisioning additions (#354) Ready to merge. Verified locally: - `yarn check:sdl` passes - targeted bridge/IBEX unit tests pass - `git diff --check` passes **squash & merge** used to keep `tmp/bridge-rebase-pr-ready` clean. --- dev/apollo-federation/supergraph.graphql | 2 +- .../Flash GraphQL API/environments/local.bru | 4 +- src/app/authentication/login.ts | 5 +- src/domain/pubsub/index.ts | 1 + src/graphql/admin/queries.ts | 2 + .../query/bridge-reconciliation-orphans.ts | 40 ++ src/graphql/admin/schema.graphql | 60 +++ src/graphql/admin/types/index.ts | 3 +- .../object/bridge-reconciliation-orphan.ts | 21 + .../root/mutation/onchain-payment-send-all.ts | 7 +- src/graphql/public/schema.graphql | 2 +- src/graphql/shared/types/object/btc-wallet.ts | 5 +- src/scripts/reconcile-bridge-ibex-deposits.ts | 6 +- src/servers/cron.ts | 6 +- src/services/bridge/index.ts | 71 +++- src/services/bridge/reconciliation.ts | 147 ++++++- .../bridge/webhook-server/routes/deposit.ts | 7 + src/services/ibex/client.ts | 55 ++- src/services/ibex/types.ts | 16 +- .../webhook-server/routes/crypto-receive.ts | 27 +- src/services/mongoose/bridge-accounts.ts | 13 +- .../mongoose/bridge-reconciliation-orphan.ts | 76 +++- src/services/mongoose/schema.ts | 21 + src/services/mongoose/wallets.ts | 5 +- test/flash/unit/services/bridge/index.spec.ts | 214 +++++++++- .../services/bridge/reconciliation.spec.ts | 378 ++++++++++++++++++ .../routes/crypto-receive.spec.ts | 117 ++++++ 27 files changed, 1251 insertions(+), 60 deletions(-) create mode 100644 src/graphql/admin/root/query/bridge-reconciliation-orphans.ts create mode 100644 src/graphql/admin/types/object/bridge-reconciliation-orphan.ts create mode 100644 test/flash/unit/services/bridge/reconciliation.spec.ts create mode 100644 test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 8c6041023..bb5202f04 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -1644,7 +1644,6 @@ type Query @join__type(graph: PUBLIC) { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount @@ -1655,6 +1654,7 @@ type Query currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] diff --git a/dev/bruno/Flash GraphQL API/environments/local.bru b/dev/bruno/Flash GraphQL API/environments/local.bru index 2c8d6c6bd..1cda70a56 100644 --- a/dev/bruno/Flash GraphQL API/environments/local.bru +++ b/dev/bruno/Flash GraphQL API/environments/local.bru @@ -8,6 +8,6 @@ vars { token: walletId: walletIdUsd: c593736e-5a58-42e4-93fa-dc895856c1f1 - userEmail: maurienteso@gmail.com - userFullName: maurienteso + userEmail: mauriente@gmail.com + userFullName: maurientes } diff --git a/src/app/authentication/login.ts b/src/app/authentication/login.ts index 736eb90ed..c87b3705c 100644 --- a/src/app/authentication/login.ts +++ b/src/app/authentication/login.ts @@ -309,7 +309,10 @@ export const loginDeviceUpgradeWithPhone = async ({ if (deviceWallets instanceof Error) return deviceWallets let deviceAccountHasBalance = false for (const wallet of deviceWallets) { - const balance = await getBalanceForWallet({ walletId: wallet.id }) + const balance = await getBalanceForWallet({ + walletId: wallet.id, + currency: wallet.currency, + }) if (balance instanceof Error) return balance if (!balance.isZero()) { deviceAccountHasBalance = true diff --git a/src/domain/pubsub/index.ts b/src/domain/pubsub/index.ts index a64a52b7b..d27047df1 100644 --- a/src/domain/pubsub/index.ts +++ b/src/domain/pubsub/index.ts @@ -5,6 +5,7 @@ export const PubSubDefaultTriggers = { UserPriceUpdate: "USER_PRICE_UPDATE", AccountUpdate: "ACCOUNT_UPDATE", LnPaymentStatus: "LN_PAYMENT_STATUS", + BridgeReconciliationUpdate: "BRIDGE_RECONCILIATION_UPDATE", } as const export const customPubSubTrigger = ({ diff --git a/src/graphql/admin/queries.ts b/src/graphql/admin/queries.ts index f18b8836a..a069657df 100644 --- a/src/graphql/admin/queries.ts +++ b/src/graphql/admin/queries.ts @@ -15,6 +15,7 @@ import AccountDetailsByAccountId from "./root/query/account-details-by-account-i import MerchantsPendingApprovalQuery from "./root/query/merchants-pending-approval-listing" import IdDocumentReadUrlQuery from "./root/query/id-document-read-url" import NotificationTopicsQuery from "./root/query/notification-topics" +import BridgeReconciliationOrphansQuery from "./root/query/bridge-reconciliation-orphans" export const queryFields = { unauthed: {}, @@ -34,6 +35,7 @@ export const queryFields = { merchantsPendingApproval: MerchantsPendingApprovalQuery, idDocumentReadUrl: IdDocumentReadUrlQuery, notificationTopics: NotificationTopicsQuery, + bridgeReconciliationOrphans: BridgeReconciliationOrphansQuery, }, } diff --git a/src/graphql/admin/root/query/bridge-reconciliation-orphans.ts b/src/graphql/admin/root/query/bridge-reconciliation-orphans.ts new file mode 100644 index 000000000..a50bded31 --- /dev/null +++ b/src/graphql/admin/root/query/bridge-reconciliation-orphans.ts @@ -0,0 +1,40 @@ +import { GT } from "@graphql/index" +import BridgeReconciliationOrphanObject from "@graphql/admin/types/object/bridge-reconciliation-orphan" +import { findOrphans } from "@services/mongoose/bridge-reconciliation-orphan" + +const BridgeReconciliationOrphansQuery = GT.Field({ + type: GT.NonNullList(BridgeReconciliationOrphanObject), + args: { + status: { type: GT.String, defaultValue: null }, + orphanType: { type: GT.String, defaultValue: null }, + limit: { type: GT.Int, defaultValue: 50 }, + }, + resolve: async ( + _: unknown, + { + status, + orphanType, + limit, + }: { status?: string; orphanType?: string; limit?: number }, + ) => { + const result = await findOrphans({ + status: status as "unmatched" | "resolved" | undefined, + orphanType: orphanType as + | "bridge_without_ibex" + | "ibex_without_bridge" + | undefined, + limit: limit ?? 50, + }) + + if (result instanceof Error) throw result + + return result.map((o) => ({ + ...o, + detectedAt: o.detectedAt.toISOString(), + resolvedAt: o.resolvedAt?.toISOString() ?? null, + triageContext: JSON.stringify(o.triageContext), + })) + }, +}) + +export default BridgeReconciliationOrphansQuery diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index d858eaf79..1eb75c263 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -110,6 +110,21 @@ type BTCWallet implements Wallet { walletCurrency: WalletCurrency! } +type BridgeReconciliationOrphan { + amount: String + currency: String + customerId: String + detectedAt: String! + id: ID! + orphanKey: String! + orphanType: String! + resolvedAt: String + status: String! + transferId: String + triageContext: String! + txHash: String +} + input BusinessDeleteMapInfoInput { username: Username! } @@ -319,6 +334,7 @@ type Query { accountDetailsByUserPhone(phone: Phone!): AuditedAccount! accountDetailsByUsername(username: Username!): AuditedAccount! allLevels: [AccountLevel!]! + bridgeReconciliationOrphans(limit: Int = 50, orphanType: String = null, status: String = null): [BridgeReconciliationOrphan!]! idDocumentReadUrl( """Storage key of the ID document file""" fileKey: String! @@ -551,6 +567,50 @@ type UsdWallet implements Wallet { walletCurrency: WalletCurrency! } +""" +A wallet belonging to an account which contains a USDT balance and a list of transactions. +""" +type UsdtWallet implements Wallet { + accountId: ID! + balance: FractionalCentAmount! + id: ID! + isExternal: Boolean! + lnurlp: Lnurl + + """An unconfirmed incoming onchain balance.""" + pendingIncomingBalance: SignedAmount! + transactions( + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + transactionsByAddress( + """Returns the items that include this address.""" + address: OnChainAddress! + + """Returns the items in the list that come after the specified cursor.""" + after: String + + """Returns the items in the list that come before the specified cursor.""" + before: String + + """Returns the first n items from the list.""" + first: Int + + """Returns the last n items from the list.""" + last: Int + ): TransactionConnection + walletCurrency: WalletCurrency! +} + input UserUpdatePhoneInput { accountUuid: ID! phone: Phone! diff --git a/src/graphql/admin/types/index.ts b/src/graphql/admin/types/index.ts index 566bf6c92..95ebebcf9 100644 --- a/src/graphql/admin/types/index.ts +++ b/src/graphql/admin/types/index.ts @@ -1,5 +1,6 @@ import BtcWallet from "@graphql/shared/types/object/btc-wallet" import GraphQLApplicationError from "@graphql/shared/types/object/graphql-application-error" import UsdWallet from "@graphql/shared/types/object/usd-wallet" +import UsdtWallet from "@graphql/shared/types/object/usdt-wallet" -export const ALL_INTERFACE_TYPES = [GraphQLApplicationError, BtcWallet, UsdWallet] +export const ALL_INTERFACE_TYPES = [GraphQLApplicationError, BtcWallet, UsdWallet, UsdtWallet] diff --git a/src/graphql/admin/types/object/bridge-reconciliation-orphan.ts b/src/graphql/admin/types/object/bridge-reconciliation-orphan.ts new file mode 100644 index 000000000..7ce3bc480 --- /dev/null +++ b/src/graphql/admin/types/object/bridge-reconciliation-orphan.ts @@ -0,0 +1,21 @@ +import { GT } from "@graphql/index" + +const BridgeReconciliationOrphanObject = GT.Object({ + name: "BridgeReconciliationOrphan", + fields: () => ({ + id: { type: GT.NonNullID }, + orphanKey: { type: GT.NonNull(GT.String) }, + orphanType: { type: GT.NonNull(GT.String) }, + status: { type: GT.NonNull(GT.String) }, + txHash: { type: GT.String }, + transferId: { type: GT.String }, + customerId: { type: GT.String }, + amount: { type: GT.String }, + currency: { type: GT.String }, + detectedAt: { type: GT.NonNull(GT.String) }, + resolvedAt: { type: GT.String }, + triageContext: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeReconciliationOrphanObject diff --git a/src/graphql/public/root/mutation/onchain-payment-send-all.ts b/src/graphql/public/root/mutation/onchain-payment-send-all.ts index 2f90ff8c9..c40b2c50a 100644 --- a/src/graphql/public/root/mutation/onchain-payment-send-all.ts +++ b/src/graphql/public/root/mutation/onchain-payment-send-all.ts @@ -10,7 +10,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import { Wallets } from "@app" import { getBalanceForWallet } from "@app/wallets" -import { USDAmount } from "@domain/shared" +import { USDAmount, WalletCurrency } from "@domain/shared" const OnChainPaymentSendAllInput = GT.Input({ name: "OnChainPaymentSendAllInput", @@ -63,7 +63,10 @@ const OnChainPaymentSendAllMutation = GT.Field< return { errors: [{ message: speed.message }] } } - const amount = await getBalanceForWallet({ walletId }) + const amount = await getBalanceForWallet({ + walletId, + currency: WalletCurrency.Usd, + }) if (amount instanceof Error) return amount if (!(amount instanceof USDAmount)) { return { errors: [{ message: "Onchain payments require a USD wallet" }] } diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index b5d7f3c79..0ed531044 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1288,7 +1288,6 @@ type PublicWallet { type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! - latestAccountUpgradeRequest: AccountUpgradeRequestPayload! bridgeExternalAccounts: [BridgeExternalAccount] bridgeKycStatus: String bridgeVirtualAccount: BridgeVirtualAccount @@ -1299,6 +1298,7 @@ type Query { currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload + latestAccountUpgradeRequest: AccountUpgradeRequestPayload! lnInvoicePaymentStatus(input: LnInvoicePaymentStatusInput!): LnInvoicePaymentStatusPayload! me: User mobileVersions: [MobileVersions] diff --git a/src/graphql/shared/types/object/btc-wallet.ts b/src/graphql/shared/types/object/btc-wallet.ts index 3a29738e2..9991092a0 100644 --- a/src/graphql/shared/types/object/btc-wallet.ts +++ b/src/graphql/shared/types/object/btc-wallet.ts @@ -52,7 +52,10 @@ const BtcWallet = GT.Object({ description: "A balance stored in BTC.", resolve: async (source) => { if (source.type === WalletType.External) return null - const balanceSats = await Wallets.getBalanceForWallet({ walletId: source.id }) + const balanceSats = await Wallets.getBalanceForWallet({ + walletId: source.id, + currency: source.currency, + }) if (balanceSats instanceof Error) { throw mapError(balanceSats) } diff --git a/src/scripts/reconcile-bridge-ibex-deposits.ts b/src/scripts/reconcile-bridge-ibex-deposits.ts index 7e643bc0f..747ac99ca 100644 --- a/src/scripts/reconcile-bridge-ibex-deposits.ts +++ b/src/scripts/reconcile-bridge-ibex-deposits.ts @@ -9,14 +9,14 @@ import { reconcileBridgeAndIbexDeposits } from "@services/bridge/reconciliation" const args = yargs(hideBin(process.argv)) .option("window-hours", { type: "number", - default: 24, - describe: "Reconciliation window in hours", + default: 0.25, + describe: "Reconciliation window in hours (default: 15 minutes)", }) .option("configPath", { type: "string", demandOption: true }) .parseSync() const main = async () => { - const windowMs = Math.max(1, Math.floor(args["window-hours"])) * 60 * 60 * 1000 + const windowMs = args["window-hours"] * 60 * 60 * 1000 const result = await reconcileBridgeAndIbexDeposits({ windowMs }) if (result instanceof Error) throw result baseLogger.info(result, "Bridge↔IBEX reconciliation finished") diff --git a/src/servers/cron.ts b/src/servers/cron.ts index 19511890e..469b59062 100644 --- a/src/servers/cron.ts +++ b/src/servers/cron.ts @@ -65,10 +65,14 @@ const swapOutJob = async () => { if (swapResult instanceof Error) throw swapResult } +// Window covers 15 min of events — real-time webhook reconciliation handles everything +// else immediately. This batch pass is only a safety net for missed/delayed webhooks. +const RECONCILE_WINDOW_MS = 15 * 60 * 1000 + const reconcileBridgeDepositsJob = async () => { if (!BridgeConfig.enabled) return - const result = await reconcileBridgeAndIbexDeposits() + const result = await reconcileBridgeAndIbexDeposits({ windowMs: RECONCILE_WINDOW_MS }) if (result instanceof Error) throw result } diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index ecd4891c2..f5f51e2da 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -17,6 +17,7 @@ import { RepositoryError } from "@domain/errors" import { toBridgeCustomerId } from "@domain/primitives/bridge" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" import { USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" import { WalletsRepository } from "@services/mongoose/wallets" import { IdentityRepository } from "@services/kratos" @@ -91,6 +92,38 @@ type ExternalAccountResult = { export const deriveWithdrawalIdempotencyKey = (rowId: string): string => crypto.createHash("sha256").update(`withdrawal:${rowId}`).digest("hex") +const ensureEthUsdtCashWallet = async ( + account: Account, +): Promise => { + const wallets = await WalletsRepository().listByAccountId(account.id) + if (wallets instanceof Error) return wallets + + let usdtWallet = wallets.find( + (wallet) => + wallet.currency === WalletCurrency.Usdt && wallet.type === WalletType.Checking, + ) + + if (!usdtWallet) { + const createdWallet = await WalletsRepository().persistNew({ + accountId: account.id, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + if (createdWallet instanceof Error) return createdWallet + usdtWallet = createdWallet + } + + if (account.defaultWalletId !== usdtWallet.id) { + const updatedAccount = await AccountsRepository().update({ + ...account, + defaultWalletId: usdtWallet.id, + }) + if (updatedAccount instanceof Error) return updatedAccount + } + + return usdtWallet +} + // ============ Guards ============ const checkBridgeEnabled = (): true | BridgeDisabledError => { @@ -180,6 +213,16 @@ const initiateKyc = async ({ return result } catch (error) { + const bridgeError = error as { statusCode?: number; response?: { existing_kyc_link?: { kyc_link: string; customer_id: string; tos_link: string } } } + + if (bridgeError?.statusCode === 400 && bridgeError.response?.existing_kyc_link) { + return { + kycLink: bridgeError.response.existing_kyc_link.kyc_link, + customerId: bridgeError.response.existing_kyc_link.customer_id, + tosLink: bridgeError.response.existing_kyc_link.tos_link, + } + } + baseLogger.error( { accountId, operation: "initiateKyc", error }, "Bridge operation failed", @@ -191,8 +234,9 @@ const initiateKyc = async ({ /** * Creates a virtual account for receiving USD deposits * - Requires approved KYC - * - Creates IBEX Tron USDT receive address - * - Creates Bridge virtual account pointing to Tron address + * - Ensures an IBEX ETH-USDT Cash Wallet exists and is the account default + * - Creates IBEX Ethereum USDT receive address + * - Creates Bridge virtual account pointing to Ethereum address */ const createVirtualAccount = async ( accountId: AccountId, @@ -227,7 +271,7 @@ const createVirtualAccount = async ( return new BridgeKycPendingError("KYC not yet completed") } - // Idempotency guard: return the existing VA immediately without touching Bridge + // Idempotency guard first: do not mutate wallets/default when a VA already exists const existingVa = await BridgeAccountsRepo.findVirtualAccountByAccountId( accountId as string, ) @@ -241,26 +285,29 @@ const createVirtualAccount = async ( } } - // Get or create Ethereum address - let ethereumAddress = - account.bridgeEthereumAddress || "0xaF095D35bfDd462165eA7eCF8AC75351a93d72bD" + const usdtCashWallet = await ensureEthUsdtCashWallet(account) + if (usdtCashWallet instanceof Error) return usdtCashWallet + + // Get or create Ethereum USDT receive address for the ETH-USDT Cash Wallet + let ethereumAddress = account.bridgeEthereumAddress if (!ethereumAddress) { - const option = await IbexClient.getEthereumUsdtOption() + let option = await IbexClient.getEthereumUsdtOption() if (option instanceof Error) return new BridgeError(option.message) + option.name = `USDT-ETH ${account.username}-${crypto.randomBytes(4).toString("hex")}` const receiveInfo = await IbexClient.createCryptoReceiveInfo( - account.defaultWalletId as IbexAccountId, + usdtCashWallet.id as IbexAccountId, option, ) if (receiveInfo instanceof Error) return new BridgeError(receiveInfo.message) const updateResult = await AccountsRepository().updateBridgeFields(accountId, { - bridgeEthereumAddress: receiveInfo.address, + bridgeEthereumAddress: receiveInfo.data.address, }) if (updateResult instanceof Error) return updateResult - ethereumAddress = receiveInfo.address + ethereumAddress = receiveInfo.data.address } // Deterministic key so Bridge deduplicates on their side if two calls race past @@ -407,7 +454,9 @@ const initiateWithdrawal = async ( const wallets = await WalletsRepository().listByAccountId(accountId) if (wallets instanceof Error) return wallets - const usdtWallet = wallets.find((w) => w.currency === WalletCurrency.Usdt) + const usdtWallet = wallets.find( + (w) => w.currency === WalletCurrency.Usdt && w.type === WalletType.Checking, + ) if (!usdtWallet) { return new BridgeInsufficientFundsError("No USDT wallet found on account") } diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts index ec964ba00..891229e10 100644 --- a/src/services/bridge/reconciliation.ts +++ b/src/services/bridge/reconciliation.ts @@ -1,9 +1,14 @@ import { baseLogger } from "@services/logger" import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" -import { upsertBridgeReconciliationOrphan } from "@services/mongoose/bridge-reconciliation-orphan" -import { BridgeDepositLog } from "@services/mongoose/schema" +import { + upsertBridgeReconciliationOrphan, + resolveOrphansByTxHash, +} from "@services/mongoose/bridge-reconciliation-orphan" +import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" +import { PubSubService } from "@services/pubsub" +import { PubSubDefaultTriggers } from "@domain/pubsub" -const ONE_DAY_MS = 24 * 60 * 60 * 1000 +const FIFTEEN_MIN_MS = 15 * 60 * 1000 type BridgeDepositLike = { eventId: string @@ -29,7 +34,7 @@ type IbexReceiveLike = { const toOrphanKey = (prefix: string, value: string) => `${prefix}:${value.toLowerCase()}` export const reconcileBridgeAndIbexDeposits = async ({ - windowMs = ONE_DAY_MS, + windowMs = FIFTEEN_MIN_MS, }: { windowMs?: number } = {}): Promise< @@ -47,7 +52,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ const bridgeDeposits = (await BridgeDepositLog.find({ createdAt: { $gte: since, $lte: now }, - state: "funds_received", + state: "payment_processed", }) .lean() .exec()) as BridgeDepositLike[] @@ -82,7 +87,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ amount: deposit.amount, currency: deposit.currency, triageContext: { - reason: "Bridge funds_received has no destinationTxHash", + reason: "Bridge payment_processed has no destinationTxHash", windowStart: since.toISOString(), windowEnd: now.toISOString(), depositState: deposit.state, @@ -107,7 +112,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ currency: deposit.currency, triageContext: { reason: - "No IBEX crypto.receive found for Bridge destinationTxHash within 24h window", + "No IBEX crypto.receive found for Bridge destinationTxHash within window", windowStart: since.toISOString(), windowEnd: now.toISOString(), depositState: deposit.state, @@ -129,7 +134,7 @@ export const reconcileBridgeAndIbexDeposits = async ({ currency: receive.currency, triageContext: { reason: - "No Bridge deposit funds_received found for IBEX tx hash within 24h window", + "No Bridge deposit payment_processed found for IBEX tx hash within window", windowStart: since.toISOString(), windowEnd: now.toISOString(), address: receive.address, @@ -153,3 +158,129 @@ export const reconcileBridgeAndIbexDeposits = async ({ return error instanceof Error ? error : new Error(String(error)) } } + +type ReconcileByTxHashResult = { + txHash: string + status: "matched" | "unmatched" + orphanType?: "bridge_without_ibex" | "ibex_without_bridge" + transferId?: string + customerId?: string + amount?: string + currency?: string + detectedAt: Date +} + +export const reconcileByTxHash = async ({ + txHash, +}: { + txHash: string +}): Promise => { + const normalizedHash = txHash.toLowerCase() + const now = new Date() + + try { + const [bridgeDeposit, ibexReceive] = await Promise.all([ + BridgeDepositLog.findOne({ + destinationTxHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, + state: "payment_processed", + }) + .lean() + .exec(), + IbexCryptoReceiveLog.findOne({ + txHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, + }) + .lean() + .exec(), + ]) + + const pubsub = PubSubService() + + if (bridgeDeposit && ibexReceive) { + await resolveOrphansByTxHash(normalizedHash) + + const event: ReconcileByTxHashResult = { + txHash: normalizedHash, + status: "matched", + transferId: (bridgeDeposit as BridgeDepositLike).transferId, + customerId: (bridgeDeposit as BridgeDepositLike).customerId, + amount: (bridgeDeposit as BridgeDepositLike).amount, + currency: (bridgeDeposit as BridgeDepositLike).currency, + detectedAt: now, + } + + baseLogger.info(event, "Bridge↔IBEX real-time reconciliation: matched") + pubsub.publish({ + trigger: PubSubDefaultTriggers.BridgeReconciliationUpdate, + payload: event, + }) + return event + } + + let orphanType: "bridge_without_ibex" | "ibex_without_bridge" + let orphanKey: string + let triageContext: Record + let transferId: string | undefined + let customerId: string | undefined + let amount: string | undefined + let currency: string | undefined + + if (bridgeDeposit && !ibexReceive) { + orphanType = "bridge_without_ibex" + orphanKey = toOrphanKey("bridge", normalizedHash) + transferId = (bridgeDeposit as BridgeDepositLike).transferId + customerId = (bridgeDeposit as BridgeDepositLike).customerId + amount = (bridgeDeposit as BridgeDepositLike).amount + currency = (bridgeDeposit as BridgeDepositLike).currency + triageContext = { + reason: "Bridge payment_processed has no matching IBEX crypto.receive yet", + txHash: normalizedHash, + depositState: (bridgeDeposit as BridgeDepositLike).state, + createdAt: (bridgeDeposit as BridgeDepositLike).createdAt.toISOString(), + detectedAt: now.toISOString(), + } + } else { + orphanType = "ibex_without_bridge" + orphanKey = toOrphanKey("ibex", normalizedHash) + amount = ibexReceive ? (ibexReceive as IbexReceiveLike).amount : undefined + currency = ibexReceive ? (ibexReceive as IbexReceiveLike).currency : undefined + triageContext = { + reason: "IBEX crypto.receive has no matching Bridge funds_received yet", + txHash: normalizedHash, + address: ibexReceive ? (ibexReceive as IbexReceiveLike).address : undefined, + network: ibexReceive ? (ibexReceive as IbexReceiveLike).network : undefined, + detectedAt: now.toISOString(), + } + } + + await upsertBridgeReconciliationOrphan({ + orphanKey, + orphanType, + txHash: normalizedHash, + transferId, + customerId, + amount, + currency, + triageContext, + }) + + const event: ReconcileByTxHashResult = { + txHash: normalizedHash, + status: "unmatched", + orphanType, + transferId, + customerId, + amount, + currency, + detectedAt: now, + } + + baseLogger.info(event, "Bridge↔IBEX real-time reconciliation: unmatched") + pubsub.publish({ + trigger: PubSubDefaultTriggers.BridgeReconciliationUpdate, + payload: event, + }) + return event + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index e2217c7b7..a87352b1a 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -10,6 +10,7 @@ import { Request, Response } from "express" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { createBridgeDepositLog } from "@services/mongoose/bridge-deposit-log" +import { reconcileByTxHash } from "@services/bridge/reconciliation" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body @@ -77,6 +78,12 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to persist deposit log" }) } + if (state === "payment_processed" && receipt?.destination_tx_hash) { + reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => + baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), + ) + } + return res.status(200).json({ status: "success" }) } catch (error) { baseLogger.error({ error, id, event_id }, "Error processing Bridge deposit webhook") diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 2eb0a65fa..da0b4b1f4 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -39,6 +39,7 @@ import { CryptoReceiveOption, CryptoReceiveInfo, CreateCryptoReceiveInfoRequest, + IbexCurrency, } from "./types" import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" @@ -231,10 +232,10 @@ const payToLnurl = async ( const getIbexToken = async (): Promise => { const cached = await Ibex.authentication.storage.getAccessToken() - if (typeof cached === "string") return `Bearer ${cached}` + if (typeof cached === "string") return `${cached}` // The SDK uses a single base URL for all calls, but the sandbox auth domain is separate - const resp = await fetch(`${IbexConfig.authUrl}/auth/signin`, { + const resp = await fetch(`${IbexConfig.url}/auth/signin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), @@ -268,7 +269,7 @@ const getIbexToken = async (): Promise => { ) } - return `Bearer ${data.accessToken}` + return data.accessToken as string } const ibexFetch = async ( @@ -299,6 +300,24 @@ const ibexGet = (token: string, path: string) => const ibexPost = (token: string, path: string, body: unknown) => ibexFetch(token, path, { method: "POST", body: JSON.stringify(body) }) + +const createIbexAccount = async ( + name: string, + currencyId: IbexCurrencyId, +): Promise => { + try { + const token = await getIbexToken() + if (token instanceof IbexError) return token + const data = await ibexPost( + token, + "/account/create" + , { name, currencyId }) + if (data instanceof IbexError) return data + return data + } catch (err) { + return new IbexError(err instanceof Error ? err : new Error(String(err))) + } +} const getCryptoReceiveBalance = async ( receiveInfoId: string, ): Promise => { @@ -322,12 +341,13 @@ const getCryptoReceiveOptions = async (): Promise( + const data = await ibexGet( token, "/crypto/receive-infos/options", ) + if (data instanceof IbexError) return data - return data.options || [] + return data } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } @@ -346,36 +366,39 @@ const createCryptoReceiveInfo = async ( { name: option.name, network: option.network } as CreateCryptoReceiveInfoRequest, ) if (data instanceof IbexError) return data - if (!data.address) return new UnexpectedIbexResponse("Address not found") + if (!data.data.address) return new UnexpectedIbexResponse("Address not found") return data } catch (err) { return new IbexError(err instanceof Error ? err : new Error(String(err))) } } -const getTronUsdtOption = async (): Promise => { +const getTronUsdtOption = async (): Promise => { const options = await getCryptoReceiveOptions() if (options instanceof IbexError) return options const tronUsdt = options.find( (opt) => - opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "tron", + opt.currencyId === USDTAmount.currencyId && opt.network.toLowerCase() === "tron", ) if (!tronUsdt) { return new IbexError(new Error("Tron USDT option not found")) } - return tronUsdt.id + return tronUsdt } const getEthereumUsdtOption = async (): Promise => { const options = await getCryptoReceiveOptions() if (options instanceof IbexError) return options + const UsdtCurrencyId = await getIbexCurrencyId(WalletCurrency.Usdt) + if (UsdtCurrencyId instanceof IbexError) return UsdtCurrencyId as IbexError + const ethereumUsdt = options.find( (opt) => - opt.currency.toLowerCase() === "usdt" && opt.network.toLowerCase() === "ethereum", + opt.currencyId === UsdtCurrencyId && opt.network.toLowerCase() === "ethereum", ) if (!ethereumUsdt) { @@ -385,6 +408,16 @@ const getEthereumUsdtOption = async (): Promise return ethereumUsdt } +const getIbexCurrencyId = async ( + currency: WalletCurrency, +): Promise => { + const data = await ibexGet<{ currencies: IbexCurrency[] }>("", "/currency/all") + if (data instanceof IbexError) return data + const currencyId = data.currencies.find((c) => c.name === currency)?.id + if (!currencyId) return new IbexError(new Error(`Currency ${currency} not found`)) + return currencyId +} + // const sendBetweenAccounts = async ( // sender: IbexAccount, // receiver: IbexAccount, @@ -422,10 +455,12 @@ export default wrapAsyncFunctionsToRunInSpan({ createLnurlPay, decodeLnurl, payToLnurl, + createIbexAccount, getCryptoReceiveBalance, getCryptoReceiveOptions, createCryptoReceiveInfo, getTronUsdtOption, getEthereumUsdtOption, + getIbexCurrencyId, }, }) diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index ec3d55015..16764d137 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -38,17 +38,27 @@ export type IbexInvoiceArgs = { } export interface CryptoReceiveOption { - id: string - currency: string + id?: string + currencyId: number network: string + name?: string +} + +export interface IbexCurrency { + id: IbexCurrencyId name: string + isFiat: boolean + symbol: string + accountEnabled: boolean } export interface CryptoReceiveInfo { id: string wallet_id: string option_id: string - address: string + data: { + address: string + } currency: string network: string created_at: string diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index e104a52df..83282f9a5 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -5,6 +5,7 @@ import { listWalletsByAccountId } from "@app/wallets" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" import { LockService } from "@services/lock" +import { reconcileByTxHash } from "@services/bridge/reconciliation" import { authenticate, logRequest } from "../middleware" @@ -21,8 +22,16 @@ interface CryptoReceiveResult { const cryptoReceiveHandler = async (req: Request, res: Response) => { const { tx_hash, address, amount, currency, network } = req.body - - if (!tx_hash || !address || !amount || currency !== "USDT" || network !== "tron") { + const normalizedCurrency = String(currency || "").toUpperCase() + const normalizedNetwork = String(network || "").toLowerCase() + + if ( + !tx_hash || + !address || + !amount || + normalizedCurrency !== "USDT" || + normalizedNetwork !== "ethereum" + ) { baseLogger.warn( { tx_hash, address, amount, currency, network }, "Invalid crypto receive payload", @@ -30,8 +39,8 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "Invalid payload" }) } - const lockResult = await LockService().lockPaymentHash( - tx_hash as PaymentHash, + const lockResult = await LockService().lockOnChainTxHash( + tx_hash as OnChainTxHash, async () => { try { const account = await AccountsRepository().findByBridgeEthereumAddress(address) @@ -44,8 +53,8 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { txHash: String(tx_hash), address: String(address), amount: String(amount), - currency: String(currency), - network: String(network), + currency: normalizedCurrency, + network: normalizedNetwork, accountId: account.id, }) if (ibexLog instanceof Error) { @@ -56,6 +65,10 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return { status: "error", code: "internal_error" } as CryptoReceiveResult } + reconcileByTxHash({ txHash: String(tx_hash) }).catch((err) => + baseLogger.error({ err, tx_hash }, "Real-time reconciliation failed"), + ) + const wallets = await listWalletsByAccountId(account.id) if (wallets instanceof Error) { baseLogger.error( @@ -122,4 +135,4 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { router.post(paths.cryptoReceive, authenticate, logRequest, cryptoReceiveHandler) -export { paths, router } +export { cryptoReceiveHandler, paths, router } diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 1828e3650..234304c2c 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -106,7 +106,18 @@ export const createWithdrawal = async (data: { try { const record = await BridgeWithdrawal.create(data) return record - } catch (error) { + } catch (error: unknown) { + const mongoErr = error as { code?: number } + if (mongoErr.code === 11000) { + const record = await BridgeWithdrawal.findOne({ + accountId: data.accountId, + externalAccountId: data.externalAccountId, + amount: data.amount, + currency: data.currency, + status: "pending", + }) + if (record) return record + } return new RepositoryError(String(error)) } } diff --git a/src/services/mongoose/bridge-reconciliation-orphan.ts b/src/services/mongoose/bridge-reconciliation-orphan.ts index 0150333d2..3e3b6807f 100644 --- a/src/services/mongoose/bridge-reconciliation-orphan.ts +++ b/src/services/mongoose/bridge-reconciliation-orphan.ts @@ -1,6 +1,7 @@ import { BridgeReconciliationOrphan } from "./schema" type OrphanType = "bridge_without_ibex" | "ibex_without_bridge" +type OrphanStatus = "unmatched" | "resolved" export const upsertBridgeReconciliationOrphan = async (data: { orphanKey: string @@ -16,7 +17,7 @@ export const upsertBridgeReconciliationOrphan = async (data: { try { const orphan = await BridgeReconciliationOrphan.findOneAndUpdate( { orphanKey: data.orphanKey }, - { ...data, detectedAt: new Date() }, + { ...data, status: "unmatched", detectedAt: new Date() }, { upsert: true, new: true, setDefaultsOnInsert: true }, ) @@ -25,3 +26,76 @@ export const upsertBridgeReconciliationOrphan = async (data: { return error instanceof Error ? error : new Error(String(error)) } } + +export const resolveOrphansByTxHash = async ( + txHash: string, +): Promise<{ resolvedCount: number } | Error> => { + try { + const now = new Date() + const result = await BridgeReconciliationOrphan.updateMany( + { + txHash: txHash.toLowerCase(), + status: "unmatched", + }, + { $set: { status: "resolved", resolvedAt: now } }, + ) + return { resolvedCount: result.modifiedCount } + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} + +export const findOrphans = async ({ + status, + orphanType, + limit = 50, +}: { + status?: OrphanStatus + orphanType?: OrphanType + limit?: number +} = {}): Promise< + | { + id: string + orphanKey: string + orphanType: OrphanType + status: OrphanStatus + transferId?: string + txHash?: string + customerId?: string + amount?: string + currency?: string + triageContext: Record + detectedAt: Date + resolvedAt?: Date + }[] + | Error +> => { + try { + const filter: Record = {} + if (status) filter.status = status + if (orphanType) filter.orphanType = orphanType + + const docs = await BridgeReconciliationOrphan.find(filter) + .sort({ detectedAt: -1 }) + .limit(limit) + .lean() + .exec() + + return docs.map((d) => ({ + id: (d._id as { toString(): string }).toString(), + orphanKey: d.orphanKey as string, + orphanType: d.orphanType as OrphanType, + status: (d.status ?? "unmatched") as OrphanStatus, + transferId: d.transferId as string | undefined, + txHash: d.txHash as string | undefined, + customerId: d.customerId as string | undefined, + amount: d.amount as string | undefined, + currency: d.currency as string | undefined, + triageContext: d.triageContext as Record, + detectedAt: d.detectedAt as Date, + resolvedAt: d.resolvedAt as Date | undefined, + })) + } catch (error) { + return error instanceof Error ? error : new Error(String(error)) + } +} diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index 2feb91d87..15a1fd2f5 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -658,6 +658,18 @@ const BridgeWithdrawalSchema = new Schema({ updatedAt: { type: Date, default: Date.now }, }) +// At most one pending row per (account, destination, amount, currency). Partial filter must +// not use $exists:false — MongoDB rejects it for partial indexes ("$not ... $exists"). +// "pending" alone is enough: completed/failed rows are excluded so the same tuple can repeat +// after a terminal status. +BridgeWithdrawalSchema.index( + { accountId: 1, externalAccountId: 1, amount: 1, currency: 1 }, + { + unique: true, + partialFilterExpression: { status: "pending" }, + }, +) + const BridgeDepositLogSchema = new Schema({ eventId: { type: String, required: true, unique: true }, transferId: { type: String, required: true }, @@ -703,6 +715,12 @@ const BridgeReconciliationOrphanSchema = new Schema({ enum: ["bridge_without_ibex", "ibex_without_bridge"], required: true, }, + status: { + type: String, + enum: ["unmatched", "resolved"], + default: "unmatched", + required: true, + }, transferId: { type: String }, txHash: { type: String }, bridgeEventId: { type: String }, @@ -711,10 +729,13 @@ const BridgeReconciliationOrphanSchema = new Schema({ currency: { type: String }, triageContext: { type: Schema.Types.Mixed, required: true }, detectedAt: { type: Date, default: Date.now }, + resolvedAt: { type: Date }, }) BridgeReconciliationOrphanSchema.index({ orphanType: 1, detectedAt: -1 }) BridgeReconciliationOrphanSchema.index({ detectedAt: -1 }) +BridgeReconciliationOrphanSchema.index({ status: 1, detectedAt: -1 }) +BridgeReconciliationOrphanSchema.index({ txHash: 1 }) export const BridgeReconciliationOrphan = mongoose.model( "BridgeReconciliationOrphan", diff --git a/src/services/mongoose/wallets.ts b/src/services/mongoose/wallets.ts index 0a2da5d61..80da0dcce 100644 --- a/src/services/mongoose/wallets.ts +++ b/src/services/mongoose/wallets.ts @@ -4,7 +4,6 @@ import { CouldNotFindWalletFromIdError, CouldNotFindWalletFromOnChainAddressError, CouldNotFindWalletFromOnChainAddressesError, - CouldNotListWalletsFromAccountIdError, CouldNotListWalletsFromWalletCurrencyError, InvalidLnurlError, RepositoryError, @@ -23,6 +22,7 @@ import { ErrorLevel, USDAmount, USDTAmount, WalletCurrency } from "@domain/share import { WalletType } from "@domain/wallets" + import { toObjectId, fromObjectId, parseRepositoryError } from "./utils" import { Wallet } from "./schema" import { AccountsRepository } from "./accounts" @@ -66,6 +66,7 @@ export const WalletsRepository = (): IWalletsRepository => { if (resp instanceof IbexError) return resp const ibexAccountId = resp.id + let lnurlp: string | undefined if (ibexAccountId !== undefined) { const lnurlResp = await Ibex.createLnurlPay({ @@ -118,7 +119,7 @@ export const WalletsRepository = (): IWalletsRepository => { _accountId: toObjectId(accountId), }) if (!result || result.length === 0) { - return new CouldNotListWalletsFromAccountIdError(`accountId: ${accountId}}`) + return [] } return result.map(resultToWallet) } catch (err) { diff --git a/test/flash/unit/services/bridge/index.spec.ts b/test/flash/unit/services/bridge/index.spec.ts index 635200766..67d6b50c1 100644 --- a/test/flash/unit/services/bridge/index.spec.ts +++ b/test/flash/unit/services/bridge/index.spec.ts @@ -18,6 +18,8 @@ jest.mock("@services/logger", () => ({ })) jest.mock("@services/mongoose/bridge-accounts", () => ({ + createVirtualAccount: jest.fn(), + findVirtualAccountByAccountId: jest.fn(), createWithdrawal: jest.fn(), findPendingWithdrawalWithoutTransfer: jest.fn(), findExternalAccountsByAccountId: jest.fn(), @@ -26,7 +28,15 @@ jest.mock("@services/mongoose/bridge-accounts", () => ({ jest.mock("@services/bridge/client", () => ({ __esModule: true, - default: { createTransfer: jest.fn() }, + default: { createVirtualAccount: jest.fn(), createTransfer: jest.fn() }, +})) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + getEthereumUsdtOption: jest.fn(), + createCryptoReceiveInfo: jest.fn(), + }, })) jest.mock("@services/ibex/client", () => ({ @@ -83,6 +93,8 @@ import BridgeClient from "@services/bridge/client" import { AccountsRepository } from "@services/mongoose/accounts" import { WalletsRepository } from "@services/mongoose/wallets" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" +import IbexClient from "@services/ibex/client" +import { RepositoryError } from "@domain/errors" // ── Fixtures ────────────────────────────────────────────────────────────────── @@ -92,6 +104,9 @@ const AMOUNT = "50" const CUSTOMER_ID = "cust-001" const ETHEREUM_ADDRESS = "ETH_ADDR_001" const TRANSFER_ID = "transfer-bridge-001" +const USDT_WALLET_ID = "ibex-eth-usdt-wallet-001" +const RECEIVE_INFO_ID = "receive-info-001" +const VIRTUAL_ACCOUNT_ID = "virtual-account-001" const mockAccount = { id: ACCOUNT_ID, @@ -119,6 +134,22 @@ const mockTransfer = { state: "pending", } +const mockVirtualAccount = { + id: VIRTUAL_ACCOUNT_ID, + source_deposit_instructions: { + bank_name: "Test Bank", + bank_routing_number: "123456789", + bank_account_number: "123456789012", + }, +} + +const makeWallet = (id: string, currency: string) => ({ + id, + accountId: ACCOUNT_ID, + type: "checking", + currency, +}) + // ── Helpers ─────────────────────────────────────────────────────────────────── const setupGuards = () => { @@ -131,9 +162,9 @@ const setupGuards = () => { findById: jest.fn().mockResolvedValue(mockAccount), }) ;(WalletsRepository as jest.Mock).mockReturnValue({ - listByAccountId: jest - .fn() - .mockResolvedValue([{ id: "wallet-001", currency: "USDT" }]), + listByAccountId: jest.fn().mockResolvedValue([ + { id: "wallet-001", currency: "USDT", type: "checking" }, + ]), }) ;(getBalanceForWallet as jest.Mock).mockResolvedValue(balance) ;(BridgeAccountsRepo.findExternalAccountsByAccountId as jest.Mock).mockResolvedValue([ @@ -178,6 +209,181 @@ describe("deriveWithdrawalIdempotencyKey", () => { }) }) +/** + * Linear ENG-296 — ETH-USDT Cash Wallet + Bridge virtual account + * @see https://linear.app/island-bitcoin/issue/ENG-296 + * + * Acceptance ↔ tests (staging steps: `dev/qa/ENG-296-staging-checklist.md`): + * - AC1 IBEX ETH-USDT as Cash Wallet: first `it` — USDT checking persisted/reused, Ibex + * `createCryptoReceiveInfo` uses USDT wallet id; account `bridgeEthereumAddress` updated. + * - AC2 USD not primary: first `it` — `AccountsRepository.update` sets `defaultWalletId` to USDT wallet. + * - AC3 createVirtualAccount E2E: first & second `it` — Bridge + repo; third `it` — idempotent VA return. + */ +describe("createVirtualAccount — ETH-USDT Cash Wallet provisioning (ENG-296)", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("ENG-296 AC1+AC2+AC3: provisions USDT cash wallet, flips default off USD, persists Ibex ETH receive address, creates Bridge VA", async () => { + const usdtWallet = makeWallet(USDT_WALLET_ID, "USDT") + const accountWithoutUsdt = { + ...mockAccount, + defaultWalletId: "legacy-usd-wallet-id", + bridgeEthereumAddress: undefined, + } + + const accountsRepo = { + findById: jest.fn().mockResolvedValue(accountWithoutUsdt), + update: jest.fn().mockResolvedValue({ + ...accountWithoutUsdt, + defaultWalletId: USDT_WALLET_ID, + }), + updateBridgeFields: jest.fn().mockResolvedValue({ + ...accountWithoutUsdt, + defaultWalletId: USDT_WALLET_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + }), + } + ;(AccountsRepository as jest.Mock).mockReturnValue(accountsRepo) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn().mockResolvedValue([makeWallet("legacy-usd-wallet-id", "USD")]), + persistNew: jest.fn().mockResolvedValue(usdtWallet), + }) + ;(BridgeAccountsRepo.findVirtualAccountByAccountId as jest.Mock).mockResolvedValue( + new RepositoryError("not found"), + ) + ;(IbexClient.getEthereumUsdtOption as jest.Mock).mockResolvedValue({ + id: "eth-usdt-option", + currency: "USDT", + network: "ethereum", + name: "Ethereum USDT", + }) + ;(IbexClient.createCryptoReceiveInfo as jest.Mock).mockResolvedValue({ + id: RECEIVE_INFO_ID, + wallet_id: USDT_WALLET_ID, + option_id: "eth-usdt-option", + data: { address: ETHEREUM_ADDRESS }, + currency: "USDT", + network: "ethereum", + created_at: "2026-05-09T00:00:00Z", + }) + ;(BridgeClient.createVirtualAccount as jest.Mock).mockResolvedValue(mockVirtualAccount) + ;(BridgeAccountsRepo.createVirtualAccount as jest.Mock).mockResolvedValue({ + bridgeVirtualAccountId: VIRTUAL_ACCOUNT_ID, + }) + + await BridgeService.createVirtualAccount(ACCOUNT_ID) + + expect(WalletsRepository().persistNew).toHaveBeenCalledWith({ + accountId: ACCOUNT_ID, + type: "checking", + currency: "USDT", + }) + expect(AccountsRepository().update).toHaveBeenCalledWith( + expect.objectContaining({ defaultWalletId: USDT_WALLET_ID }), + ) + expect(IbexClient.createCryptoReceiveInfo).toHaveBeenCalledWith( + USDT_WALLET_ID, + expect.objectContaining({ network: "ethereum", currency: "USDT" }), + ) + expect(accountsRepo.updateBridgeFields).toHaveBeenCalledWith( + ACCOUNT_ID, + expect.objectContaining({ bridgeEthereumAddress: ETHEREUM_ADDRESS }), + ) + expect(BridgeClient.createVirtualAccount).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.objectContaining({ + destination: expect.objectContaining({ + currency: "usdt", + payment_rail: "ethereum", + address: ETHEREUM_ADDRESS, + }), + }), + expect.any(String), + ) + }) + + it("ENG-296 AC1+AC3: reuses existing USDT cash wallet and stored Ethereum address (no extra Ibex receive-info call)", async () => { + const usdtWallet = makeWallet(USDT_WALLET_ID, "USDT") + const accountWithUsdtDefault = { + ...mockAccount, + defaultWalletId: USDT_WALLET_ID, + bridgeEthereumAddress: ETHEREUM_ADDRESS, + } + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(accountWithUsdtDefault), + update: jest.fn(), + updateBridgeFields: jest.fn(), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn().mockResolvedValue([usdtWallet]), + persistNew: jest.fn(), + }) + ;(BridgeAccountsRepo.findVirtualAccountByAccountId as jest.Mock).mockResolvedValue( + new RepositoryError("not found"), + ) + ;(BridgeClient.createVirtualAccount as jest.Mock).mockResolvedValue(mockVirtualAccount) + ;(BridgeAccountsRepo.createVirtualAccount as jest.Mock).mockResolvedValue({ + bridgeVirtualAccountId: VIRTUAL_ACCOUNT_ID, + }) + + await BridgeService.createVirtualAccount(ACCOUNT_ID) + + expect(WalletsRepository().persistNew).not.toHaveBeenCalled() + expect(AccountsRepository().update).not.toHaveBeenCalled() + expect(IbexClient.createCryptoReceiveInfo).not.toHaveBeenCalled() + expect(BridgeClient.createVirtualAccount).toHaveBeenCalledWith( + CUSTOMER_ID, + expect.objectContaining({ + destination: expect.objectContaining({ address: ETHEREUM_ADDRESS }), + }), + expect.any(String), + ) + }) + + it("ENG-296 AC3 (idempotent): existing VA returns stored bank details without wallet or Ibex side effects", async () => { + const existingVaRecord = { + bridgeVirtualAccountId: VIRTUAL_ACCOUNT_ID, + bankName: "Existing Bank", + routingNumber: "021000021", + accountNumber: "000111222", + accountNumberLast4: "0222", + } + + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + update: jest.fn(), + updateBridgeFields: jest.fn(), + }) + ;(WalletsRepository as jest.Mock).mockReturnValue({ + listByAccountId: jest.fn(), + persistNew: jest.fn(), + }) + ;(BridgeAccountsRepo.findVirtualAccountByAccountId as jest.Mock).mockResolvedValue( + existingVaRecord, + ) + + const result = await BridgeService.createVirtualAccount(ACCOUNT_ID) + + expect(result).toEqual( + expect.objectContaining({ + virtualAccountId: VIRTUAL_ACCOUNT_ID, + bankName: "Existing Bank", + routingNumber: "021000021", + accountNumber: "000111222", + accountNumberLast4: "0222", + }), + ) + expect(WalletsRepository().listByAccountId).not.toHaveBeenCalled() + expect(WalletsRepository().persistNew).not.toHaveBeenCalled() + expect(AccountsRepository().update).not.toHaveBeenCalled() + expect(IbexClient.getEthereumUsdtOption).not.toHaveBeenCalled() + expect(IbexClient.createCryptoReceiveInfo).not.toHaveBeenCalled() + expect(BridgeClient.createVirtualAccount).not.toHaveBeenCalled() + }) +}) + describe("initiateWithdrawal — idempotency key wiring", () => { beforeEach(() => { jest.clearAllMocks() diff --git a/test/flash/unit/services/bridge/reconciliation.spec.ts b/test/flash/unit/services/bridge/reconciliation.spec.ts new file mode 100644 index 000000000..2c4c3a6d6 --- /dev/null +++ b/test/flash/unit/services/bridge/reconciliation.spec.ts @@ -0,0 +1,378 @@ +/** + * Unit tests for Bridge↔IBEX reconciliation + * Covers reconcileByTxHash (real-time) and reconcileBridgeAndIbexDeposits (batch) + */ + +// ── Mocks (must be before imports) ─────────────────────────────────────────── + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/schema", () => ({ + BridgeDepositLog: { findOne: jest.fn(), find: jest.fn() }, + IbexCryptoReceiveLog: { findOne: jest.fn() }, +})) + +jest.mock("@services/mongoose/ibex-crypto-receive-log", () => ({ + findIbexCryptoReceiveLogsSince: jest.fn(), +})) + +jest.mock("@services/mongoose/bridge-reconciliation-orphan", () => ({ + upsertBridgeReconciliationOrphan: jest.fn(), + resolveOrphansByTxHash: jest.fn(), +})) + +jest.mock("@services/pubsub", () => ({ + PubSubService: jest.fn(), +})) + +jest.mock("@domain/pubsub", () => ({ + PubSubDefaultTriggers: { + BridgeReconciliationUpdate: "BRIDGE_RECONCILIATION_UPDATE", + }, +})) + +import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" +import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +import { + upsertBridgeReconciliationOrphan, + resolveOrphansByTxHash, +} from "@services/mongoose/bridge-reconciliation-orphan" +import { PubSubService } from "@services/pubsub" +import { + reconcileByTxHash, + reconcileBridgeAndIbexDeposits, +} from "@services/bridge/reconciliation" + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +const TX_HASH = "0xABC123def456" +const NORM_HASH = TX_HASH.toLowerCase() + +const BRIDGE_DEPOSIT = { + eventId: "evt_001", + transferId: "tr_001", + customerId: "cust_001", + amount: "100", + currency: "usdt", + destinationTxHash: NORM_HASH, + state: "payment_processed", + createdAt: new Date("2026-01-01T12:00:00Z"), +} + +const IBEX_RECEIVE = { + txHash: NORM_HASH, + address: "0xdeadbeef", + amount: "100", + currency: "USDT", + network: "tron", + accountId: "acc_001", + receivedAt: new Date("2026-01-01T12:00:02Z"), +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const mockPublish = jest.fn() + +const makeLeanQuery = (result: unknown) => ({ + lean: () => ({ exec: () => Promise.resolve(result) }), +}) + +beforeEach(() => { + jest.clearAllMocks() + ;(PubSubService as jest.Mock).mockReturnValue({ publish: mockPublish }) + ;(resolveOrphansByTxHash as jest.Mock).mockResolvedValue({ resolvedCount: 0 }) + ;(upsertBridgeReconciliationOrphan as jest.Mock).mockResolvedValue({ id: "orphan_001" }) +}) + +// ── reconcileByTxHash ───────────────────────────────────────────────────────── + +describe("reconcileByTxHash", () => { + describe("both sides found → matched", () => { + beforeEach(() => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + }) + + it("returns status matched", async () => { + const result = await reconcileByTxHash({ txHash: TX_HASH }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("matched") + expect(result.txHash).toBe(NORM_HASH) + }) + + it("calls resolveOrphansByTxHash with normalized hash", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(resolveOrphansByTxHash).toHaveBeenCalledWith(NORM_HASH) + }) + + it("does NOT call upsertBridgeReconciliationOrphan", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).not.toHaveBeenCalled() + }) + + it("publishes a matched event to PubSub", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(mockPublish).toHaveBeenCalledWith( + expect.objectContaining({ + trigger: "BRIDGE_RECONCILIATION_UPDATE", + payload: expect.objectContaining({ + status: "matched", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + customerId: BRIDGE_DEPOSIT.customerId, + amount: BRIDGE_DEPOSIT.amount, + }), + }), + ) + }) + + it("normalizes txHash to lowercase before querying and returning", async () => { + const result = await reconcileByTxHash({ txHash: "0XABC123DEF456" }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.txHash).toBe(NORM_HASH) + const [bridgeCall] = (BridgeDepositLog.findOne as jest.Mock).mock.calls + expect(bridgeCall[0].destinationTxHash.$regex.flags).toContain("i") + }) + }) + + describe("only Bridge found → bridge_without_ibex", () => { + beforeEach(() => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + }) + + it("returns status unmatched with correct orphanType", async () => { + const result = await reconcileByTxHash({ txHash: TX_HASH }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("unmatched") + expect(result.orphanType).toBe("bridge_without_ibex") + }) + + it("does NOT call resolveOrphansByTxHash", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(resolveOrphansByTxHash).not.toHaveBeenCalled() + }) + + it("upserts orphan with key bridge:{hash}", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `bridge:${NORM_HASH}`, + orphanType: "bridge_without_ibex", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + customerId: BRIDGE_DEPOSIT.customerId, + }), + ) + }) + + it("publishes an unmatched event to PubSub", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(mockPublish).toHaveBeenCalledWith( + expect.objectContaining({ + payload: expect.objectContaining({ + status: "unmatched", + orphanType: "bridge_without_ibex", + }), + }), + ) + }) + }) + + describe("only IBEX found → ibex_without_bridge", () => { + beforeEach(() => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + }) + + it("returns status unmatched with correct orphanType", async () => { + const result = await reconcileByTxHash({ txHash: TX_HASH }) + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("unmatched") + expect(result.orphanType).toBe("ibex_without_bridge") + }) + + it("upserts orphan with key ibex:{hash}", async () => { + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `ibex:${NORM_HASH}`, + orphanType: "ibex_without_bridge", + txHash: NORM_HASH, + }), + ) + }) + }) + + describe("self-healing: second call with both sides resolves orphan", () => { + it("resolves orphan when called again after missing side arrives", async () => { + // First call: only Bridge + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + await reconcileByTxHash({ txHash: TX_HASH }) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledTimes(1) + + jest.clearAllMocks() + ;(PubSubService as jest.Mock).mockReturnValue({ publish: mockPublish }) + ;(resolveOrphansByTxHash as jest.Mock).mockResolvedValue({ resolvedCount: 1 }) + + // Second call: both sides present (IBEX webhook arrived) + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(BRIDGE_DEPOSIT)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(IBEX_RECEIVE)) + const result = await reconcileByTxHash({ txHash: TX_HASH }) + + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.status).toBe("matched") + expect(resolveOrphansByTxHash).toHaveBeenCalledWith(NORM_HASH) + expect(upsertBridgeReconciliationOrphan).not.toHaveBeenCalled() + }) + }) + + describe("Bridge query uses payment_processed state filter", () => { + it("passes state: payment_processed to BridgeDepositLog.findOne", async () => { + ;(BridgeDepositLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + ;(IbexCryptoReceiveLog.findOne as jest.Mock).mockReturnValue(makeLeanQuery(null)) + await reconcileByTxHash({ txHash: TX_HASH }) + expect(BridgeDepositLog.findOne).toHaveBeenCalledWith( + expect.objectContaining({ state: "payment_processed" }), + ) + }) + }) +}) + +// ── reconcileBridgeAndIbexDeposits (batch) ──────────────────────────────────── + +describe("reconcileBridgeAndIbexDeposits", () => { + const makeBridgeFind = (deposits: unknown[]) => ({ + lean: () => ({ exec: () => Promise.resolve(deposits) }), + }) + + describe("all deposits matched", () => { + it("returns zero orphans when every Bridge deposit has a matching IBEX receive", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([BRIDGE_DEPOSIT])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.scannedBridge).toBe(1) + expect(result.scannedIbex).toBe(1) + expect(result.bridgeWithoutIbex).toBe(0) + expect(result.ibexWithoutBridge).toBe(0) + expect(upsertBridgeReconciliationOrphan).not.toHaveBeenCalled() + }) + }) + + describe("Bridge deposit with no matching IBEX receive", () => { + it("flags as bridge_without_ibex orphan", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([BRIDGE_DEPOSIT])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.bridgeWithoutIbex).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `bridge:${NORM_HASH}`, + orphanType: "bridge_without_ibex", + txHash: NORM_HASH, + transferId: BRIDGE_DEPOSIT.transferId, + }), + ) + }) + }) + + describe("Bridge deposit with no destinationTxHash", () => { + it("flags as bridge-no-tx:{transferId} orphan", async () => { + const depositNoHash = { ...BRIDGE_DEPOSIT, destinationTxHash: undefined } + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([depositNoHash])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.bridgeWithoutIbex).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `bridge-no-tx:${BRIDGE_DEPOSIT.transferId}`, + orphanType: "bridge_without_ibex", + }), + ) + }) + }) + + describe("IBEX receive with no matching Bridge deposit", () => { + it("flags as ibex_without_bridge orphan", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + expect(result.ibexWithoutBridge).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledWith( + expect.objectContaining({ + orphanKey: `ibex:${NORM_HASH}`, + orphanType: "ibex_without_bridge", + txHash: IBEX_RECEIVE.txHash, + }), + ) + }) + }) + + describe("batch uses payment_processed state filter", () => { + it("passes state: payment_processed to BridgeDepositLog.find", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([]) + + await reconcileBridgeAndIbexDeposits() + expect(BridgeDepositLog.find).toHaveBeenCalledWith( + expect.objectContaining({ state: "payment_processed" }), + ) + }) + }) + + describe("mixed scenario", () => { + it("counts matched and unmatched independently", async () => { + const deposit2 = { ...BRIDGE_DEPOSIT, transferId: "tr_002", destinationTxHash: "0xother" } + const ibex2 = { ...IBEX_RECEIVE, txHash: "0xorphan_ibex" } + + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue( + makeBridgeFind([BRIDGE_DEPOSIT, deposit2]), + ) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue([IBEX_RECEIVE, ibex2]) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).not.toBeInstanceOf(Error) + if (result instanceof Error) return + // BRIDGE_DEPOSIT ↔ IBEX_RECEIVE match (same hash) + // deposit2 has no ibex → bridge_without_ibex + // ibex2 has no bridge → ibex_without_bridge + expect(result.scannedBridge).toBe(2) + expect(result.scannedIbex).toBe(2) + expect(result.bridgeWithoutIbex).toBe(1) + expect(result.ibexWithoutBridge).toBe(1) + expect(upsertBridgeReconciliationOrphan).toHaveBeenCalledTimes(2) + }) + }) + + describe("error handling", () => { + it("returns an Error when findIbexCryptoReceiveLogsSince fails", async () => { + ;(BridgeDepositLog.find as jest.Mock).mockReturnValue(makeBridgeFind([])) + ;(findIbexCryptoReceiveLogsSince as jest.Mock).mockResolvedValue( + new Error("mongo connection lost"), + ) + + const result = await reconcileBridgeAndIbexDeposits() + expect(result).toBeInstanceOf(Error) + }) + }) +}) diff --git a/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts new file mode 100644 index 000000000..b3249c702 --- /dev/null +++ b/test/flash/unit/services/ibex/webhook-server/routes/crypto-receive.spec.ts @@ -0,0 +1,117 @@ +jest.mock("@services/ibex/webhook-server/middleware", () => ({ + authenticate: jest.fn((_req, _res, next) => next()), + logRequest: jest.fn((_req, _res, next) => next()), +})) + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/ibex-crypto-receive-log", () => ({ + createIbexCryptoReceiveLog: jest.fn(), +})) + +jest.mock("@app/wallets", () => ({ + listWalletsByAccountId: jest.fn(), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/lock", () => ({ + LockService: jest.fn(), +})) + +jest.mock("@services/bridge/reconciliation", () => ({ + reconcileByTxHash: jest.fn().mockResolvedValue({ status: "matched" }), +})) + +import { cryptoReceiveHandler } from "@services/ibex/webhook-server/routes/crypto-receive" +import { AccountsRepository } from "@services/mongoose/accounts" +import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" +import { listWalletsByAccountId } from "@app/wallets" +import { LockService } from "@services/lock" +import { WalletCurrency } from "@domain/shared" + +const ACCOUNT_ID = "account-001" as AccountId +const WALLET_ID = "wallet-usdt-001" as WalletId +const ADDRESS = "0xabc123" +const TX_HASH = "tx-001" + +const makeResponse = () => { + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + } + return res +} + +describe("cryptoReceiveHandler", () => { + beforeEach(() => { + jest.clearAllMocks() + ;(LockService as jest.Mock).mockReturnValue({ + lockOnChainTxHash: jest.fn((_hash, fn) => fn()), + }) + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findByBridgeEthereumAddress: jest.fn().mockResolvedValue({ id: ACCOUNT_ID }), + }) + ;(createIbexCryptoReceiveLog as jest.Mock).mockResolvedValue({ id: "log-001" }) + ;(listWalletsByAccountId as jest.Mock).mockResolvedValue([ + { id: WALLET_ID, currency: WalletCurrency.Usdt }, + ]) + }) + + it("accepts Ethereum USDT receive webhooks and normalizes persisted currency/network", async () => { + const res = makeResponse() + + await cryptoReceiveHandler( + { + body: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "usdt", + network: "Ethereum", + }, + } as never, + res as never, + ) + + expect(AccountsRepository().findByBridgeEthereumAddress).toHaveBeenCalledWith(ADDRESS) + expect(createIbexCryptoReceiveLog).toHaveBeenCalledWith( + expect.objectContaining({ + txHash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "ethereum", + accountId: ACCOUNT_ID, + }), + ) + expect(res.status).toHaveBeenCalledWith(200) + expect(res.json).toHaveBeenCalledWith({ status: "success" }) + }) + + it("rejects legacy Tron USDT receive webhooks for the ETH-USDT Cash Wallet path", async () => { + const res = makeResponse() + + await cryptoReceiveHandler( + { + body: { + tx_hash: TX_HASH, + address: ADDRESS, + amount: "12.345678", + currency: "USDT", + network: "tron", + }, + } as never, + res as never, + ) + + expect(LockService().lockOnChainTxHash).not.toHaveBeenCalled() + expect(createIbexCryptoReceiveLog).not.toHaveBeenCalled() + expect(res.status).toHaveBeenCalledWith(400) + expect(res.json).toHaveBeenCalledWith({ error: "Invalid payload" }) + }) +}) From 66be031e32328df7a100dac3df1e698867103826 Mon Sep 17 00:00:00 2001 From: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> Date: Tue, 19 May 2026 09:32:20 -0400 Subject: [PATCH 11/22] ENG-297 - IBEX USD -> USDT Parity (#355) * ENG-297 fix(wallets): support USDT cash wallet parity * fix(wallets): support USDT intraledger sends --- dev/bin/eng-297-graphql-smoke.ts | 489 ++++++++++++++++++ src/app/payments/send-intraledger.ts | 14 +- src/app/payments/send-lightning.ts | 7 +- src/app/wallets/add-invoice-for-wallet.ts | 14 +- .../wallets/get-transactions-for-wallet.ts | 33 +- src/app/wallets/index.ts | 1 + src/app/wallets/send-on-chain.ts | 9 +- src/app/wallets/usd-wallet-amount.ts | 31 ++ src/app/wallets/validate.ts | 21 +- src/domain/shared/amount.ts | 13 +- src/domain/wallets/payment-input-validator.ts | 32 +- .../ln-noamount-usd-invoice-fee-probe.ts | 22 +- .../ln-noamount-usd-invoice-payment-send.ts | 17 +- .../root/mutation/ln-usd-invoice-fee-probe.ts | 22 +- .../root/mutation/onchain-usd-payment-send.ts | 14 +- .../root/query/on-chain-usd-tx-fee-query.ts | 13 +- src/services/ibex/client.ts | 47 +- src/services/ibex/types.ts | 19 +- .../app/payments/send-intraledger.spec.ts | 262 ++++++++++ .../get-transactions-for-wallet.spec.ts | 38 ++ .../app/wallets/usd-wallet-amount.spec.ts | 27 + test/flash/unit/domain/payments/index.spec.ts | 9 + .../onchain-usd-wallet-validator.spec.ts | 30 ++ .../services/ibex/client-usd-wallet.spec.ts | 135 +++++ 24 files changed, 1230 insertions(+), 89 deletions(-) create mode 100644 dev/bin/eng-297-graphql-smoke.ts create mode 100644 src/app/wallets/usd-wallet-amount.ts create mode 100644 test/flash/unit/app/payments/send-intraledger.spec.ts create mode 100644 test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts create mode 100644 test/flash/unit/app/wallets/usd-wallet-amount.spec.ts create mode 100644 test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts create mode 100644 test/flash/unit/services/ibex/client-usd-wallet.spec.ts diff --git a/dev/bin/eng-297-graphql-smoke.ts b/dev/bin/eng-297-graphql-smoke.ts new file mode 100644 index 000000000..026893d15 --- /dev/null +++ b/dev/bin/eng-297-graphql-smoke.ts @@ -0,0 +1,489 @@ +import axios from "axios" + +type Args = { + url: string + token: string + first: number + amount: number + paymentRequest?: string + address?: string + strictFixtures: boolean +} + +type GraphQLResponse = { + data?: T + errors?: { message: string; path?: string[] }[] +} + +type Wallet = { + __typename: string + id: string + walletCurrency: string + balance?: number | null + transactions?: { + edges?: { + node?: { + id: string + settlementCurrency: string + settlementAmount: number + settlementDisplayAmount: string + settlementDisplayCurrency: string + settlementDisplayFee: string + settlementFee: number + settlementPrice: { + base: number + offset: number + } + initiationVia: { __typename: string } + settlementVia: { __typename: string } + } + }[] + } | null +} + +type CheckResult = { + name: string + status: "PASS" | "FAIL" | "SKIP" + details?: string +} + +const usage = ` +Usage: + yarn ts-node --transpile-only -r tsconfig-paths/register dev/bin/eng-297-graphql-smoke.ts \\ + --url http://localhost:4002/graphql \\ + --token "$AUTH_TOKEN" \\ + [--payment-request lnbc...] \\ + [--address bc1...] \\ + [--amount 100] + +Required: + --url GraphQL endpoint + --token Bearer token for a test account + +Optional fixtures: + --payment-request Bolt11 invoice for lnUsdInvoiceFeeProbe checks + --address On-chain address for onChainUsdTxFee checks + --amount Fractional cent amount for no-amount/on-chain checks. Default: 100 + --first Transaction page size per wallet. Default: 10 + --strict-fixtures Fail instead of skip checks that require optional fixtures +` + +const parseArgs = (argv: string[]): Args => { + const parsed: Record = {} + + for (let i = 0; i < argv.length; i++) { + const arg = argv[i] + if (arg === "--help" || arg === "-h") { + console.log(usage.trim()) + process.exit(0) + } + if (!arg.startsWith("--")) continue + + const key = arg.slice(2) + if (key === "strict-fixtures") { + parsed[key] = true + continue + } + + const value = argv[i + 1] + if (!value || value.startsWith("--")) { + throw new Error(`Missing value for --${key}`) + } + parsed[key] = value + i++ + } + + const url = (parsed.url || process.env.GRAPHQL_URL) as string | undefined + const token = (parsed.token || process.env.AUTH_TOKEN || process.env.TOKEN) as + | string + | undefined + + if (!url) throw new Error("Missing --url or GRAPHQL_URL") + if (!token) throw new Error("Missing --token, AUTH_TOKEN, or TOKEN") + + return { + url, + token, + first: Number(parsed.first || 10), + amount: Number(parsed.amount || 100), + paymentRequest: (parsed["payment-request"] || parsed.paymentRequest) as + | string + | undefined, + address: parsed.address as string | undefined, + strictFixtures: Boolean(parsed["strict-fixtures"]), + } +} + +const results: CheckResult[] = [] + +const pass = (name: string, details?: string) => { + results.push({ name, status: "PASS", details }) +} + +const fail = (name: string, details?: string) => { + results.push({ name, status: "FAIL", details }) +} + +const skip = (name: string, details?: string) => { + results.push({ name, status: "SKIP", details }) +} + +function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message) +} + +const graphql = async ( + args: Args, + operationName: string, + query: string, + variables?: Record, +): Promise => { + const resp = await axios.post>( + args.url, + { operationName, query, variables }, + { + headers: { + Authorization: `Bearer ${args.token}`, + "Content-Type": "application/json", + }, + validateStatus: () => true, + }, + ) + + if (resp.status < 200 || resp.status >= 300) { + throw new Error(`HTTP ${resp.status}: ${JSON.stringify(resp.data)}`) + } + + if (resp.data.errors?.length) { + throw new Error(JSON.stringify(resp.data.errors, null, 2)) + } + + if (!resp.data.data) throw new Error("Missing GraphQL data") + return resp.data.data +} + +const recordCheck = async (name: string, check: () => Promise) => { + try { + const details = await check() + pass(name, details || undefined) + } catch (err) { + fail(name, err instanceof Error ? err.message : `${err}`) + } +} + +const optionalCheck = async ( + args: Args, + name: string, + fixtureDescription: string, + check: () => Promise, +) => { + if (args.strictFixtures) { + await recordCheck(name, check) + return + } + + try { + const details = await check() + pass(name, details || undefined) + } catch (err) { + const message = err instanceof Error ? err.message : `${err}` + if (message.includes("Missing fixture:")) { + skip(name, fixtureDescription) + return + } + fail(name, message) + } +} + +const walletSummary = (wallet: Wallet) => + `${wallet.__typename}/${wallet.walletCurrency}/${wallet.id}` + +const getWallets = async (args: Args): Promise => { + const data = await graphql<{ + me: { + defaultAccount: { + wallets: Wallet[] + } + } | null + }>( + args, + "Eng297WalletsAndTransactions", + ` + query Eng297WalletsAndTransactions($first: Int!) { + me { + defaultAccount { + wallets { + __typename + ... on BTCWallet { + id + walletCurrency + balance + transactions(first: $first) { ...TransactionConnectionFields } + } + ... on UsdWallet { + id + walletCurrency + usdBalance: balance + isExternal + transactions(first: $first) { ...TransactionConnectionFields } + } + ... on UsdtWallet { + id + walletCurrency + usdtBalance: balance + isExternal + transactions(first: $first) { ...TransactionConnectionFields } + } + } + } + } + } + + fragment TransactionConnectionFields on TransactionConnection { + edges { + node { + id + settlementCurrency + settlementAmount + settlementDisplayAmount + settlementDisplayCurrency + settlementDisplayFee + settlementFee + settlementPrice { + base + offset + } + initiationVia { __typename } + settlementVia { __typename } + } + } + } + `, + { first: args.first }, + ) + + const defaultAccount = data.me?.defaultAccount + assert(defaultAccount, "Missing me.defaultAccount") + return defaultAccount.wallets +} + +const requireWallet = (wallets: Wallet[], currency: string): Wallet => { + const wallet = wallets.find((candidate) => candidate.walletCurrency === currency) + assert(wallet, `Missing ${currency} wallet`) + return wallet as Wallet +} + +const assertNoTransactionShapeGaps = (wallet: Wallet) => { + const transactions = wallet.transactions?.edges || [] + for (const edge of transactions) { + const tx = edge.node + if (!tx) { + throw new Error(`${walletSummary(wallet)} has transaction edge without node`) + } + assert(tx.id, `${walletSummary(wallet)} transaction missing id`) + assert( + tx.settlementCurrency, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementCurrency`, + ) + assert( + tx.settlementDisplayCurrency, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementDisplayCurrency`, + ) + assert( + tx.settlementPrice, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementPrice`, + ) + assert( + tx.initiationVia?.__typename, + `${walletSummary(wallet)} transaction ${tx.id} missing initiationVia.__typename`, + ) + assert( + tx.settlementVia?.__typename, + `${walletSummary(wallet)} transaction ${tx.id} missing settlementVia.__typename`, + ) + } +} + +const runLnUsdInvoiceFeeProbe = async (args: Args, wallet: Wallet) => { + if (!args.paymentRequest) throw new Error("Missing fixture: paymentRequest") + + const data = await graphql<{ + lnUsdInvoiceFeeProbe: { + errors: { message: string }[] + amount?: number + invoiceAmount?: number + } + }>( + args, + "Eng297LnUsdInvoiceFeeProbe", + ` + mutation Eng297LnUsdInvoiceFeeProbe($input: LnUsdInvoiceFeeProbeInput!) { + lnUsdInvoiceFeeProbe(input: $input) { + errors { message } + amount + invoiceAmount + } + } + `, + { + input: { + walletId: wallet.id, + paymentRequest: args.paymentRequest, + }, + }, + ) + + const result = data.lnUsdInvoiceFeeProbe + assert( + result.errors.length === 0, + `${walletSummary(wallet)} lnUsdInvoiceFeeProbe errors: ${JSON.stringify(result.errors)}`, + ) + assert(result.amount !== undefined, `${walletSummary(wallet)} missing fee amount`) + assert( + result.invoiceAmount !== undefined, + `${walletSummary(wallet)} missing invoiceAmount`, + ) +} + +const runLnNoAmountUsdInvoiceFeeProbe = async (args: Args, wallet: Wallet) => { + if (!args.paymentRequest) throw new Error("Missing fixture: paymentRequest") + + const data = await graphql<{ + lnNoAmountUsdInvoiceFeeProbe: { + errors: { message: string }[] + amount?: number + invoiceAmount?: number + } + }>( + args, + "Eng297LnNoAmountUsdInvoiceFeeProbe", + ` + mutation Eng297LnNoAmountUsdInvoiceFeeProbe($input: LnNoAmountUsdInvoiceFeeProbeInput!) { + lnNoAmountUsdInvoiceFeeProbe(input: $input) { + errors { message } + amount + invoiceAmount + } + } + `, + { + input: { + walletId: wallet.id, + paymentRequest: args.paymentRequest, + amount: args.amount, + }, + }, + ) + + const result = data.lnNoAmountUsdInvoiceFeeProbe + assert( + result.errors.length === 0, + `${walletSummary(wallet)} lnNoAmountUsdInvoiceFeeProbe errors: ${JSON.stringify(result.errors)}`, + ) + assert(result.amount !== undefined, `${walletSummary(wallet)} missing fee amount`) + assert( + result.invoiceAmount !== undefined, + `${walletSummary(wallet)} missing invoiceAmount`, + ) +} + +const runOnChainUsdTxFee = async (args: Args, wallet: Wallet) => { + if (!args.address) throw new Error("Missing fixture: address") + + const data = await graphql<{ + onChainUsdTxFee: { + amount: number + } + }>( + args, + "Eng297OnChainUsdTxFee", + ` + query Eng297OnChainUsdTxFee( + $walletId: WalletId! + $address: OnChainAddress! + $amount: FractionalCentAmount! + ) { + onChainUsdTxFee(walletId: $walletId, address: $address, amount: $amount) { + amount + } + } + `, + { + walletId: wallet.id, + address: args.address, + amount: args.amount, + }, + ) + + assert( + data.onChainUsdTxFee.amount !== undefined, + `${walletSummary(wallet)} missing onChainUsdTxFee amount`, + ) +} + +const main = async () => { + const args = parseArgs(process.argv.slice(2)) + let wallets: Wallet[] = [] + + await recordCheck("wallet list includes USD and USDT wallets", async () => { + wallets = await getWallets(args) + const usdWallet = requireWallet(wallets, "USD") + const usdtWallet = requireWallet(wallets, "USDT") + return `${walletSummary(usdWallet)}, ${walletSummary(usdtWallet)}` + }) + + if (wallets.length > 0) { + await recordCheck("wallet transaction shapes are render-safe", async () => { + for (const wallet of wallets) assertNoTransactionShapeGaps(wallet) + const counts = wallets.map( + (wallet) => `${wallet.walletCurrency}:${wallet.transactions?.edges?.length || 0}`, + ) + return counts.join(", ") + }) + + for (const currency of ["USD", "USDT"]) { + const wallet = wallets.find((candidate) => candidate.walletCurrency === currency) + if (!wallet) continue + + await optionalCheck( + args, + `${currency} lnUsdInvoiceFeeProbe`, + "skipped; pass --payment-request to exercise Lightning fee probe", + () => runLnUsdInvoiceFeeProbe(args, wallet), + ) + + await optionalCheck( + args, + `${currency} lnNoAmountUsdInvoiceFeeProbe`, + "skipped; pass --payment-request to exercise no-amount Lightning fee probe", + () => runLnNoAmountUsdInvoiceFeeProbe(args, wallet), + ) + + await optionalCheck( + args, + `${currency} onChainUsdTxFee`, + "skipped; pass --address to exercise on-chain fee probe", + () => runOnChainUsdTxFee(args, wallet), + ) + } + } + + for (const result of results) { + const icon = result.status === "PASS" ? "✅" : result.status === "SKIP" ? "⏭️" : "❌" + console.log(`${icon} ${result.status} ${result.name}`) + if (result.details) console.log(` ${result.details}`) + } + + const failed = results.filter((result) => result.status === "FAIL") + if (failed.length > 0) { + console.error(`\n${failed.length} smoke check(s) failed`) + process.exit(1) + } + + console.log("\nENG-297 GraphQL smoke checks completed") +} + +main().catch((err) => { + console.error(err instanceof Error ? err.message : err) + process.exit(1) +}) diff --git a/src/app/payments/send-intraledger.ts b/src/app/payments/send-intraledger.ts index fb7017d09..b38633361 100644 --- a/src/app/payments/send-intraledger.ts +++ b/src/app/payments/send-intraledger.ts @@ -7,6 +7,7 @@ import { } from "@app/prices" import { removeDeviceTokens } from "@app/users/remove-device-tokens" import { validateIsBtcWallet, validateIsUsdWallet } from "@app/wallets" +import { usdWalletAmountFromInput } from "@app/wallets/usd-wallet-amount" import { InvalidLightningPaymentFlowBuilderStateError, @@ -15,11 +16,12 @@ import { } from "@domain/payments" import { AccountLevel, AccountValidator } from "@domain/accounts" import { DisplayAmountsConverter } from "@domain/fiat" -import { BigIntConversionError, checkedToUsdPaymentAmount, ErrorLevel, paymentAmountFromNumber, USDAmount, ValidationError, WalletCurrency } from "@domain/shared" +import { checkedToUsdPaymentAmount, ErrorLevel, paymentAmountFromNumber, ValidationError, WalletCurrency } from "@domain/shared" import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { ResourceExpiredLockServiceError } from "@domain/lock" import { checkedToWalletId, SettlementMethod } from "@domain/wallets" import { DeviceTokensNotRegisteredNotificationsServiceError } from "@domain/notifications" +import { MismatchedCurrencyForWalletError } from "@domain/errors" import { LockService } from "@services/lock" import { LedgerService } from "@services/ledger" @@ -74,8 +76,12 @@ const intraledgerPaymentSendWalletId = async ({ kratosUserId: recipientUserId, } = recipientAccount - const amount = USDAmount.cents(uncheckedAmount.toString()) - if (amount instanceof BigIntConversionError) return amount + if (senderWallet.currency !== recipientWallet.currency) { + return new MismatchedCurrencyForWalletError() + } + + const amount = usdWalletAmountFromInput(uncheckedAmount.toString(), senderWallet.currency) + if (amount instanceof Error) return amount const invoiceResp = await Ibex.addInvoice({ accountId: recipientWalletId, amount, @@ -132,7 +138,7 @@ export const intraledgerPaymentSendWalletIdForBtcWallet = async ( export const intraledgerPaymentSendWalletIdForUsdWallet = async ( args: IntraLedgerPaymentSendWalletIdArgs, ): Promise => { - const validated = await validateIsUsdWallet(args.senderWalletId) + const validated = await validateIsUsdWallet(args.senderWalletId, { includeUsdt: true }) return validated instanceof Error ? validated : intraledgerPaymentSendWalletId(args) } diff --git a/src/app/payments/send-lightning.ts b/src/app/payments/send-lightning.ts index 730de6755..880205c86 100644 --- a/src/app/payments/send-lightning.ts +++ b/src/app/payments/send-lightning.ts @@ -172,7 +172,7 @@ export const payNoAmountInvoiceByWalletIdForBtcWallet = async ( export const payNoAmountInvoiceByWalletIdForUsdWallet = async ( args: PayNoAmountInvoiceByWalletIdArgs, ): Promise => { - const validated = await validateIsUsdWallet(args.senderWalletId) + const validated = await validateIsUsdWallet(args.senderWalletId, { includeUsdt: true }) return validated instanceof Error ? validated : payNoAmountInvoiceByWalletId(args) } @@ -278,7 +278,10 @@ const validateNoAmountInvoicePaymentInputs = async ({ const inputPaymentAmount = senderWallet.currency === WalletCurrency.Btc ? checkedToBtcPaymentAmount(amount) - : checkedToUsdPaymentAmount(amount) + : checkedToUsdPaymentAmount( + amount, + senderWallet.currency as typeof WalletCurrency.Usd | typeof WalletCurrency.Usdt, + ) if (inputPaymentAmount instanceof Error) return inputPaymentAmount return { diff --git a/src/app/wallets/add-invoice-for-wallet.ts b/src/app/wallets/add-invoice-for-wallet.ts index 9a301d04b..49ef30994 100644 --- a/src/app/wallets/add-invoice-for-wallet.ts +++ b/src/app/wallets/add-invoice-for-wallet.ts @@ -20,9 +20,11 @@ import { import Ibex from "@services/ibex/client" import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" import { decodeInvoice } from "@domain/bitcoin/lightning/ln-invoice" -import { USDAmount } from "@domain/shared" + import { AddInvoiceResponse201 } from "ibex-client" +import { usdWalletAmountFromInput } from "./usd-wallet-amount" + import { validateIsBtcWallet, validateIsUsdWallet } from "./validate" const defaultBtcExpiration = DEFAULT_EXPIRATIONS["BTC"].delayMinutes @@ -46,7 +48,9 @@ const addInvoiceForSelf = async ({ const limitOk = await checkSelfWalletIdRateLimits(wallet.accountId) if (limitOk instanceof Error) return limitOk - const checkedAmount = amount ? USDAmount.cents(amount.toString()) : undefined + const checkedAmount = amount + ? usdWalletAmountFromInput(amount.toString(), wallet.currency) + : undefined if (checkedAmount instanceof Error) return checkedAmount const resp = await Ibex.addInvoice({ amount: checkedAmount, @@ -77,7 +81,7 @@ export const addInvoiceForSelfForUsdWallet = async ( const expiresIn = checkedToMinutes(args.expiresIn || defaultUsdExpiration) if (expiresIn instanceof Error) return expiresIn - const validated = await validateIsUsdWallet(walletId) + const validated = await validateIsUsdWallet(walletId, { includeUsdt: true }) if (validated instanceof Error) return validated return addInvoiceForSelf({ ...args, walletId, expiresIn }) } @@ -133,7 +137,7 @@ const addInvoiceForRecipient = async ({ const limitOk = await checkRecipientWalletIdRateLimits(wallet.accountId) if (limitOk instanceof Error) return limitOk - const checkedAmount = USDAmount.cents(amount.toString()) + const checkedAmount = usdWalletAmountFromInput(amount.toString(), wallet.currency) if (checkedAmount instanceof Error) return checkedAmount const resp = await Ibex.addInvoice({ amount: checkedAmount, @@ -163,7 +167,7 @@ export const addInvoiceForRecipientForUsdWallet = async ( const expiresIn = checkedToMinutes(args.expiresIn || defaultUsdExpiration) if (expiresIn instanceof Error) return expiresIn - const validated = await validateIsUsdWallet(recipientWalletId) + const validated = await validateIsUsdWallet(recipientWalletId, { includeUsdt: true }) if (validated instanceof Error) return validated return addInvoiceForRecipient({ ...args, recipientWalletId, expiresIn }) diff --git a/src/app/wallets/get-transactions-for-wallet.ts b/src/app/wallets/get-transactions-for-wallet.ts index e8925dd70..4d3bec459 100644 --- a/src/app/wallets/get-transactions-for-wallet.ts +++ b/src/app/wallets/get-transactions-for-wallet.ts @@ -1,4 +1,5 @@ import { PartialResult } from "@app/partial-result" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" import { baseLogger } from "@services/logger" @@ -32,9 +33,39 @@ export const getTransactionsForWallets = async ({ }) } +const currencyFromIbexCurrencyId = (currencyId: number | undefined): WalletCurrency | undefined => { + if (currencyId === USDAmount.currencyId) return WalletCurrency.Usd + if (currencyId === USDTAmount.currencyId) return WalletCurrency.Usdt + return undefined +} + export const toWalletTransactions = (ibexResp: GResponse200): IbexTransaction[] => { return ibexResp.map(trx => { - const currency = (trx.currencyId === 3 ? "USD" : "BTC") as WalletCurrency // WalletCurrency: "USD" | "BTC", + const currency = currencyFromIbexCurrencyId(trx.currencyId) + + if (!currency) { + baseLogger.error(`Failed to parse Ibex transaction currency. { WalletId: ${trx.accountId}, TransactionId: ${trx.id}, currencyId: ${trx.currencyId} }`) + return { + walletId: (trx.accountId || "") as WalletId, + settlementAmount: 0 as Satoshis, + settlementFee: 0 as Satoshis, + settlementCurrency: WalletCurrency.Usd, + settlementDisplayAmount: `${trx.amount}`, + settlementDisplayFee: `${trx.networkFee}`, + settlementDisplayPrice: { + base: 0n, + offset: 0n, + displayCurrency: "USD" as DisplayCurrency, + walletCurrency: WalletCurrency.Usd, + }, + createdAt: trx.createdAt ? new Date(trx.createdAt) : new Date(), + id: trx.id || "null", + status: "success" as TxStatus, + memo: null, + initiationVia: { type: "unknown" }, + settlementVia: { type: "unknown" }, + } as UnknownTypeTransaction + } const settlementDisplayPrice: WalletMinorUnitDisplayPrice = { base: trx.exchangeRateCurrencySats ? BigInt(Math.floor(trx.exchangeRateCurrencySats)) : 0n, diff --git a/src/app/wallets/index.ts b/src/app/wallets/index.ts index 3839fed5a..94210271c 100644 --- a/src/app/wallets/index.ts +++ b/src/app/wallets/index.ts @@ -17,6 +17,7 @@ export * from "./settle-payout-txn" export * from "./update-legacy-on-chain-receipt" export * from "./update-pending-invoices" export * from "./validate" +export * from "./usd-wallet-amount" import { WalletsRepository } from "@services/mongoose" diff --git a/src/app/wallets/send-on-chain.ts b/src/app/wallets/send-on-chain.ts index a50ed3034..fddf17f32 100644 --- a/src/app/wallets/send-on-chain.ts +++ b/src/app/wallets/send-on-chain.ts @@ -3,7 +3,8 @@ import { NETWORK } from "@config" import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { checkedToOnChainAddress } from "@domain/bitcoin/onchain" import { UnsupportedCurrencyError } from "@domain/errors" -import { ErrorLevel, USDAmount } from "@domain/shared" +import { ErrorLevel } from "@domain/shared" +import { UsdWalletAmount } from "@services/ibex/types" import { OnchainUsdPaymentValidator } from "@domain/wallets" import { AccountsRepository, WalletsRepository } from "@services/mongoose" @@ -19,15 +20,15 @@ type PayOnChainByWalletIdWithoutCurrencyArgs = { address: string speed: PayoutSpeed memo: string | null - amount?: number | FractionalCentAmount | USDAmount + amount?: number | FractionalCentAmount | UsdWalletAmount } type PayOnChainByUsdArgs = PayOnChainByWalletIdWithoutCurrencyArgs & { - amount: USDAmount + amount: UsdWalletAmount } type PayOnChainByWalletIdForUsdWalletArgs = PayOnChainByWalletIdWithoutCurrencyArgs & { - amount: number | FractionalCentAmount | USDAmount + amount: number | FractionalCentAmount | UsdWalletAmount } /* diff --git a/src/app/wallets/usd-wallet-amount.ts b/src/app/wallets/usd-wallet-amount.ts new file mode 100644 index 000000000..e2c166e4f --- /dev/null +++ b/src/app/wallets/usd-wallet-amount.ts @@ -0,0 +1,31 @@ +import { UnsupportedCurrencyError } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +import { WalletsRepository } from "@services/mongoose" + +export type UsdWalletAmount = USDAmount | USDTAmount + +export const usdWalletAmountFromInput = ( + amount: string | number, + currency: WalletCurrency, +): UsdWalletAmount | ApplicationError => { + const raw = amount.toString() + + if (currency === WalletCurrency.Usd) return USDAmount.cents(raw) + if (currency === WalletCurrency.Usdt) return USDTAmount.smallestUnits(raw) + + return new UnsupportedCurrencyError(`USD wallet amount unsupported for ${currency}`) +} + +export const usdWalletAmountFromWalletId = async ({ + walletId, + amount, +}: { + walletId: WalletId + amount: string | number +}): Promise => { + const wallet = await WalletsRepository().findById(walletId) + if (wallet instanceof Error) return wallet + + return usdWalletAmountFromInput(amount, wallet.currency) +} diff --git a/src/app/wallets/validate.ts b/src/app/wallets/validate.ts index a214ada91..3b22093f5 100644 --- a/src/app/wallets/validate.ts +++ b/src/app/wallets/validate.ts @@ -9,7 +9,7 @@ export const validateIsBtcWallet = async ( const wallet = await WalletsRepository().findById(walletId) if (wallet instanceof Error) return wallet - if (wallet.currency === WalletCurrency.Usd) { + if (wallet.currency !== WalletCurrency.Btc) { return new MismatchedCurrencyForWalletError() } return true @@ -17,11 +17,28 @@ export const validateIsBtcWallet = async ( export const validateIsUsdWallet = async ( walletId: WalletId, + args?: { includeUsdt?: boolean }, ): Promise => { const wallet = await WalletsRepository().findById(walletId) if (wallet instanceof Error) return wallet - if (wallet.currency === WalletCurrency.Btc) { + const isAllowed = + wallet.currency === WalletCurrency.Usd || + (args?.includeUsdt === true && wallet.currency === WalletCurrency.Usdt) + + if (!isAllowed) { + return new MismatchedCurrencyForWalletError() + } + return true +} + +export const validateIsUsdtWallet = async ( + walletId: WalletId, +): Promise => { + const wallet = await WalletsRepository().findById(walletId) + if (wallet instanceof Error) return wallet + + if (wallet.currency !== WalletCurrency.Usdt) { return new MismatchedCurrencyForWalletError() } return true diff --git a/src/domain/shared/amount.ts b/src/domain/shared/amount.ts index 0e867aad4..2f5438784 100644 --- a/src/domain/shared/amount.ts +++ b/src/domain/shared/amount.ts @@ -90,9 +90,16 @@ export const UsdPaymentAmount = (cents: bigint): UsdPaymentAmount => { } } -export const checkedToUsdPaymentAmount = ( +export function checkedToUsdPaymentAmount( amount: number | null, -): UsdPaymentAmount | ValidationError => { +): UsdPaymentAmount | ValidationError +export function checkedToUsdPaymentAmount< + T extends typeof WalletCurrency.Usd | typeof WalletCurrency.Usdt, +>(amount: number | null, currency: T): PaymentAmount | ValidationError +export function checkedToUsdPaymentAmount( + amount: number | null, + currency: typeof WalletCurrency.Usd | typeof WalletCurrency.Usdt = WalletCurrency.Usd, +): UsdPaymentAmount | PaymentAmount | ValidationError { if (amount === null) { return new InvalidUsdPaymentAmountError() } @@ -105,7 +112,7 @@ export const checkedToUsdPaymentAmount = ( return new InvalidUsdPaymentAmountError() } if (!(amount && amount > 0)) return new InvalidUsdPaymentAmountError() - return paymentAmountFromNumber({ amount, currency: WalletCurrency.Usd }) + return paymentAmountFromNumber({ amount, currency }) } export const paymentAmountFromNumber = ({ diff --git a/src/domain/wallets/payment-input-validator.ts b/src/domain/wallets/payment-input-validator.ts index eefe1b093..0491b3786 100644 --- a/src/domain/wallets/payment-input-validator.ts +++ b/src/domain/wallets/payment-input-validator.ts @@ -1,4 +1,10 @@ -import { USDAmount, ValidationError, isUsdWallet, validator } from "@domain/shared" +import { + USDAmount, + USDTAmount, + ValidationError, + WalletCurrency, + validator, +} from "@domain/shared" import { isActiveAccount, walletBelongsToAccount } from "@domain/accounts" import { SendOnchainArgs } from "@services/ibex/types" @@ -8,23 +14,35 @@ import { SendOnchainArgs } from "@services/ibex/types" // else return true // } -const checkOnchainMin = async (o: { amount: USDAmount }) => { +const isUsdWalletForOnChainPayment = async (o: { wallet: Wallet }) => { + if ( + o.wallet.currency === WalletCurrency.Usd || + o.wallet.currency === WalletCurrency.Usdt + ) { + return true + } + return new ValidationError(`Expected USD, got ${o.wallet.currency}`) +} + +const checkOnchainMin = async (o: { amount: USDAmount | USDTAmount }) => { // TODO: Currently relying on Ibex to enforce dust limits // const { dustThreshold } = getOnChainWalletConfig() // const minBtc = BtcAmount.sats(dustThreshold.toString()) // const btcPrice = await PriceService().getUsdCentRealTimePrice(_) // if (btcPrice instanceof PriceServiceError) return new ValidationError(btcPrice) // const minUsd = minBtc.convertAtRate(MoneyAmount.from("50000", WalletCurrency.Usd)) - const minUsd = USDAmount.ZERO - return o.amount.isGreaterThan(minUsd) - ? true - : new ValidationError(`Amount must be greater than ${minUsd.asDollars()}`) + const isGreaterThanZero = + o.amount instanceof USDTAmount + ? o.amount.isGreaterThan(USDTAmount.ZERO) + : o.amount.isGreaterThan(USDAmount.ZERO) + + return isGreaterThanZero ? true : new ValidationError("Amount must be greater than 0") } type SendOnchainArgsWithContext = SendOnchainArgs & { wallet: Wallet; account: Account } export const OnchainUsdPaymentValidator = validator([ - isUsdWallet, + isUsdWalletForOnChainPayment, isActiveAccount, walletBelongsToAccount, checkOnchainMin, diff --git a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts index 1950be8a7..89fe1592f 100644 --- a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts +++ b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-fee-probe.ts @@ -1,22 +1,15 @@ -import { InvalidFeeProbeStateError } from "@domain/bitcoin/lightning" - -// import { Payments } from "@app" +import { usdWalletAmountFromWalletId, UsdWalletAmount } from "@app/wallets" import { GT } from "@graphql/index" import WalletId from "@graphql/shared/types/scalar/wallet-id" -import CentAmount from "@graphql/public/types/scalar/cent-amount" import CentAmountPayload from "@graphql/public/types/payload/cent-amount" import LnPaymentRequest from "@graphql/shared/types/scalar/ln-payment-request" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" -import { normalizePaymentAmount } from "../../../shared/root/mutation" - // FLASH FORK: import ibex dependencies import Ibex from "@services/ibex/client" -import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" -import { GetFeeEstimateResponse200 } from "ibex-client" -import { BigIntConversionError, checkedToUsdPaymentAmount, USDAmount, ValidationError } from "@domain/shared" +import { IbexError } from "@services/ibex/errors" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import { IbexFeeEstimation } from "@services/ibex/types" @@ -55,9 +48,14 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ // }) // TODO: Move Ibex call to Payments interface - const checkedAmount = USDAmount.cents(amount.toString()) - if (checkedAmount instanceof BigIntConversionError) return checkedAmount - const resp: IbexFeeEstimation | IbexError = await Ibex.getLnFeeEstimation({ + const checkedAmount = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) + if (checkedAmount instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(checkedAmount)] } + } + const resp: IbexFeeEstimation | IbexError = await Ibex.getLnFeeEstimation({ invoice: paymentRequest as Bolt11, send: checkedAmount, }) diff --git a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts index 0ffbb8aca..e8d8d6227 100644 --- a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts +++ b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts @@ -13,10 +13,10 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract // FLASH FORK: import ibex dependencies import { PaymentSendStatus } from "@domain/bitcoin/lightning" +import { usdWalletAmountFromWalletId } from "@app/wallets" import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" -import { BigIntConversionError, checkedToUsdPaymentAmount, paymentAmountFromNumber, USDAmount, ValidationError, WalletCurrency } from "@domain/shared" const LnNoAmountUsdInvoicePaymentInput = GT.Input({ name: "LnNoAmountUsdInvoicePaymentInput", @@ -90,9 +90,16 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< if (!domainAccount) throw new Error("Authentication required") // eslint-disable-next-line @typescript-eslint/no-explicit-any - - const usCents = USDAmount.cents(amount.toString()) - if (usCents instanceof BigIntConversionError) return usCents + const usCents = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) + if (usCents instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(usCents)], + } + } const PayLightningInvoice = await Ibex.payInvoice({ invoice: paymentRequest as Bolt11, accountId: walletId, @@ -102,7 +109,7 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< if (PayLightningInvoice instanceof IbexError) { return { status: "failed", - errors: [mapAndParseErrorForGqlResponse(PayLightningInvoice)] + errors: [mapAndParseErrorForGqlResponse(PayLightningInvoice)], } } diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts index 8c2223ff9..ccfb2c7bb 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts @@ -1,7 +1,3 @@ -import { InvalidFeeProbeStateError } from "@domain/bitcoin/lightning" - -// import { Payments } from "@app" - import { GT } from "@graphql/index" import WalletId from "@graphql/shared/types/scalar/wallet-id" import CentAmountPayload from "@graphql/public/types/payload/cent-amount" @@ -10,17 +6,10 @@ import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import { checkedToWalletId } from "@domain/wallets" -import { normalizePaymentAmount } from "../../../shared/root/mutation" - -// FLASH FORK: import ibex dependencies import Ibex from "@services/ibex/client" -import { IbexError, UnexpectedIbexResponse } from "@services/ibex/errors" -import { ValidationError, WalletCurrency } from "@domain/shared" -import { baseLogger } from "@services/logger" -import IError from "@graphql/shared/types/abstract/error" -import USDCentsScalar from "@graphql/shared/types/scalar/usd-cents" -import CentAmount from "@graphql/public/types/scalar/cent-amount" +import { IbexError } from "@services/ibex/errors" +import { WalletsRepository } from "@services/mongoose" // import { IbexRoutes } from "../../../../services/ibex/Routes" // import { requestIBexPlugin } from "../../../../services/ibex/IbexHelper" @@ -86,9 +75,14 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< // uncheckedPaymentRequest: paymentRequest, // }) + const wallet = await WalletsRepository().findById(walletIdChecked) + if (wallet instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(wallet)] } + } + const resp = await Ibex.getLnFeeEstimation({ invoice: paymentRequest as Bolt11, - // send: { currencyId: USDollars.currencyId }, + currency: wallet.currency, }) if (resp instanceof IbexError) return { errors: [mapAndParseErrorForGqlResponse(resp)] } diff --git a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts index bdd8f4b83..b45224a6c 100644 --- a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts +++ b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts @@ -11,7 +11,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { Wallets } from "@app/index" -import { BigIntConversionError, USDAmount } from "@domain/shared" +import { usdWalletAmountFromWalletId } from "@app/wallets" const OnChainUsdPaymentSendInput = GT.Input({ name: "OnChainUsdPaymentSendInput", @@ -67,8 +67,16 @@ const OnChainUsdPaymentSendMutation = GT.Field< } if (!domainAccount) throw new Error("Authentication required") - const usdAmount = USDAmount.cents(amount.toString()) - if (usdAmount instanceof BigIntConversionError) return usdAmount + const usdAmount = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) + if (usdAmount instanceof Error) { + return { + status: PaymentSendStatus.Failure.value, + errors: [mapAndParseErrorForGqlResponse(usdAmount)], + } + } const result = await Wallets.payOnChainByWalletId({ senderAccount: domainAccount, diff --git a/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts b/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts index 90b96e51d..63977876f 100644 --- a/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts +++ b/src/graphql/public/root/query/on-chain-usd-tx-fee-query.ts @@ -1,11 +1,7 @@ import { PayoutSpeed as DomainPayoutSpeed } from "@domain/bitcoin/onchain" -import { paymentAmountFromNumber, USDAmount, ValidationError, WalletCurrency } from "@domain/shared" - -// import { Wallets } from "@app" +import { usdWalletAmountFromWalletId } from "@app/wallets" import { GT } from "@graphql/index" -import { mapError } from "@graphql/error-map" - import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import OnChainAddress from "@graphql/shared/types/scalar/on-chain-address" import PayoutSpeed from "@graphql/public/types/scalar/payout-speed" @@ -13,8 +9,6 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import OnChainUsdTxFee from "@graphql/public/types/object/onchain-usd-tx-fee" -import { normalizePaymentAmount } from "../../../shared/root/mutation" - // FLASH FORK: import ibex dependencies import Ibex from "@services/ibex/client" @@ -46,7 +40,10 @@ const OnChainUsdTxFeeQuery = GT.Field({ // speed, // }) - const send = USDAmount.cents(amount.toString()) + const send = await usdWalletAmountFromWalletId({ + walletId, + amount: amount.toString(), + }) if (send instanceof Error) return send const resp = await Ibex.estimateOnchainFee(send, address) diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index da0b4b1f4..56c23c72e 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -40,6 +40,7 @@ import { CryptoReceiveInfo, CreateCryptoReceiveInfoRequest, IbexCurrency, + UsdWalletAmount, } from "./types" import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./errors" @@ -58,6 +59,30 @@ const createAccount = async ( return Ibex.createAccount({ name, currencyId }).then(errorHandler) } +const ibexCurrencyIdForUsdAmount = (amount: UsdWalletAmount): IbexCurrencyId => { + if (amount instanceof USDAmount) return USDAmount.currencyId + return USDTAmount.currencyId +} + +const ibexCurrencyIdForUsdWalletCurrency = ( + currency?: WalletCurrency, +): IbexCurrencyId => { + if (currency === WalletCurrency.Usdt) return USDTAmount.currencyId + return USDAmount.currencyId +} + +const parseIbexUsdAmount = ( + amount: number | string, + currencyId: IbexCurrencyId, +): UsdWalletAmount | ParseError => { + const parsed = + currencyId === USDTAmount.currencyId + ? USDTAmount.fromNumber(amount.toString()) + : USDAmount.dollars(amount.toString()) + + return parsed instanceof Error ? new ParseError(parsed) : parsed +} + const parseAccountBalance = ( balance: number | undefined, currency: WalletCurrency, @@ -132,16 +157,16 @@ const invoiceFromHash = async ( return Ibex.invoiceFromHash({ invoice_hash }).then(errorHandler) } -// Only supports USD for now const getLnFeeEstimation = async ( args: GetFeeEstimateArgs, -): Promise => { - const currencyId = USDAmount.currencyId - // const amount = (args.send instanceof IbexCurrency) ? args.send.amount.toString() : undefined +): Promise | IbexError> => { + const currencyId = args.send + ? ibexCurrencyIdForUsdAmount(args.send) + : ibexCurrencyIdForUsdWalletCurrency(args.currency) const resp = await Ibex.getFeeEstimation({ bolt11: args.invoice as string, - amount: args.send?.asDollars(8), + amount: args.send?.toIbex().toString(), currencyId: currencyId.toString(), }) if (resp instanceof Error) return new IbexError(resp) @@ -150,10 +175,10 @@ const getLnFeeEstimation = async ( else if (resp.invoiceAmount === null || resp.invoiceAmount === undefined) return new UnexpectedIbexResponse("invoiceAmount not found.") else { - const fee = USDAmount.dollars(resp.amount) - if (fee instanceof Error) return new ParseError(fee) - const invoiceAmount = USDAmount.dollars(resp.invoiceAmount) - if (invoiceAmount instanceof Error) return new ParseError(invoiceAmount) + const fee = parseIbexUsdAmount(resp.amount, currencyId) + if (fee instanceof Error) return fee + const invoiceAmount = parseIbexUsdAmount(resp.invoiceAmount, currencyId) + if (invoiceAmount instanceof Error) return invoiceAmount return { fee, invoice: invoiceAmount, @@ -190,12 +215,12 @@ const sendOnchain = async ( } const estimateOnchainFee = async ( - send: USDAmount, + send: UsdWalletAmount, address: OnChainAddress, ): Promise => { return Ibex.estimateFeeV2({ "amount": send.toIbex(), - "currency-id": USDAmount.currencyId.toString(), + "currency-id": ibexCurrencyIdForUsdAmount(send).toString(), address, }).then(errorHandler) } diff --git a/src/services/ibex/types.ts b/src/services/ibex/types.ts index 16764d137..c7fde4b71 100644 --- a/src/services/ibex/types.ts +++ b/src/services/ibex/types.ts @@ -1,26 +1,29 @@ -import { USDAmount, USDTAmount } from "@domain/shared" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +export type UsdWalletAmount = USDAmount | USDTAmount export type PayInvoiceArgs = { accountId: IbexAccountId invoice: Bolt11 - send?: USDAmount // must match currency of account + send?: UsdWalletAmount // must match currency of account } export type SendOnchainArgs = { accountId: IbexAccountId // source of funds address: OnChainAddress // destination - amount: USDAmount + amount: UsdWalletAmount } // Ibex supports fee estimation in different currencies export type GetFeeEstimateArgs = { invoice: Bolt11 - send?: USDAmount + send?: UsdWalletAmount + currency?: WalletCurrency } -export type IbexFeeEstimation = { - fee: USDAmount - invoice: USDAmount +export type IbexFeeEstimation = { + fee: T + invoice: T } export type IbexAccountDetails = { @@ -32,7 +35,7 @@ export type IbexAccountDetails = { export type IbexInvoiceArgs = { accountId: IbexAccountId - amount?: USDAmount + amount?: UsdWalletAmount memo: string expiration?: Seconds } diff --git a/test/flash/unit/app/payments/send-intraledger.spec.ts b/test/flash/unit/app/payments/send-intraledger.spec.ts new file mode 100644 index 000000000..179ff1259 --- /dev/null +++ b/test/flash/unit/app/payments/send-intraledger.spec.ts @@ -0,0 +1,262 @@ +const mockAddInvoice = jest.fn() +const mockPayInvoice = jest.fn() +const mockFindWalletById = jest.fn() +const mockFindAccountById = jest.fn() + +jest.mock("@config", () => ({ + getCallbackServiceConfig: jest.fn(() => ({})), + getValuesToSkipProbe: jest.fn(() => []), +})) + +jest.mock("@services/tracing", () => ({ + addAttributesToCurrentSpan: jest.fn(), + recordExceptionInCurrentSpan: jest.fn(), +})) + +jest.mock("@app/prices", () => ({ + btcFromUsdMidPriceFn: jest.fn(), + getCurrentPriceAsDisplayPriceRatio: jest.fn(), + usdFromBtcMidPriceFn: jest.fn(), +})) + +jest.mock("@app/wallets", () => { + const { MismatchedCurrencyForWalletError } = jest.requireActual("@domain/errors") + const { WalletCurrency } = jest.requireActual("@domain/shared") + + const validateIsBtcWallet = jest.fn(async () => true) + const validateIsUsdWallet = jest.fn(async (walletId, args) => { + const wallet = await mockFindWalletById(walletId) + if (wallet instanceof Error) return wallet + + if ( + wallet.currency === WalletCurrency.Usd || + (args?.includeUsdt === true && wallet.currency === WalletCurrency.Usdt) + ) { + return true + } + + return new MismatchedCurrencyForWalletError() + }) + + return { validateIsBtcWallet, validateIsUsdWallet } +}) + +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + addInvoice: (...args: unknown[]) => mockAddInvoice(...args), + payInvoice: (...args: unknown[]) => mockPayInvoice(...args), + }, +})) + +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(() => ({ + findById: (...args: unknown[]) => mockFindAccountById(...args), + })), + WalletsRepository: jest.fn(() => ({ + findById: (...args: unknown[]) => mockFindWalletById(...args), + })), + UsersRepository: jest.fn(), +})) + +jest.mock("@services/dealer-price", () => ({ + DealerPriceService: jest.fn(() => ({})), +})) + +jest.mock("@services/lock", () => ({ + LockService: jest.fn(() => ({})), +})) + +jest.mock("@services/ledger", () => ({ + LedgerService: jest.fn(() => ({})), +})) + +jest.mock("@services/ledger/facade", () => ({})) + +jest.mock("@services/notifications", () => ({ + NotificationsService: jest.fn(() => ({})), +})) + +jest.mock("@services/svix", () => ({ + CallbackService: jest.fn(() => ({})), +})) + +jest.mock("@app/payments/helpers", () => ({ + addContactsAfterSend: jest.fn(), + checkIntraledgerLimits: jest.fn(async () => true), + checkTradeIntraAccountLimits: jest.fn(async () => true), + getPriceRatioForLimits: jest.fn(async () => ({})), +})) + +import { intraledgerPaymentSendWalletIdForUsdWallet } from "@app/payments/send-intraledger" +import { MismatchedCurrencyForWalletError } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +const senderUsdWalletId = "11111111-1111-4111-8111-111111111111" as WalletId +const senderUsdtWalletId = "22222222-2222-4222-8222-222222222222" as WalletId +const recipientUsdWalletId = "33333333-3333-4333-8333-333333333333" as WalletId +const recipientUsdtWalletId = "44444444-4444-4444-8444-444444444444" as WalletId + +const activeAccount = (id: string) => + ({ + id, + status: "active", + level: 1, + }) as unknown as Account + +const wallet = ({ + id, + accountId, + currency, +}: { + id: string + accountId: string + currency: string +}) => + ({ + id, + accountId, + currency, + }) as unknown as Wallet + +describe("intraledgerPaymentSendWalletIdForUsdWallet", () => { + beforeEach(() => { + jest.clearAllMocks() + + mockFindAccountById.mockImplementation(async (accountId: AccountId) => + activeAccount(accountId as string), + ) + mockAddInvoice.mockResolvedValue({ invoice: { bolt11: "lnbc1recipient" } }) + mockPayInvoice.mockResolvedValue({ status: 2 }) + }) + + it("keeps USD to USD using cent amount semantics", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdWalletId) { + return wallet({ + id: senderUsdWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usd, + }) + } + return wallet({ + id: recipientUsdWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usd, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdWalletId, + recipientWalletId: recipientUsdWalletId, + amount: 19446, + memo: "USD intraledger", + }) + + expect(result).toEqual({ value: "success" }) + expect(mockAddInvoice).toHaveBeenCalledWith({ + accountId: recipientUsdWalletId, + amount: expect.any(USDAmount), + memo: "USD intraledger", + }) + expect(mockAddInvoice.mock.calls[0][0].amount.asCents()).toBe("19446") + expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(194.46) + expect(mockPayInvoice).toHaveBeenCalledWith({ + accountId: senderUsdWalletId, + invoice: "lnbc1recipient", + }) + }) + + it("sends USDT to USDT using USDT micro-unit amount semantics", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdtWalletId) { + return wallet({ + id: senderUsdtWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usdt, + }) + } + return wallet({ + id: recipientUsdtWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usdt, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdtWalletId, + recipientWalletId: recipientUsdtWalletId, + amount: 19446, + memo: "USDT intraledger", + }) + + expect(result).toEqual({ value: "success" }) + expect(mockAddInvoice).toHaveBeenCalledWith({ + accountId: recipientUsdtWalletId, + amount: expect.any(USDTAmount), + memo: "USDT intraledger", + }) + expect(mockAddInvoice.mock.calls[0][0].amount.asSmallestUnits()).toBe("19446") + expect(mockAddInvoice.mock.calls[0][0].amount.toIbex()).toBe(0.019446) + expect(mockPayInvoice).toHaveBeenCalledWith({ + accountId: senderUsdtWalletId, + invoice: "lnbc1recipient", + }) + }) + + it("rejects USD to USDT as a mixed-currency intraledger payment", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdWalletId) { + return wallet({ + id: senderUsdWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usd, + }) + } + return wallet({ + id: recipientUsdtWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usdt, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdWalletId, + recipientWalletId: recipientUsdtWalletId, + amount: 100, + memo: "mixed currency", + }) + + expect(result).toBeInstanceOf(MismatchedCurrencyForWalletError) + expect(mockAddInvoice).not.toHaveBeenCalled() + expect(mockPayInvoice).not.toHaveBeenCalled() + }) + + it("rejects USDT to USD as a mixed-currency intraledger payment", async () => { + mockFindWalletById.mockImplementation(async (walletId: WalletId) => { + if (walletId === senderUsdtWalletId) { + return wallet({ + id: senderUsdtWalletId, + accountId: "sender-account", + currency: WalletCurrency.Usdt, + }) + } + return wallet({ + id: recipientUsdWalletId, + accountId: "recipient-account", + currency: WalletCurrency.Usd, + }) + }) + + const result = await intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: senderUsdtWalletId, + recipientWalletId: recipientUsdWalletId, + amount: 100, + memo: "mixed currency", + }) + + expect(result).toBeInstanceOf(MismatchedCurrencyForWalletError) + expect(mockAddInvoice).not.toHaveBeenCalled() + expect(mockPayInvoice).not.toHaveBeenCalled() + }) +}) diff --git a/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts new file mode 100644 index 000000000..d47e1775f --- /dev/null +++ b/test/flash/unit/app/wallets/get-transactions-for-wallet.spec.ts @@ -0,0 +1,38 @@ +import { toWalletTransactions } from "@app/wallets/get-transactions-for-wallet" +import { WalletCurrency } from "@domain/shared" +import { GResponse200 } from "ibex-client" + +describe("toWalletTransactions", () => { + it("maps IBEX USDT currency id to USDT wallet currency", () => { + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: 19446, + currencyId: 29, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementCurrency).toBe(WalletCurrency.Usdt) + expect(transaction.settlementAmount).toBe(19446) + }) + + it("does not silently classify unknown IBEX currency ids as BTC", () => { + const [transaction] = toWalletTransactions([ + { + id: "trx-id", + accountId: "wallet-id", + amount: 100, + currencyId: 999, + transactionTypeId: 1, + createdAt: "2026-05-13T00:00:00.000Z", + }, + ] as GResponse200) + + expect(transaction.settlementCurrency).not.toBe(WalletCurrency.Btc) + expect(transaction.initiationVia.type).toBe("unknown") + expect(transaction.settlementVia.type).toBe("unknown") + }) +}) diff --git a/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts b/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts new file mode 100644 index 000000000..4c2573745 --- /dev/null +++ b/test/flash/unit/app/wallets/usd-wallet-amount.spec.ts @@ -0,0 +1,27 @@ +import { usdWalletAmountFromInput } from "@app/wallets/usd-wallet-amount" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" + +describe("usdWalletAmountFromInput", () => { + it("treats USD wallet input as cents", () => { + const amount = usdWalletAmountFromInput("19446", WalletCurrency.Usd) + + expect(amount).toBeInstanceOf(USDAmount) + expect((amount as USDAmount).asCents()).toBe("19446") + expect((amount as USDAmount).toIbex()).toBe(194.46) + }) + + it("treats USDT wallet input as micro-USDT", () => { + const amount = usdWalletAmountFromInput("19446", WalletCurrency.Usdt) + + expect(amount).toBeInstanceOf(USDTAmount) + expect((amount as USDTAmount).asSmallestUnits()).toBe("19446") + expect((amount as USDTAmount).asNumber()).toBe("0.019446") + expect((amount as USDTAmount).toIbex()).toBe(0.019446) + }) + + it("rejects BTC", () => { + const amount = usdWalletAmountFromInput("19446", WalletCurrency.Btc) + + expect(amount).toBeInstanceOf(Error) + }) +}) diff --git a/test/flash/unit/domain/payments/index.spec.ts b/test/flash/unit/domain/payments/index.spec.ts index 2aecb5119..95f28c06a 100644 --- a/test/flash/unit/domain/payments/index.spec.ts +++ b/test/flash/unit/domain/payments/index.spec.ts @@ -37,6 +37,15 @@ describe("checkedToBtcPaymentAmount", () => { }) describe("checkedToUsdPaymentAmount", () => { + it("returns USDT payment amounts when called with a USDT wallet currency", () => { + expect(checkedToUsdPaymentAmount(19446, WalletCurrency.Usdt)).toEqual( + expect.objectContaining({ + currency: WalletCurrency.Usdt, + amount: 19446n, + }), + ) + }) + it("errors on null", () => { expect(checkedToUsdPaymentAmount(null)).toBeInstanceOf(InvalidUsdPaymentAmountError) }) diff --git a/test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts b/test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts new file mode 100644 index 000000000..241dae0f7 --- /dev/null +++ b/test/flash/unit/domain/wallets/onchain-usd-wallet-validator.spec.ts @@ -0,0 +1,30 @@ +import { AccountStatus } from "@domain/accounts" +import { OnchainUsdPaymentValidator } from "@domain/wallets" +import { USDTAmount, WalletCurrency, isValidated } from "@domain/shared" + +const account = { + id: "account-id" as AccountId, + status: AccountStatus.Active, +} as Account + +const usdtWallet = { + id: "wallet-id" as WalletId, + accountId: account.id, + currency: WalletCurrency.Usdt, +} as Wallet + +describe("Onchain USD wallet payment validation", () => { + it("accepts USDT USD wallets", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + + const result = await OnchainUsdPaymentValidator({ + account, + wallet: usdtWallet, + accountId: usdtWallet.id as IbexAccountId, + address: "0xabc" as OnChainAddress, + amount, + }) + + expect(isValidated(result)).toBe(true) + }) +}) diff --git a/test/flash/unit/services/ibex/client-usd-wallet.spec.ts b/test/flash/unit/services/ibex/client-usd-wallet.spec.ts new file mode 100644 index 000000000..0c6dc89e4 --- /dev/null +++ b/test/flash/unit/services/ibex/client-usd-wallet.spec.ts @@ -0,0 +1,135 @@ +const mockAddInvoice = jest.fn() +const mockGetFeeEstimation = jest.fn() +const mockEstimateFeeV2 = jest.fn() + +jest.mock("@services/ibex/cache", () => ({ + Redis: { + get: jest.fn(), + set: jest.fn(), + delete: jest.fn(), + }, +})) + +jest.mock("ibex-client", () => { + class AuthenticationError extends Error {} + class ApiError extends Error {} + class UnexpectedResponseError extends Error {} + class IbexClientError extends Error {} + + return { + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + authentication: { + storage: { + getAccessToken: jest.fn(), + setAccessToken: jest.fn(), + setRefreshToken: jest.fn(), + }, + }, + addInvoice: (...args: unknown[]) => mockAddInvoice(...args), + getFeeEstimation: (...args: unknown[]) => mockGetFeeEstimation(...args), + estimateFeeV2: (...args: unknown[]) => mockEstimateFeeV2(...args), + })), + AuthenticationError, + ApiError, + UnexpectedResponseError, + IbexClientError, + } +}) + +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import Ibex from "@services/ibex/client" + +describe("IBEX USD wallet amount handling", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("creates USDT invoices using decimal USDT amount", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + mockAddInvoice.mockResolvedValue({ invoice: { bolt11: "lnbc1" } }) + + await Ibex.addInvoice({ + accountId: "ibex-usdt-account" as IbexAccountId, + amount, + memo: "usdt invoice", + }) + + expect(mockAddInvoice).toHaveBeenCalledWith( + expect.objectContaining({ + accountId: "ibex-usdt-account", + amount: 0.019446, + }), + ) + }) + + it("estimates LN fees with USDT currency id and parses USDT amounts", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + mockGetFeeEstimation.mockResolvedValue({ amount: 0.000123, invoiceAmount: 0.019446 }) + + const result = await Ibex.getLnFeeEstimation({ + invoice: "lnbc1" as Bolt11, + send: amount, + }) + + expect(mockGetFeeEstimation).toHaveBeenCalledWith({ + bolt11: "lnbc1", + amount: "0.019446", + currencyId: "29", + }) + expect(result).not.toBeInstanceOf(Error) + expect((result as { fee: USDTAmount }).fee).toBeInstanceOf(USDTAmount) + expect((result as { fee: USDTAmount }).fee.asSmallestUnits()).toBe("123") + expect((result as { invoice: USDTAmount }).invoice.asSmallestUnits()).toBe("19446") + }) + + it("estimates fixed-amount LN fees with USDT currency id when no send amount is provided", async () => { + mockGetFeeEstimation.mockResolvedValue({ amount: 0.000123, invoiceAmount: 0.019446 }) + + const result = await Ibex.getLnFeeEstimation({ + invoice: "lnbc1" as Bolt11, + currency: WalletCurrency.Usdt, + }) + + expect(mockGetFeeEstimation).toHaveBeenCalledWith({ + bolt11: "lnbc1", + amount: undefined, + currencyId: "29", + }) + expect(result).not.toBeInstanceOf(Error) + expect((result as { fee: USDTAmount }).fee).toBeInstanceOf(USDTAmount) + expect((result as { fee: USDTAmount }).fee.asSmallestUnits()).toBe("123") + }) + + it("keeps USD LN fee estimation behavior unchanged", async () => { + const amount = USDAmount.cents("19446") as USDAmount + mockGetFeeEstimation.mockResolvedValue({ amount: 0.12, invoiceAmount: 194.46 }) + + const result = await Ibex.getLnFeeEstimation({ + invoice: "lnbc1" as Bolt11, + send: amount, + }) + + expect(mockGetFeeEstimation).toHaveBeenCalledWith({ + bolt11: "lnbc1", + amount: "194.46", + currencyId: "3", + }) + expect(result).not.toBeInstanceOf(Error) + expect((result as { fee: USDAmount }).fee).toBeInstanceOf(USDAmount) + expect((result as { fee: USDAmount }).fee.asCents()).toBe("12") + }) + + it("estimates on-chain fees with USDT currency id", async () => { + const amount = USDTAmount.smallestUnits("19446") as USDTAmount + mockEstimateFeeV2.mockResolvedValue({ fee: 0.000123 }) + + await Ibex.estimateOnchainFee(amount, "0xabc" as OnChainAddress) + + expect(mockEstimateFeeV2).toHaveBeenCalledWith({ + "amount": 0.019446, + "currency-id": "29", + "address": "0xabc", + }) + }) +}) From b48388cef5b3ad20762de6dc583caf981da3aeb9 Mon Sep 17 00:00:00 2001 From: Forge0x Date: Tue, 19 May 2026 12:16:55 -0400 Subject: [PATCH 12/22] fix(graphql): serialize USDT fee probe amounts (#358) --- src/graphql/shared/types/scalar/usd-cents.ts | 5 ++++- .../shared/types/scalar/usd-cents.spec.ts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts diff --git a/src/graphql/shared/types/scalar/usd-cents.ts b/src/graphql/shared/types/scalar/usd-cents.ts index c285f546b..d9e505d0e 100644 --- a/src/graphql/shared/types/scalar/usd-cents.ts +++ b/src/graphql/shared/types/scalar/usd-cents.ts @@ -1,4 +1,4 @@ -import { USDAmount } from "@domain/shared" +import { USDAmount, USDTAmount } from "@domain/shared" import { GT } from "@graphql/index" const USDCentsScalar = GT.Scalar({ @@ -16,6 +16,9 @@ const USDCentsScalar = GT.Scalar({ if (value instanceof USDAmount) { return Number(value.asCents(2)) } + if (value instanceof USDTAmount) { + return Number(value.asSmallestUnits()) + } else throw new Error(`Failed to serialize USDAmount: ${value}`) } }) diff --git a/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts new file mode 100644 index 000000000..497ab3903 --- /dev/null +++ b/test/flash/unit/graphql/shared/types/scalar/usd-cents.spec.ts @@ -0,0 +1,18 @@ +import { USDAmount, USDTAmount } from "@domain/shared" +import USDCentsScalar from "@graphql/shared/types/scalar/usd-cents" + +describe("USDCentsScalar", () => { + it("serializes USD amounts as cents", () => { + const amount = USDAmount.cents("123") + if (amount instanceof Error) throw amount + + expect(USDCentsScalar.serialize(amount)).toBe(123) + }) + + it("serializes USDT amounts as micro-USDT units", () => { + const amount = USDTAmount.smallestUnits("1234567") + if (amount instanceof Error) throw amount + + expect(USDCentsScalar.serialize(amount)).toBe(1234567) + }) +}) From 226c1e719be158cff378cd4a7fb987e764fe35bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olaniran=20=E2=9A=A1?= <93789719+heyolaniran@users.noreply.github.com> Date: Fri, 22 May 2026 00:45:29 +0100 Subject: [PATCH 13/22] Feat/bridge reconciliation (#360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add timeout for Bridge request * feat(bridge): ENG-350 — reset withdrawal state on transfer.failed and surface failure reason * feat(bridge): ENG-350 harden transfer webhook edge cases and notify users * feat(bridge): ENG-350 introduce full KYC status lifecycle with offboarded support * refactor(bridge): rename BridgeDepositLog model to BridgeDeposits * refactor(ibex): rename IbexCryptoReceiveLog model to IbexCryptoReceive * refactor(bridge): rename BridgeReplayLog model to BridgeReplay * fix(bridge) : ENG-289 timeout increase to 15s * feat: adding full kyc status to reflect them stored as bridgeKycStatus in mongoDB instead of just pending state * feat: Kyc status * fix: Duplicated webhook event message * fix: Duplicated webhook deposit event message * fix: Virtual Account Edge case --- dev/config/base-config.yaml | 3 +- .../bridge/send-withdrawal-notification.ts | 112 ++++++++++ src/config/locales/en.json | 11 + src/config/locales/es.json | 11 + src/config/schema.ts | 2 + src/config/schema.types.d.ts | 1 + src/domain/accounts/index.types.d.ts | 20 +- src/graphql/error-map.ts | 8 + src/graphql/public/schema.graphql | 1 + .../public/types/object/bridge-withdrawal.ts | 1 + src/services/bridge/client.ts | 87 +++++--- src/services/bridge/errors.ts | 12 + src/services/bridge/index.ts | 210 ++++++++++++++++-- src/services/bridge/index.types.d.ts | 50 ++++- src/services/bridge/reconciliation.ts | 12 +- .../bridge/webhook-server/routes/deposit.ts | 25 ++- .../bridge/webhook-server/routes/kyc.ts | 84 ++++++- .../bridge/webhook-server/routes/replay.ts | 37 ++- .../bridge/webhook-server/routes/transfer.ts | 124 +++++++++-- .../webhook-server/transfer-direction.ts | 29 +++ .../webhook-server/routes/crypto-receive.ts | 4 +- src/services/mongoose/accounts.ts | 4 +- src/services/mongoose/bridge-accounts.ts | 33 ++- src/services/mongoose/bridge-deposit-log.ts | 6 +- src/services/mongoose/bridge-replay-log.ts | 10 +- .../mongoose/ibex-crypto-receive-log.ts | 10 +- src/services/mongoose/schema.ts | 34 +-- src/services/mongoose/schema.types.d.ts | 2 +- .../send-withdrawal-notification.spec.ts | 122 ++++++++++ .../bridge/webhook-server/replay.spec.ts | 32 +++ .../bridge/webhook-server/transfer.spec.ts | 189 ++++++++++++++++ 31 files changed, 1136 insertions(+), 150 deletions(-) create mode 100644 src/app/bridge/send-withdrawal-notification.ts create mode 100644 src/services/bridge/webhook-server/transfer-direction.ts create mode 100644 test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts create mode 100644 test/flash/unit/services/bridge/webhook-server/transfer.spec.ts diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index 3cbc37bee..88fb2ada0 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -18,7 +18,8 @@ bridge: enabled: true apiKey: "sk-test-3bd6463c9cd77c3d8858c60b9997d0c6" baseUrl: "https://api.sandbox.bridge.xyz/v0" - minWithdrawalAmount: 10 + minWithdrawalAmount: 2 + timeoutMs: 15000 webhook: port: 4009 replaySecret: "also-not-so-secret" diff --git a/src/app/bridge/send-withdrawal-notification.ts b/src/app/bridge/send-withdrawal-notification.ts new file mode 100644 index 000000000..8adf90f30 --- /dev/null +++ b/src/app/bridge/send-withdrawal-notification.ts @@ -0,0 +1,112 @@ +import { getI18nInstance } from "@config" +import { checkedToAccountId } from "@domain/accounts" +import { getLanguageOrDefault } from "@domain/locale" +import { + DeviceTokensNotRegisteredNotificationsServiceError, + FlashNotificationCategories, + NotificationsServiceError, +} from "@domain/notifications" +import { removeDeviceTokens } from "@app/users/remove-device-tokens" +import { baseLogger } from "@services/logger" +import { AccountsRepository } from "@services/mongoose/accounts" +import { UsersRepository } from "@services/mongoose/users" +import { + PushNotificationsService, + SendFilteredPushNotificationStatus, +} from "@services/notifications/push-notifications" + +const i18n = getI18nInstance() + +const formatWithdrawalAmount = (amount: string, currency: string): string => + `${amount} ${currency.toUpperCase()}` + +export type BridgeWithdrawalNotificationOutcome = "completed" | "failed" + +export const sendBridgeWithdrawalNotification = async ({ + accountId: accountIdRaw, + amount, + currency, + outcome, + failureReason, +}: { + accountId: string + amount: string + currency: string + outcome: BridgeWithdrawalNotificationOutcome + failureReason?: string +}): Promise => { + const accountId = checkedToAccountId(accountIdRaw) + if (accountId instanceof Error) return accountId + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const user = await UsersRepository().findById(account.kratosUserId) + if (user instanceof Error) return user + + const locale = getLanguageOrDefault(user.language) + const formattedAmount = formatWithdrawalAmount(amount, currency) + const phraseBase = `notification.bridgeWithdrawal.${outcome}` + + const title = i18n.__({ phrase: `${phraseBase}.title`, locale }) + const bodyPhrase = + outcome === "failed" && failureReason + ? `${phraseBase}.bodyWithReason` + : `${phraseBase}.body` + const body = i18n.__( + { phrase: bodyPhrase, locale }, + { + amount: formattedAmount, + reason: failureReason ?? "", + }, + ) + + const result = await PushNotificationsService().sendFilteredNotification({ + deviceTokens: user.deviceTokens, + title, + body, + notificationCategory: FlashNotificationCategories.Cashout, + notificationSettings: account.notificationSettings, + data: { + type: `bridge_withdrawal_${outcome}`, + amount, + currency, + ...(failureReason ? { failureReason } : {}), + }, + }) + + if (result instanceof NotificationsServiceError) return result + + if (result.status === SendFilteredPushNotificationStatus.Filtered) { + return true + } + + return true +} + +export const sendBridgeWithdrawalNotificationBestEffort = async ( + args: Parameters[0], +): Promise => { + const result = await sendBridgeWithdrawalNotification(args) + + if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + const accountId = checkedToAccountId(args.accountId) + if (accountId instanceof Error) return + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return + + await removeDeviceTokens({ + userId: account.kratosUserId, + deviceTokens: result.tokens, + }) + return + } + + if (result instanceof Error) { + baseLogger.warn( + { accountId: args.accountId, outcome: args.outcome, error: result }, + "Failed to send Bridge withdrawal push notification", + ) + } +} diff --git a/src/config/locales/en.json b/src/config/locales/en.json index c9bd32b95..55eafc82d 100644 --- a/src/config/locales/en.json +++ b/src/config/locales/en.json @@ -40,6 +40,17 @@ "cashout": { "body": "Your cashout of {{amount}} has been deposited to your bank account.", "title": "Cashout" + }, + "bridgeWithdrawal": { + "completed": { + "body": "Your withdrawal of {{amount}} has been sent to your bank account.", + "title": "Withdrawal complete" + }, + "failed": { + "body": "Your withdrawal of {{amount}} could not be completed.", + "bodyWithReason": "Your withdrawal of {{amount}} could not be completed: {{reason}}.", + "title": "Withdrawal failed" + } } } } \ No newline at end of file diff --git a/src/config/locales/es.json b/src/config/locales/es.json index 122b16b47..7325c9fe0 100644 --- a/src/config/locales/es.json +++ b/src/config/locales/es.json @@ -36,6 +36,17 @@ "bodyDisplayCurrency": "+{{baseCurrencyAmount}}{{baseCurrencyName}} ({{displayCurrencyAmount}})", "title": "Transacción {{walletCurrency}}" } + }, + "bridgeWithdrawal": { + "completed": { + "body": "Su retiro de {{amount}} se envió a su cuenta bancaria.", + "title": "Retiro completado" + }, + "failed": { + "body": "No se pudo completar su retiro de {{amount}}.", + "bodyWithReason": "No se pudo completar su retiro de {{amount}}: {{reason}}.", + "title": "Retiro fallido" + } } } } diff --git a/src/config/schema.ts b/src/config/schema.ts index 0d673fd2b..c4e6077c2 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -649,6 +649,8 @@ export const configSchema = { enabled: { type: "boolean" }, apiKey: { type: "string" }, baseUrl: { type: "string" }, + minWithdrawalAmount: { type: "number" }, + timeoutMs: { type: "integer", default: 10000 }, webhook: { type: "object", properties: { diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index 4f480b86f..20c1952a4 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -49,6 +49,7 @@ type BridgeConfig = { apiKey: string baseUrl: string minWithdrawalAmount: number + timeoutMs?: number webhook: BridgeWebhook } diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index 3fed86428..1b39d3c32 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -88,7 +88,7 @@ type Account = { erpParty?: string // Lookup key to Customer in ERPNext. Required for Account level > 1 // Bridge integration: bridgeCustomerId?: BridgeCustomerId - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string } @@ -135,10 +135,10 @@ type LimitsCheckerFn = (args: LimiterCheckInputs) => Promise[]) => Promise< | { - volumeTotalLimit: UsdPaymentAmount - volumeUsed: UsdPaymentAmount - volumeRemaining: UsdPaymentAmount - } + volumeTotalLimit: UsdPaymentAmount + volumeUsed: UsdPaymentAmount + volumeRemaining: UsdPaymentAmount + } | ValidationError > @@ -150,10 +150,10 @@ type AccountLimitsChecker = { type AccountLimitsVolumes = | { - volumesIntraledger: LimitsVolumesFn - volumesWithdrawal: LimitsVolumesFn - volumesTradeIntraAccount: LimitsVolumesFn - } + volumesIntraledger: LimitsVolumesFn + volumesWithdrawal: LimitsVolumesFn + volumesTradeIntraAccount: LimitsVolumesFn + } | ValidationError type AccountValidator = { @@ -179,7 +179,7 @@ interface IAccountsRepository { id: AccountId, fields: { bridgeCustomerId?: BridgeCustomerId - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string }, ): Promise diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index c53a4b37f..9f93e8875 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -501,6 +501,10 @@ export const mapError = (error: ApplicationError): CustomApolloError => { message = "KYC verification was rejected" return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeKycOffboardedError": + message = "Your account has been offboarded from Bridge. Please contact support." + return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeCustomerNotFoundError": message = "Bridge customer not found" return new ValidationInternalError({ message, logger: baseLogger }) @@ -517,6 +521,10 @@ export const mapError = (error: ApplicationError): CustomApolloError => { message = "Request timed out" return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeTransferFailedError": + message = error.message || "Transfer failed" + return new ValidationInternalError({ message, logger: baseLogger }) + case "BridgeWebhookValidationError": message = "Invalid webhook signature" return new ValidationInternalError({ message, logger: baseLogger }) diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 0ed531044..e2f607717 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -300,6 +300,7 @@ type BridgeWithdrawal { amount: String! createdAt: String! currency: String! + failureReason: String id: ID! status: String! } diff --git a/src/graphql/public/types/object/bridge-withdrawal.ts b/src/graphql/public/types/object/bridge-withdrawal.ts index f29ba250d..0c6298215 100644 --- a/src/graphql/public/types/object/bridge-withdrawal.ts +++ b/src/graphql/public/types/object/bridge-withdrawal.ts @@ -7,6 +7,7 @@ const BridgeWithdrawal = GT.Object({ amount: { type: GT.NonNull(GT.String) }, currency: { type: GT.NonNull(GT.String) }, status: { type: GT.NonNull(GT.String) }, + failureReason: { type: GT.String }, createdAt: { type: GT.NonNull(GT.String) }, }), }) diff --git a/src/services/bridge/client.ts b/src/services/bridge/client.ts index e5503fccf..d6d07db65 100644 --- a/src/services/bridge/client.ts +++ b/src/services/bridge/client.ts @@ -7,7 +7,8 @@ import crypto from "crypto" import { BridgeConfig } from "@config" -import { BridgeCustomerId, BridgeTransferId } from "@domain/primitives/bridge" +import { BridgeCustomerId, BridgeTransferId, BridgeVirtualAccountId } from "@domain/primitives/bridge" +import { BridgeTimeoutError } from "./errors" // ============ Error Handling ============ @@ -66,15 +67,15 @@ export interface Customer { id: string type: "individual" | "business" status?: - | "active" - | "awaiting_questionnaire" - | "rejected" - | "paused" - | "under_review" - | "offboarded" - | "awaiting_ubo" - | "incomplete" - | "not_started" + | "active" + | "awaiting_questionnaire" + | "rejected" + | "paused" + | "under_review" + | "offboarded" + | "awaiting_ubo" + | "incomplete" + | "not_started" has_accepted_terms_of_service?: string created_at: string updated_at: string @@ -365,23 +366,37 @@ export class BridgeClient { } } - const response = await fetch(url, { - method, - headers, - body: body ? JSON.stringify(body) : undefined, - }) - - const responseData = await response.json().catch(() => null) + const timeoutMs = BridgeConfig.timeoutMs ?? 15_000 + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), timeoutMs) + + try { + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + signal: controller.signal, + }) + + const responseData = await response.json().catch(() => null) + + if (!response.ok) { + throw new BridgeApiError( + `Bridge API error: ${response.status} ${response.statusText}`, + response.status, + responseData, + ) + } - if (!response.ok) { - throw new BridgeApiError( - `Bridge API error: ${response.status} ${response.statusText}`, - response.status, - responseData, - ) + return responseData as T + } catch (err) { + if (err instanceof Error && err.name === "AbortError") { + throw new BridgeTimeoutError() + } + throw err + } finally { + clearTimeout(timeoutId) } - - return responseData as T } // ============ Customers ============ @@ -425,6 +440,28 @@ export class BridgeClient { ) } + async getVirtualAccount( + customerId: BridgeCustomerId, virtualAccountId: BridgeVirtualAccountId, idempotencyKey?: string, + ): Promise { + return this.request( + "GET", + `/customers/${customerId}/virtual_accounts/${virtualAccountId}`, + undefined, + idempotencyKey, + ) + } + + + async getVirtualAccountByCustomerId(customerId: BridgeCustomerId): Promise { + const response = await this.request<{ data: VirtualAccount[] }>( + "GET", + `/customers/${customerId}/virtual_accounts`, + ) + + return response.data as VirtualAccount[] + + + } // ============ External Accounts ============ async createExternalAccount( diff --git a/src/services/bridge/errors.ts b/src/services/bridge/errors.ts index 6f6572b0c..3cef54938 100644 --- a/src/services/bridge/errors.ts +++ b/src/services/bridge/errors.ts @@ -45,6 +45,12 @@ export class BridgeKycRejectedError extends BridgeError { } } +export class BridgeKycOffboardedError extends BridgeError { + constructor(message: string = "Your account has been offboarded from Bridge. Please contact support.") { + super(message) + } +} + export class BridgeInsufficientFundsError extends BridgeError { constructor(message: string = "Insufficient funds for withdrawal") { super(message) @@ -77,6 +83,12 @@ export class BridgeDisabledError extends BridgeError { } } +export class BridgeTransferFailedError extends BridgeError { + constructor(reason: string = "Transfer failed") { + super(reason) + } +} + export class BridgeWebhookValidationError extends BridgeError { constructor(message: string = "Invalid webhook signature") { super(message) diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index f5f51e2da..ba0fab74e 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -14,7 +14,7 @@ import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" import { baseLogger } from "@services/logger" import { RepositoryError } from "@domain/errors" -import { toBridgeCustomerId } from "@domain/primitives/bridge" +import { toBridgeCustomerId, toBridgeVirtualAccountId } from "@domain/primitives/bridge" import { getBalanceForWallet } from "@app/wallets/get-balance-for-wallet" import { USDTAmount, WalletCurrency } from "@domain/shared" import { WalletType } from "@domain/wallets" @@ -30,9 +30,11 @@ import { BridgeAccountLevelError, BridgeKycPendingError, BridgeKycRejectedError, + BridgeKycOffboardedError, BridgeCustomerNotFoundError, } from "./errors" -import BridgeApiClient from "./client" +import BridgeApiClient, { BridgeClient } from "./client" +import { BridgeVirtualAccount } from "@services/mongoose/schema" // ============ Types ============ @@ -70,7 +72,7 @@ type WithdrawalResult = { createdAt: string } -type KycStatusResult = "pending" | "approved" | "rejected" | null +type KycStatusResult = "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" | null type VirtualAccountResult = { bridgeVirtualAccountId: string @@ -199,7 +201,7 @@ const initiateKyc = async ({ const updateResult = await AccountsRepository().updateBridgeFields(accountId, { bridgeCustomerId: customerId, - bridgeKycStatus: "pending", + bridgeKycStatus: "open", }) if (updateResult instanceof Error) { @@ -216,6 +218,14 @@ const initiateKyc = async ({ const bridgeError = error as { statusCode?: number; response?: { existing_kyc_link?: { kyc_link: string; customer_id: string; tos_link: string } } } if (bridgeError?.statusCode === 400 && bridgeError.response?.existing_kyc_link) { + + // store the customer id and the kyc status + const customerId = toBridgeCustomerId(bridgeError.response.existing_kyc_link.customer_id) + const updateResult = await AccountsRepository().updateBridgeFields(accountId, { + bridgeCustomerId: customerId, + bridgeKycStatus: "not_started", + }) + return { kycLink: bridgeError.response.existing_kyc_link.kyc_link, customerId: bridgeError.response.existing_kyc_link.customer_id, @@ -252,22 +262,53 @@ const createVirtualAccount = async ( const account = await checkAccountLevel(accountId) if (account instanceof Error) return account + + const PENDING_BRIDGE_STATUSES = new Set([ + "incomplete", + "awaiting_questionnaire", + "awaiting_ubo", + "under_review", + "paused", + ]) + try { - const customerId = account.bridgeCustomerId - if (!customerId) { + + if (!account.bridgeCustomerId) { return new BridgeCustomerNotFoundError( "Account has no Bridge customer ID. Complete KYC first.", ) } + const customerId = toBridgeCustomerId(account.bridgeCustomerId) + if (!customerId && !account.bridgeKycStatus) { + return new BridgeCustomerNotFoundError( + "Account has no Bridge customer ID. Complete KYC first.", + ) + } + + const customer = await BridgeApiClient.getCustomer(customerId); + + if (customer instanceof Error) { + baseLogger.error( + { accountId, error: customer }, + "Failed to retrieve Bridge customer status" + ) + return customer + } + + let kycStatus = customer.status + // Check KYC status - if (account.bridgeKycStatus === "pending") { - return new BridgeKycPendingError() + if (kycStatus === "offboarded") { + return new BridgeKycOffboardedError() } - if (account.bridgeKycStatus === "rejected") { + if (kycStatus === "rejected") { return new BridgeKycRejectedError() } - if (account.bridgeKycStatus !== "approved") { + if (PENDING_BRIDGE_STATUSES.has(kycStatus!) || kycStatus as string === "open") { + return new BridgeKycPendingError() + } + if (kycStatus !== "active" && kycStatus as string !== "approved") { return new BridgeKycPendingError("KYC not yet completed") } @@ -310,12 +351,7 @@ const createVirtualAccount = async ( ethereumAddress = receiveInfo.data.address } - // Deterministic key so Bridge deduplicates on their side if two calls race past - // the check above before either has written to the repo. - const vaIdempotencyKey = crypto - .createHash("sha256") - .update(`va:${accountId}`) - .digest("hex") + const vaIdempotencyKey = `${accountId}:${crypto.randomUUID()}` // Create Bridge virtual account const virtualAccount = await BridgeApiClient.createVirtualAccount( @@ -593,14 +629,123 @@ const getKycStatus = async (accountId: AccountId): Promise va.destination.address === account.bridgeEthereumAddress) + + if (relatedVa?.status === "activated") { + + // if there's a related VA on Bridge side but it's not in our repo, create it in our repo to keep them in sync + if (existingVa instanceof RepositoryError) { + const repoResult = await BridgeAccountsRepo.createVirtualAccount({ + accountId: accountId as string, + bridgeVirtualAccountId: relatedVa.id, + bankName: relatedVa.source_deposit_instructions.bank_name || "", + routingNumber: relatedVa.source_deposit_instructions.bank_routing_number || "", + accountNumber: relatedVa.source_deposit_instructions.bank_account_number || "", + accountNumberLast4: relatedVa.source_deposit_instructions.bank_account_number?.slice(-4) || "", + }) + if (repoResult instanceof Error) { + baseLogger.error( + { accountId, operation: "getKycStatus", error: repoResult }, + "Failed to create virtual account in repo after KYC approval", + ) + } else { + baseLogger.info( + { accountId, operation: "getKycStatus", virtualAccountId: relatedVa.id }, + "Proactively updated virtual account in repo after KYC approval", + ) + } + } + } else { + + const vaResult = await createVirtualAccount(accountId) + if (vaResult instanceof Error) { + baseLogger.error( + { accountId, operation: "getKycStatus", error: vaResult }, + "Failed to create virtual account after KYC approval", + ) + } else { + baseLogger.info( + { accountId, operation: "getKycStatus", virtualAccountId: vaResult.virtualAccountId }, + "Proactively created virtual account after KYC approval", + ) + } + } + } + + baseLogger.info( + { accountId, operation: "getKycStatus", kycStatus: kycStatus }, + "Bridge operation completed", + ) + + return kycStatus + } catch (error) { + baseLogger.error( + { accountId, operation: "getKycStatus", error }, + "Bridge operation failed", + ) + return error instanceof Error ? error : new Error(String(error)) + } + + } /** @@ -634,6 +779,27 @@ const getVirtualAccount = async ( return null } + // check if the virtual account still exists on Bridge side - if not, delete it from our repo and return null + const bridgeVa = await BridgeApiClient.getVirtualAccount(account.bridgeCustomerId!, toBridgeVirtualAccountId(virtualAccount.bridgeVirtualAccountId!)) + + if (bridgeVa instanceof Error) { + return new BridgeError(`Failed to retrieve virtual account from Bridge: ${bridgeVa.message}`) + } + + // check if the virtual account is still activated on Bridge side - if not, delete it from our repo and return null + if (bridgeVa.status !== "activated") { + + // delete the virtual account from our repo since it's no longer valid + + const deleteResult = await BridgeVirtualAccount.deleteOne({ + bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId! as string + }) + + + return null + } + + const result: VirtualAccountResult = { bridgeVirtualAccountId: virtualAccount.bridgeVirtualAccountId!, bankName: virtualAccount.bankName, diff --git a/src/services/bridge/index.types.d.ts b/src/services/bridge/index.types.d.ts index 430e4b601..728ee499e 100644 --- a/src/services/bridge/index.types.d.ts +++ b/src/services/bridge/index.types.d.ts @@ -48,7 +48,20 @@ interface BridgeTransfer { readonly destinationAccountId: BridgeVirtualAccountId | BridgeExternalAccountId readonly amount: number readonly currency: string - readonly status: "pending" | "processing" | "completed" | "failed" | "cancelled" + readonly status: + | "awaiting_funds" + | "in_review" + | "funds_received" + | "payment_submitted" + | "payment_processed" + | "undeliverable" + | "returned" + | "refund_in_flight" + | "refunded" + | "refund_failed" + | "missing_return_policy" + | "error" + | "canceled" readonly description?: string readonly createdAt: string readonly updatedAt: string @@ -95,21 +108,43 @@ interface BridgeDepositCompletedEvent extends BridgeWebhookEvent { interface BridgeTransferCompletedEvent extends BridgeWebhookEvent { readonly type: "transfer.completed" readonly data: { - readonly transferId: BridgeTransferId + readonly transfer_id: BridgeTransferId readonly customerId: BridgeCustomerId - readonly amount: number + readonly state: "payment_processed" + readonly amount: string + readonly currency: string + } +} + +interface BridgeTransferPaymentProcessedEvent extends BridgeWebhookEvent { + readonly type: "transfer.payment_processed" + readonly data: { + readonly transfer_id: BridgeTransferId + readonly customerId: BridgeCustomerId + readonly state: "payment_processed" + readonly amount: string readonly currency: string - readonly completedAt: string } } interface BridgeTransferFailedEvent extends BridgeWebhookEvent { readonly type: "transfer.failed" readonly data: { - readonly transferId: BridgeTransferId + readonly transfer_id: BridgeTransferId readonly customerId: BridgeCustomerId - readonly reason: string - readonly failedAt: string + readonly state: + | "undeliverable" + | "returned" + | "refunded" + | "refund_in_flight" + | "refund_failed" + | "missing_return_policy" + | "error" + | "canceled" + readonly reason?: string + readonly return_reason?: string + readonly amount: string + readonly currency: string } } @@ -118,4 +153,5 @@ type BridgeWebhookEventType = | BridgeKycRejectedEvent | BridgeDepositCompletedEvent | BridgeTransferCompletedEvent + | BridgeTransferPaymentProcessedEvent | BridgeTransferFailedEvent diff --git a/src/services/bridge/reconciliation.ts b/src/services/bridge/reconciliation.ts index 891229e10..851ebce90 100644 --- a/src/services/bridge/reconciliation.ts +++ b/src/services/bridge/reconciliation.ts @@ -1,10 +1,10 @@ import { baseLogger } from "@services/logger" -import { findIbexCryptoReceiveLogsSince } from "@services/mongoose/ibex-crypto-receive-log" +import { findIbexCryptoReceivesSince } from "@services/mongoose/ibex-crypto-receive-log" import { upsertBridgeReconciliationOrphan, resolveOrphansByTxHash, } from "@services/mongoose/bridge-reconciliation-orphan" -import { BridgeDepositLog, IbexCryptoReceiveLog } from "@services/mongoose/schema" +import { BridgeDeposits, IbexCryptoReceive } from "@services/mongoose/schema" import { PubSubService } from "@services/pubsub" import { PubSubDefaultTriggers } from "@domain/pubsub" @@ -50,14 +50,14 @@ export const reconcileBridgeAndIbexDeposits = async ({ const now = new Date() const since = new Date(now.getTime() - windowMs) - const bridgeDeposits = (await BridgeDepositLog.find({ + const bridgeDeposits = (await BridgeDeposits.find({ createdAt: { $gte: since, $lte: now }, state: "payment_processed", }) .lean() .exec()) as BridgeDepositLike[] - const ibexReceivesResult = await findIbexCryptoReceiveLogsSince({ since, until: now }) + const ibexReceivesResult = await findIbexCryptoReceivesSince({ since, until: now }) if (ibexReceivesResult instanceof Error) return ibexReceivesResult const ibexReceives = ibexReceivesResult as IbexReceiveLike[] @@ -180,13 +180,13 @@ export const reconcileByTxHash = async ({ try { const [bridgeDeposit, ibexReceive] = await Promise.all([ - BridgeDepositLog.findOne({ + BridgeDeposits.findOne({ destinationTxHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, state: "payment_processed", }) .lean() .exec(), - IbexCryptoReceiveLog.findOne({ + IbexCryptoReceive.findOne({ txHash: { $regex: new RegExp(`^${normalizedHash}$`, "i") }, }) .lean() diff --git a/src/services/bridge/webhook-server/routes/deposit.ts b/src/services/bridge/webhook-server/routes/deposit.ts index a87352b1a..c5d02b4a1 100644 --- a/src/services/bridge/webhook-server/routes/deposit.ts +++ b/src/services/bridge/webhook-server/routes/deposit.ts @@ -9,25 +9,17 @@ import { Request, Response } from "express" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" -import { createBridgeDepositLog } from "@services/mongoose/bridge-deposit-log" +import { createBridgeDeposit } from "@services/mongoose/bridge-deposit-log" import { reconcileByTxHash } from "@services/bridge/reconciliation" export const depositHandler = async (req: Request, res: Response) => { const { event_id, event_object } = req.body const { id, state, amount, currency, on_behalf_of, receipt } = event_object ?? {} - if (!id || !event_id) { + if (!id || !event_id || !amount || !on_behalf_of) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency: lock on the transfer id + state so each state transition is processed once - const lockKey = `bridge-deposit:${id}:${state}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { - baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") - return res.status(200).json({ status: "already_processed" }) - } - try { baseLogger.info( { @@ -48,10 +40,10 @@ export const depositHandler = async (req: Request, res: Response) => { "Bridge deposit event", ) - const depositLog = await createBridgeDepositLog({ + const depositLog = await createBridgeDeposit({ eventId: event_id, transferId: id, - customerId: on_behalf_of ?? "", + customerId: on_behalf_of, state, amount: String(amount), currency, @@ -78,6 +70,15 @@ export const depositHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to persist deposit log" }) } + // Idempotency: lock on event_id after successful DB write — aligns with the DB unique + // constraint on eventId and ensures Bridge retries are not blocked on transient failures + const lockKey = `bridge-deposit:${event_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ event_id, id, state }, "Duplicate Bridge deposit webhook") + return res.status(200).json({ status: "already_processed" }) + } + if (state === "payment_processed" && receipt?.destination_tx_hash) { reconcileByTxHash({ txHash: receipt.destination_tx_hash }).catch((err) => baseLogger.error({ err, event_id, id }, "Real-time reconciliation failed"), diff --git a/src/services/bridge/webhook-server/routes/kyc.ts b/src/services/bridge/webhook-server/routes/kyc.ts index 2706b7499..965b38c1f 100644 --- a/src/services/bridge/webhook-server/routes/kyc.ts +++ b/src/services/bridge/webhook-server/routes/kyc.ts @@ -1,6 +1,14 @@ /** * Bridge KYC Webhook Handler - * Handles kyc.approved and kyc.rejected events from Bridge.xyz + * Handles all kyc.* events from Bridge.xyz + * + * Bridge kyc_status → internal bridgeKycStatus mapping: + * not_started → "open" + * incomplete | awaiting_questionnaire | awaiting_ubo + * | under_review | paused → "pending" + * approved → "approved" + * rejected → "rejected" + * offboarded → "offboarded" */ import { Request, Response } from "express" @@ -18,14 +26,6 @@ export const kycHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using customer_id + event as lock key - const lockKey = `bridge-kyc:${customer_id}:${event_object.kyc_status}:${event_object.id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { - baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") - return res.status(200).json({ status: "already_processed" }) - } - try { const bridgeCustomerId = toBridgeCustomerId(customer_id) const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) @@ -37,8 +37,55 @@ export const kycHandler = async (req: Request, res: Response) => { return res.status(503).json({ error: "Account not ready" }) } + // Idempotency check — acquire lock after account is found so 503 retries are not blocked + const lockKey = `bridge-kyc:${event_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ customer_id, event_id }, "Duplicate Bridge KYC webhook") + return res.status(200).json({ status: "already_processed" }) + } + + const PENDING_BRIDGE_STATUSES = new Set([ + "incomplete", + "awaiting_questionnaire", + "awaiting_ubo", + "under_review", + "paused", + ]) + // Update KYC status based on event - if (kyc_status === "approved") { + if (kyc_status === "not_started") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "not_started", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info({ accountId: account.id, customer_id }, "Bridge KYC not started") + } else if (PENDING_BRIDGE_STATUSES.has(kyc_status)) { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: kyc_status, + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.info( + { accountId: account.id, customer_id, kyc_status }, + "Bridge KYC moved to pending", + ) + } else if (kyc_status === "approved") { const result = await AccountsRepository().updateBridgeFields(account.id, { bridgeKycStatus: "approved", }) @@ -86,6 +133,23 @@ export const kycHandler = async (req: Request, res: Response) => { }, "Bridge KYC rejected", ) + } else if (kyc_status === "offboarded") { + const result = await AccountsRepository().updateBridgeFields(account.id, { + bridgeKycStatus: "offboarded", + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, error: result }, + "Failed to update KYC status", + ) + return res.status(500).json({ error: "Failed to update status" }) + } + + baseLogger.warn( + { accountId: account.id, customer_id }, + "Bridge KYC offboarded", + ) } return res.status(200).json({ status: "success" }) diff --git a/src/services/bridge/webhook-server/routes/replay.ts b/src/services/bridge/webhook-server/routes/replay.ts index 6316e2c8c..33b499bd9 100644 --- a/src/services/bridge/webhook-server/routes/replay.ts +++ b/src/services/bridge/webhook-server/routes/replay.ts @@ -6,7 +6,12 @@ import { BridgeConfig } from "@config" import { baseLogger } from "@services/logger" -import { createBridgeReplayLog } from "@services/mongoose/bridge-replay-log" +import { createBridgeReplay } from "@services/mongoose/bridge-replay-log" + +import { + isOutboundBridgeWithdrawal, + transferReplayEventTypeForStatus, +} from "../transfer-direction" import { depositHandler } from "./deposit" import { kycHandler } from "./kyc" @@ -41,18 +46,35 @@ const toRouteKey = (bridgeEventType: string): RouteKey | null => { const resolveReplayEventType = ({ eventType, eventObjectStatus, + eventObject, }: { eventType: string eventObjectStatus?: string + eventObject?: Record }): string => { const routeFromEventType = toRouteKey(eventType) if (routeFromEventType) return eventType if (eventObjectStatus && DEPOSIT_EVENT_TYPES.has(eventObjectStatus)) { + if (isOutboundBridgeWithdrawal(eventObject)) { + const transferEvent = transferReplayEventTypeForStatus(eventObjectStatus) + if (transferEvent) return transferEvent + } return eventObjectStatus } - if (eventObjectStatus === "approved" || eventObjectStatus === "rejected") { + const KYC_BRIDGE_STATUSES = new Set([ + "not_started", + "incomplete", + "awaiting_questionnaire", + "awaiting_ubo", + "under_review", + "approved", + "rejected", + "paused", + "offboarded", + ]) + if (eventObjectStatus && KYC_BRIDGE_STATUSES.has(eventObjectStatus)) { return `kyc.${eventObjectStatus}` } @@ -82,6 +104,8 @@ const toHandlerBody = ({ state: eventObject.state, amount: eventObject.amount, currency: eventObject.currency, + reason: eventObject.reason, + return_reason: eventObject.return_reason, }, } } @@ -138,10 +162,13 @@ export const replayHandler = async (req: Request, res: Response) => { return res.status(400).json({ error: "event_object must be an object" }) } + const eventObjectTyped = event_object as Record + const normalizedEventType = resolveReplayEventType({ eventType: event_type, eventObjectStatus: typeof event_object_status === "string" ? event_object_status : undefined, + eventObject: eventObjectTyped, }) const routeKey = toRouteKey(normalizedEventType) @@ -149,8 +176,6 @@ export const replayHandler = async (req: Request, res: Response) => { if (!routeKey) { return res.status(400).json({ error: "Unsupported event_type for replay" }) } - - const eventObjectTyped = event_object as Record const eventId: string = typeof event_id === "string" ? event_id @@ -173,7 +198,7 @@ export const replayHandler = async (req: Request, res: Response) => { } if (dry_run) { - const dryRunLog = await createBridgeReplayLog({ + const dryRunLog = await createBridgeReplay({ ...logBase, httpStatus: 0, httpResponse: { dry_run: true }, @@ -217,7 +242,7 @@ export const replayHandler = async (req: Request, res: Response) => { await HANDLERS[routeKey](fakeReq, fakeRes) - const logResult = await createBridgeReplayLog({ + const logResult = await createBridgeReplay({ ...logBase, httpStatus: handlerStatus, httpResponse: handlerBody, diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index 0863a074b..a24a32fdc 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -4,38 +4,88 @@ */ import { Request, Response } from "express" +import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" import { LockService } from "@services/lock" import { baseLogger } from "@services/logger" import { toBridgeTransferId } from "@domain/primitives/bridge" +const TERMINAL_FAILURE_STATES = new Set([ + "undeliverable", + "returned", + "refunded", + "refund_failed", + "missing_return_policy", + "error", + "canceled", +]) + +const TRANSIENT_STATES = new Set(["refund_in_flight"]) + +const transferLockKey = ( + transferId: string, + event: string, + state: string | undefined, +): IdempotencyKey => + `bridge-transfer:${transferId}:${event}:${state ?? ""}` as IdempotencyKey + +const markProcessed = async ( + transferId: string, + event: string, + state: string | undefined, +): Promise<"success" | "already_processed"> => { + const lockResult = await LockService().lockIdempotencyKey( + transferLockKey(transferId, event, state), + ) + if (lockResult instanceof Error) return "already_processed" + return "success" +} + export const transferHandler = async (req: Request, res: Response) => { const { event, data } = req.body - const { transfer_id, state, amount, currency } = data + const { transfer_id, state, amount, currency, reason, return_reason } = data if (!transfer_id || !event) { return res.status(400).json({ error: "Invalid payload" }) } - // Idempotency check using transfer_id as lock key - const lockKey = `bridge-transfer:${transfer_id}` - const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) - if (lockResult instanceof Error) { - baseLogger.info({ transfer_id }, "Duplicate Bridge transfer webhook") - return res.status(200).json({ status: "already_processed" }) - } - try { const bridgeTransferId = toBridgeTransferId(transfer_id) - // Update withdrawal status based on event - if (event === "transfer.completed") { + if (TRANSIENT_STATES.has(state)) { + baseLogger.info( + { transfer_id, state, event }, + "Bridge transfer in transient state — awaiting terminal event", + ) + return res.status(200).json({ status: "ignored_transient_state" }) + } + + const isCompletion = + event === "transfer.completed" || + event === "transfer.payment_processed" || + state === "payment_processed" + + const isFailure = event === "transfer.failed" || TERMINAL_FAILURE_STATES.has(state) + + if (!isCompletion && !isFailure) { + baseLogger.info({ transfer_id, state, event }, "Bridge transfer event not handled") + return res.status(200).json({ status: "ignored" }) + } + + if (isCompletion) { const result = await BridgeAccountsRepo.updateWithdrawalStatus( bridgeTransferId, "completed", ) if (result instanceof Error) { + if (result.message === BridgeAccountsRepo.BRIDGE_WITHDRAWAL_NOT_FOUND) { + baseLogger.warn( + { transfer_id }, + "Withdrawal not found for transfer webhook — Bridge may retry after bridgeTransferId is written", + ) + return res.status(503).json({ error: "Withdrawal not ready" }) + } baseLogger.error( { transfer_id, error: result }, "Failed to update withdrawal status", @@ -43,23 +93,56 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to update status" }) } + const lockStatus = await markProcessed(transfer_id, event, state) + if (lockStatus === "already_processed") { + return res.status(200).json({ status: "already_processed" }) + } + baseLogger.info( { transfer_id, + state, amount, currency, }, "Bridge transfer completed", ) - // TODO: Send push notification to user - } else if (event === "transfer.failed") { + await sendBridgeWithdrawalNotificationBestEffort({ + accountId: result.accountId, + amount: result.amount, + currency: result.currency, + outcome: "completed", + }) + } else if (isFailure) { + const failureReason = + state === "refund_failed" + ? (return_reason as string | undefined) + : event === "transfer.failed" + ? (reason as string | undefined) + : ((reason as string | undefined) ?? (return_reason as string | undefined)) + const result = await BridgeAccountsRepo.updateWithdrawalStatus( bridgeTransferId, "failed", + failureReason, ) if (result instanceof Error) { + if (result.message === BridgeAccountsRepo.BRIDGE_WITHDRAWAL_NOT_FOUND) { + baseLogger.warn( + { transfer_id }, + "Withdrawal not found for transfer webhook — Bridge may retry after bridgeTransferId is written", + ) + return res.status(503).json({ error: "Withdrawal not ready" }) + } + if (result.message.startsWith("Withdrawal already ")) { + baseLogger.info( + { transfer_id, state, error: result.message }, + "Ignoring Bridge transfer failure — withdrawal already terminal", + ) + return res.status(200).json({ status: "already_terminal" }) + } baseLogger.error( { transfer_id, error: result }, "Failed to update withdrawal status", @@ -67,17 +150,30 @@ export const transferHandler = async (req: Request, res: Response) => { return res.status(500).json({ error: "Failed to update status" }) } + const lockStatus = await markProcessed(transfer_id, event, state) + if (lockStatus === "already_processed") { + return res.status(200).json({ status: "already_processed" }) + } + baseLogger.warn( { transfer_id, state, amount, currency, + reason, + return_reason, }, "Bridge transfer failed", ) - // TODO: Send push notification to user + await sendBridgeWithdrawalNotificationBestEffort({ + accountId: result.accountId, + amount: result.amount, + currency: result.currency, + outcome: "failed", + failureReason: result.failureReason ?? failureReason, + }) } return res.status(200).json({ status: "success" }) diff --git a/src/services/bridge/webhook-server/transfer-direction.ts b/src/services/bridge/webhook-server/transfer-direction.ts new file mode 100644 index 000000000..6e785d0ea --- /dev/null +++ b/src/services/bridge/webhook-server/transfer-direction.ts @@ -0,0 +1,29 @@ +/** + * Distinguishes Bridge outbound withdrawals (USDT → ACH) from inbound deposits + * when replaying status_transitioned events that share the same state names. + */ +export const isOutboundBridgeWithdrawal = ( + eventObject: Record | undefined, +): boolean => { + if (!eventObject) return false + const source = eventObject.source as Record | undefined + const destination = eventObject.destination as Record | undefined + return source?.payment_rail === "ethereum" && destination?.payment_rail === "ach" +} + +const OUTBOUND_WITHDRAWAL_REPLAY_STATUSES = new Set([ + "payment_processed", + "undeliverable", + "returned", + "refunded", + "refund_failed", + "missing_return_policy", + "error", + "canceled", +]) + +export const transferReplayEventTypeForStatus = (status: string): string | null => { + if (!OUTBOUND_WITHDRAWAL_REPLAY_STATUSES.has(status)) return null + if (status === "payment_processed") return "transfer.payment_processed" + return "transfer.failed" +} diff --git a/src/services/ibex/webhook-server/routes/crypto-receive.ts b/src/services/ibex/webhook-server/routes/crypto-receive.ts index 83282f9a5..d03e72676 100644 --- a/src/services/ibex/webhook-server/routes/crypto-receive.ts +++ b/src/services/ibex/webhook-server/routes/crypto-receive.ts @@ -1,6 +1,6 @@ import express, { Request, Response } from "express" import { AccountsRepository } from "@services/mongoose/accounts" -import { createIbexCryptoReceiveLog } from "@services/mongoose/ibex-crypto-receive-log" +import { createIbexCryptoReceive } from "@services/mongoose/ibex-crypto-receive-log" import { listWalletsByAccountId } from "@app/wallets" import { WalletCurrency, USDTAmount } from "@domain/shared" import { baseLogger } from "@services/logger" @@ -49,7 +49,7 @@ const cryptoReceiveHandler = async (req: Request, res: Response) => { return { status: "error", code: "account_not_found" } as CryptoReceiveResult } - const ibexLog = await createIbexCryptoReceiveLog({ + const ibexLog = await createIbexCryptoReceive({ txHash: String(tx_hash), address: String(address), amount: String(amount), diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index 853a7a93b..4cc8fb43f 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -176,7 +176,7 @@ export const AccountsRepository = (): IAccountsRepository => { id: AccountId, fields: { bridgeCustomerId?: BridgeCustomerId - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "not_started" | "incomplete" | "awaiting_questionnaire" | "awaiting_ubo" | "under_review" | "paused" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string }, ): Promise => { @@ -291,6 +291,6 @@ const translateToAccount = (result: AccountRecord): Account => ({ kratosUserId: result.kratosUserId as UserId, displayCurrency: (result.displayCurrency || UsdDisplayCurrency) as DisplayCurrency, bridgeCustomerId: result.bridgeCustomerId as BridgeCustomerId | undefined, - bridgeKycStatus: result.bridgeKycStatus, + bridgeKycStatus: (result.bridgeKycStatus === "pending" ? "open" : result.bridgeKycStatus) as Account["bridgeKycStatus"], bridgeEthereumAddress: result.bridgeEthereumAddress, }) diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index 234304c2c..c045295be 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -95,6 +95,18 @@ export const updateExternalAccountStatus = async ( // ============ Withdrawals ============ +export const BRIDGE_WITHDRAWAL_NOT_FOUND = "Withdrawal not found" + +export const BRIDGE_FAILURE_REASON_MAX_LENGTH = 512 + +export const truncateBridgeFailureReason = ( + reason: string | undefined, +): string | undefined => { + if (reason === undefined) return undefined + if (reason.length <= BRIDGE_FAILURE_REASON_MAX_LENGTH) return reason + return `${reason.slice(0, BRIDGE_FAILURE_REASON_MAX_LENGTH - 3)}...` +} + export const createWithdrawal = async (data: { accountId: string bridgeTransferId?: string @@ -171,14 +183,29 @@ export const findWithdrawalsByAccountId = async (accountId: string) => { export const updateWithdrawalStatus = async ( bridgeTransferId: BridgeTransferId, status: "pending" | "completed" | "failed", + failureReason?: string, ) => { try { + const update: Record = { status, updatedAt: new Date() } + const truncatedReason = truncateBridgeFailureReason(failureReason) + if (truncatedReason !== undefined) update.failureReason = truncatedReason + const record = await BridgeWithdrawal.findOneAndUpdate( - { bridgeTransferId }, - { status, updatedAt: new Date() }, + { bridgeTransferId, status: "pending" }, + update, { new: true }, ) - return record || new RepositoryError("Withdrawal not found") + if (record) return record + + const existing = await BridgeWithdrawal.findOne({ bridgeTransferId }) + if (!existing) return new RepositoryError(BRIDGE_WITHDRAWAL_NOT_FOUND) + + // Idempotent: duplicate webhook after we already reached this terminal status. + if (existing.status === status) return existing + + return new RepositoryError( + `Withdrawal already ${existing.status}, cannot transition to ${status}`, + ) } catch (error) { return new RepositoryError(String(error)) } diff --git a/src/services/mongoose/bridge-deposit-log.ts b/src/services/mongoose/bridge-deposit-log.ts index f71f37abd..953cd8a3d 100644 --- a/src/services/mongoose/bridge-deposit-log.ts +++ b/src/services/mongoose/bridge-deposit-log.ts @@ -1,6 +1,6 @@ -import { BridgeDepositLog } from "./schema" +import { BridgeDeposits } from "./schema" -export const createBridgeDepositLog = async (data: { +export const createBridgeDeposit = async (data: { eventId: string transferId: string customerId: string @@ -14,7 +14,7 @@ export const createBridgeDepositLog = async (data: { destinationTxHash?: string }): Promise<{ id: string } | Error> => { try { - const log = await BridgeDepositLog.create(data) + const log = await BridgeDeposits.create(data) return { id: log._id.toString() } } catch (error) { return error instanceof Error ? error : new Error(String(error)) diff --git a/src/services/mongoose/bridge-replay-log.ts b/src/services/mongoose/bridge-replay-log.ts index d1842be8c..361994991 100644 --- a/src/services/mongoose/bridge-replay-log.ts +++ b/src/services/mongoose/bridge-replay-log.ts @@ -1,6 +1,6 @@ -import { BridgeReplayLog } from "./schema" +import { BridgeReplay } from "./schema" -export const createBridgeReplayLog = async (data: { +export const createBridgeReplay = async (data: { eventId: string eventType: string eventPayload: Record @@ -14,7 +14,7 @@ export const createBridgeReplayLog = async (data: { dryRun?: boolean }): Promise<{ id: string } | Error> => { try { - const log = await BridgeReplayLog.create(data) + const log = await BridgeReplay.create(data) return { id: log._id.toString() } } catch (error) { @@ -22,7 +22,7 @@ export const createBridgeReplayLog = async (data: { } } -export const findBridgeReplayLogs = async (filter?: { +export const findBridgeReplays = async (filter?: { eventType?: string dryRun?: boolean limit?: number @@ -32,7 +32,7 @@ export const findBridgeReplayLogs = async (filter?: { if (filter?.eventType !== undefined) queryFilter.eventType = filter.eventType if (filter?.dryRun !== undefined) queryFilter.dryRun = filter.dryRun - return await BridgeReplayLog.find(queryFilter) + return await BridgeReplay.find(queryFilter) .sort({ replayedAt: -1 }) .limit(filter?.limit ?? 100) .lean() diff --git a/src/services/mongoose/ibex-crypto-receive-log.ts b/src/services/mongoose/ibex-crypto-receive-log.ts index e657c9e89..490330f09 100644 --- a/src/services/mongoose/ibex-crypto-receive-log.ts +++ b/src/services/mongoose/ibex-crypto-receive-log.ts @@ -1,6 +1,6 @@ -import { IbexCryptoReceiveLog } from "./schema" +import { IbexCryptoReceive } from "./schema" -export const createIbexCryptoReceiveLog = async (data: { +export const createIbexCryptoReceive = async (data: { txHash: string address: string amount: string @@ -9,7 +9,7 @@ export const createIbexCryptoReceiveLog = async (data: { accountId?: string }): Promise<{ id: string } | Error> => { try { - const log = await IbexCryptoReceiveLog.findOneAndUpdate( + const log = await IbexCryptoReceive.findOneAndUpdate( { txHash: data.txHash }, { ...data, receivedAt: new Date() }, { upsert: true, new: true, setDefaultsOnInsert: true }, @@ -21,7 +21,7 @@ export const createIbexCryptoReceiveLog = async (data: { } } -export const findIbexCryptoReceiveLogsSince = async ({ +export const findIbexCryptoReceivesSince = async ({ since, until = new Date(), }: { @@ -40,7 +40,7 @@ export const findIbexCryptoReceiveLogsSince = async ({ | Error > => { try { - return await IbexCryptoReceiveLog.find({ + return await IbexCryptoReceive.find({ receivedAt: { $gte: since, $lte: until }, }) .sort({ receivedAt: -1 }) diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index 15a1fd2f5..ef7b0ebf9 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -48,6 +48,7 @@ interface IBridgeWithdrawalRecord { amount: string currency: string status: "pending" | "completed" | "failed" + failureReason?: string externalAccountId: string createdAt: Date updatedAt: Date @@ -338,7 +339,7 @@ const AccountSchema = new Schema( }, bridgeKycStatus: { type: String, - enum: ["pending", "approved", "rejected"], + enum: ["open", "not_started", "incomplete", "awaiting_questionnaire", "awaiting_ubo", "under_review", "paused", "approved", "rejected", "offboarded"], required: false, }, bridgeEthereumAddress: { @@ -653,6 +654,7 @@ const BridgeWithdrawalSchema = new Schema({ amount: { type: String, required: true }, currency: { type: String, required: true }, status: { type: String, enum: ["pending", "completed", "failed"], default: "pending" }, + failureReason: { type: String, maxlength: 512 }, externalAccountId: { type: String, required: true }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, @@ -670,7 +672,7 @@ BridgeWithdrawalSchema.index( }, ) -const BridgeDepositLogSchema = new Schema({ +const BridgeDepositsSchema = new Schema({ eventId: { type: String, required: true, unique: true }, transferId: { type: String, required: true }, customerId: { type: String, required: true }, @@ -685,12 +687,12 @@ const BridgeDepositLogSchema = new Schema({ createdAt: { type: Date, default: Date.now }, }) -BridgeDepositLogSchema.index({ transferId: 1 }) -BridgeDepositLogSchema.index({ customerId: 1, createdAt: -1 }) +BridgeDepositsSchema.index({ transferId: 1 }) +BridgeDepositsSchema.index({ customerId: 1, createdAt: -1 }) -export const BridgeDepositLog = mongoose.model("BridgeDepositLog", BridgeDepositLogSchema) +export const BridgeDeposits = mongoose.model("BridgeDeposits", BridgeDepositsSchema) -const IbexCryptoReceiveLogSchema = new Schema({ +const IbexCryptoReceiveSchema = new Schema({ txHash: { type: String, required: true, unique: true }, address: { type: String, required: true }, amount: { type: String, required: true }, @@ -700,12 +702,12 @@ const IbexCryptoReceiveLogSchema = new Schema({ receivedAt: { type: Date, default: Date.now }, }) -IbexCryptoReceiveLogSchema.index({ receivedAt: -1 }) -IbexCryptoReceiveLogSchema.index({ address: 1, receivedAt: -1 }) +IbexCryptoReceiveSchema.index({ receivedAt: -1 }) +IbexCryptoReceiveSchema.index({ address: 1, receivedAt: -1 }) -export const IbexCryptoReceiveLog = mongoose.model( - "IbexCryptoReceiveLog", - IbexCryptoReceiveLogSchema, +export const IbexCryptoReceive = mongoose.model( + "IbexCryptoReceive", + IbexCryptoReceiveSchema, ) const BridgeReconciliationOrphanSchema = new Schema({ @@ -742,7 +744,7 @@ export const BridgeReconciliationOrphan = mongoose.model( BridgeReconciliationOrphanSchema, ) -const BridgeReplayLogSchema = new Schema({ +const BridgeReplaySchema = new Schema({ eventId: { type: String, required: true }, eventType: { type: String, required: true }, eventPayload: { type: Schema.Types.Mixed, required: true }, @@ -756,11 +758,11 @@ const BridgeReplayLogSchema = new Schema({ dryRun: { type: Boolean, required: true, default: false }, }) -BridgeReplayLogSchema.index({ eventId: 1 }) -BridgeReplayLogSchema.index({ replayedAt: -1 }) -BridgeReplayLogSchema.index({ eventType: 1, replayedAt: -1 }) +BridgeReplaySchema.index({ eventId: 1 }) +BridgeReplaySchema.index({ replayedAt: -1 }) +BridgeReplaySchema.index({ eventType: 1, replayedAt: -1 }) -export const BridgeReplayLog = mongoose.model("BridgeReplayLog", BridgeReplayLogSchema) +export const BridgeReplay = mongoose.model("BridgeReplay", BridgeReplaySchema) export const BridgeVirtualAccount = mongoose.model( "BridgeVirtualAccount", diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 51c86757c..39d5a3da9 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -96,7 +96,7 @@ interface AccountRecord { // Bridge integration: bridgeCustomerId?: string - bridgeKycStatus?: "pending" | "approved" | "rejected" + bridgeKycStatus?: "open" | "pending" | "approved" | "rejected" | "offboarded" bridgeEthereumAddress?: string // mongoose in-built functions diff --git a/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts b/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts new file mode 100644 index 000000000..d00d89dfa --- /dev/null +++ b/test/flash/unit/app/bridge/send-withdrawal-notification.spec.ts @@ -0,0 +1,122 @@ +import { sendBridgeWithdrawalNotification } from "@app/bridge/send-withdrawal-notification" +import { AccountsRepository } from "@services/mongoose/accounts" +import { UsersRepository } from "@services/mongoose/users" +import { + PushNotificationsService, + SendFilteredPushNotificationStatus, +} from "@services/notifications/push-notifications" +import { getI18nInstance } from "@config" + +jest.mock("@services/mongoose/accounts", () => ({ + AccountsRepository: jest.fn(), +})) + +jest.mock("@services/mongoose/users", () => ({ + UsersRepository: jest.fn(), +})) + +jest.mock("@services/notifications/push-notifications", () => ({ + PushNotificationsService: jest.fn(), + SendFilteredPushNotificationStatus: { + Filtered: "filtered", + Sent: "sent", + }, +})) + +jest.mock("@app/users/remove-device-tokens", () => ({ + removeDeviceTokens: jest.fn(), +})) + +jest.mock("@config", () => { + const mockI18n = { + __: jest.fn().mockImplementation(({ phrase }, options) => `${phrase} ${JSON.stringify(options)}`), + } + return { + getI18nInstance: jest.fn(() => mockI18n), + } +}) + +describe("sendBridgeWithdrawalNotification", () => { + const accountId = "507f1f77bcf86cd799439011" + const mockAccount = { + id: accountId, + kratosUserId: "user-id", + notificationSettings: { push: { enabled: true, disabledCategories: [] } }, + } + const mockUser = { + deviceTokens: ["token-1"], + language: "en", + } + + const sendFilteredNotification = jest.fn().mockResolvedValue({ + status: SendFilteredPushNotificationStatus.Sent, + }) + const mockI18n = getI18nInstance() + + beforeEach(() => { + jest.clearAllMocks() + ;(AccountsRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockAccount), + }) + ;(UsersRepository as jest.Mock).mockReturnValue({ + findById: jest.fn().mockResolvedValue(mockUser), + }) + ;(PushNotificationsService as jest.Mock).mockReturnValue({ + sendFilteredNotification, + }) + ;(getI18nInstance as jest.Mock).mockReturnValue(mockI18n) + }) + + it("sends a completed withdrawal notification with Cashout category", async () => { + const result = await sendBridgeWithdrawalNotification({ + accountId, + amount: "100.00", + currency: "usdt", + outcome: "completed", + }) + + expect(result).toBe(true) + expect(sendFilteredNotification).toHaveBeenCalledWith( + expect.objectContaining({ + deviceTokens: mockUser.deviceTokens, + notificationCategory: "Cashout", + data: expect.objectContaining({ type: "bridge_withdrawal_completed" }), + }), + ) + expect(mockI18n.__).toHaveBeenCalledWith( + expect.objectContaining({ phrase: "notification.bridgeWithdrawal.completed.title" }), + ) + }) + + it("uses bodyWithReason when a failure reason is provided", async () => { + await sendBridgeWithdrawalNotification({ + accountId, + amount: "50.00", + currency: "usdt", + outcome: "failed", + failureReason: "ACH return", + }) + + expect(mockI18n.__).toHaveBeenCalledWith( + expect.objectContaining({ + phrase: "notification.bridgeWithdrawal.failed.bodyWithReason", + }), + expect.objectContaining({ reason: "ACH return" }), + ) + }) + + it("returns true when notification is filtered by user settings", async () => { + sendFilteredNotification.mockResolvedValue({ + status: SendFilteredPushNotificationStatus.Filtered, + }) + + const result = await sendBridgeWithdrawalNotification({ + accountId, + amount: "10.00", + currency: "usdt", + outcome: "completed", + }) + + expect(result).toBe(true) + }) +}) diff --git a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts index ea6c9610b..9b9988b64 100644 --- a/test/flash/unit/services/bridge/webhook-server/replay.spec.ts +++ b/test/flash/unit/services/bridge/webhook-server/replay.spec.ts @@ -176,6 +176,38 @@ describe("replayHandler", () => { ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-001" }) }) + it("routes outbound withdrawal payment_processed replay to transfer handler", async () => { + ;(transferHandler as jest.Mock).mockImplementation((_req: Request, res: Response) => { + ;(res.status as jest.Mock)(200) + ;(res.json as jest.Mock)({ status: "success" }) + return Promise.resolve(res) + }) + ;(ReplayLog.createBridgeReplayLog as jest.Mock).mockResolvedValue({ id: "log-wd-001" }) + + const res = makeRes() + await replayHandler( + makeReq({ + ...BASE_BODY, + event_type: "updated.status_transitioned", + event_object_status: "payment_processed", + event_object: { + id: "tr-withdraw-001", + state: "payment_processed", + amount: "100.00", + currency: "usd", + source: { payment_rail: "ethereum", currency: "usdt" }, + destination: { payment_rail: "ach", currency: "usd" }, + }, + }), + res, + ) + + expect(transferHandler).toHaveBeenCalledTimes(1) + expect(depositHandler).not.toHaveBeenCalled() + const handlerReq = (transferHandler as jest.Mock).mock.calls[0][0] + expect(handlerReq.body.event).toBe("transfer.payment_processed") + }) + test.each(cases)( "%s is routed to the correct handler", async (eventType, handler) => { diff --git a/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts b/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts new file mode 100644 index 000000000..30bb87f31 --- /dev/null +++ b/test/flash/unit/services/bridge/webhook-server/transfer.spec.ts @@ -0,0 +1,189 @@ +jest.mock("@services/lock", () => ({ + LockService: jest.fn(), +})) + +jest.mock("@services/logger", () => ({ + baseLogger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() }, +})) + +jest.mock("@services/mongoose/bridge-accounts", () => ({ + BRIDGE_WITHDRAWAL_NOT_FOUND: "Withdrawal not found", + updateWithdrawalStatus: jest.fn(), +})) + +jest.mock("@app/bridge/send-withdrawal-notification", () => ({ + sendBridgeWithdrawalNotificationBestEffort: jest.fn().mockResolvedValue(undefined), +})) + +import { Request, Response } from "express" +import { LockService } from "@services/lock" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" +import { sendBridgeWithdrawalNotificationBestEffort } from "@app/bridge/send-withdrawal-notification" +import { transferHandler } from "@services/bridge/webhook-server/routes/transfer" + +const makeRes = () => { + const res = { status: jest.fn(), json: jest.fn() } as unknown as Response + ;(res.status as jest.Mock).mockReturnValue(res) + ;(res.json as jest.Mock).mockReturnValue(res) + return res +} + +const makeReq = (body: Record) => ({ body }) as unknown as Request + +const WITHDRAWAL_RECORD = { id: "wd-1", status: "pending", bridgeTransferId: "tr-abc" } + +const updateFn = BridgeAccountsRepo.updateWithdrawalStatus as jest.Mock +let lockFn: jest.Mock + +beforeEach(() => { + jest.clearAllMocks() + lockFn = jest.fn().mockResolvedValue({}) + updateFn.mockResolvedValue({ ...WITHDRAWAL_RECORD, status: "completed" }) + ;(LockService as jest.Mock).mockReturnValue({ lockIdempotencyKey: lockFn }) +}) + +describe("transferHandler", () => { + it("returns 503 when withdrawal row is not found yet (retryable)", async () => { + const { RepositoryError } = jest.requireActual("@domain/errors") + updateFn.mockResolvedValue(new RepositoryError("Withdrawal not found")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-early", state: "canceled", reason: "rejected" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(503) + expect(lockFn).not.toHaveBeenCalled() + }) + + it("acquires idempotency lock only after a successful status update", async () => { + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(updateFn).toHaveBeenCalled() + expect(lockFn).toHaveBeenCalledWith("bridge-transfer:tr-abc:transfer.completed:payment_processed") + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + }) + + it("does not acquire lock when status update fails", async () => { + const { RepositoryError } = jest.requireActual("@domain/errors") + updateFn.mockResolvedValue(new RepositoryError("mongo error")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-abc", state: "canceled" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(500) + expect(lockFn).not.toHaveBeenCalled() + }) + + it("ignores refund_in_flight without updating withdrawal status", async () => { + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-abc", state: "refund_in_flight" }, + }), + res, + ) + + expect(updateFn).not.toHaveBeenCalled() + expect(lockFn).not.toHaveBeenCalled() + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect((res.json as jest.Mock).mock.calls[0][0]).toEqual({ + status: "ignored_transient_state", + }) + }) + + it("returns already_processed when lock is held after a prior successful run", async () => { + lockFn.mockResolvedValue(new Error("already locked")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect((res.json as jest.Mock).mock.calls[0][0]).toEqual({ status: "already_processed" }) + }) + + it("sends a push notification after a successful completion", async () => { + updateFn.mockResolvedValue({ + ...WITHDRAWAL_RECORD, + status: "completed", + accountId: "acct-1", + amount: "25.00", + currency: "usdt", + }) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(sendBridgeWithdrawalNotificationBestEffort).toHaveBeenCalledWith({ + accountId: "acct-1", + amount: "25.00", + currency: "usdt", + outcome: "completed", + }) + }) + + it("does not send a push notification when the idempotency lock is already held", async () => { + lockFn.mockResolvedValue(new Error("already locked")) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.completed", + data: { transfer_id: "tr-abc", state: "payment_processed" }, + }), + res, + ) + + expect(sendBridgeWithdrawalNotificationBestEffort).not.toHaveBeenCalled() + }) + + it("returns 200 already_terminal when failure arrives after completion", async () => { + const { RepositoryError } = jest.requireActual("@domain/errors") + updateFn.mockResolvedValue( + new RepositoryError("Withdrawal already completed, cannot transition to failed"), + ) + + const res = makeRes() + await transferHandler( + makeReq({ + event: "transfer.failed", + data: { transfer_id: "tr-abc", state: "returned", reason: "ACH return" }, + }), + res, + ) + + expect(res.status as jest.Mock).toHaveBeenCalledWith(200) + expect((res.json as jest.Mock).mock.calls[0][0]).toEqual({ status: "already_terminal" }) + expect(lockFn).not.toHaveBeenCalled() + }) +}) From 2dc9197ef135cfba41d0c71a0bf4b62573a95646 Mon Sep 17 00:00:00 2001 From: Forge0x Date: Mon, 25 May 2026 11:10:25 -0400 Subject: [PATCH 14/22] fix(graphql): make usdt wallet balance nullable (#369) --- dev/apollo-federation/supergraph.graphql | 2 +- src/graphql/admin/schema.graphql | 2 +- src/graphql/public/schema.graphql | 2 +- .../shared/types/object/usdt-wallet.ts | 2 +- .../graphql/wallet-balance-validation.spec.ts | 30 +++++++++++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 test/flash/unit/graphql/wallet-balance-validation.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index bb5202f04..865c5fd87 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -2039,7 +2039,7 @@ type UsdtWallet implements Wallet @join__type(graph: PUBLIC) { accountId: ID! - balance: FractionalCentAmount! + balance: FractionalCentAmount id: ID! isExternal: Boolean! lnurlp: Lnurl diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index 1eb75c263..87eed84b2 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -572,7 +572,7 @@ A wallet belonging to an account which contains a USDT balance and a list of tra """ type UsdtWallet implements Wallet { accountId: ID! - balance: FractionalCentAmount! + balance: FractionalCentAmount id: ID! isExternal: Boolean! lnurlp: Lnurl diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index e2f607717..e0bac3237 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -1654,7 +1654,7 @@ A wallet belonging to an account which contains a USDT balance and a list of tra """ type UsdtWallet implements Wallet { accountId: ID! - balance: FractionalCentAmount! + balance: FractionalCentAmount id: ID! isExternal: Boolean! lnurlp: Lnurl diff --git a/src/graphql/shared/types/object/usdt-wallet.ts b/src/graphql/shared/types/object/usdt-wallet.ts index 046e21bb7..3ea7dba1f 100644 --- a/src/graphql/shared/types/object/usdt-wallet.ts +++ b/src/graphql/shared/types/object/usdt-wallet.ts @@ -56,7 +56,7 @@ const UsdtWallet = GT.Object({ }, balance: { - type: GT.NonNull(FractionalCentAmount), + type: FractionalCentAmount, resolve: async (source) => { const balance = await Wallets.getBalanceForWallet({ walletId: source.id, diff --git a/test/flash/unit/graphql/wallet-balance-validation.spec.ts b/test/flash/unit/graphql/wallet-balance-validation.spec.ts new file mode 100644 index 000000000..8a2c828c9 --- /dev/null +++ b/test/flash/unit/graphql/wallet-balance-validation.spec.ts @@ -0,0 +1,30 @@ +import { parse, validate } from "graphql" + +import { gqlMainSchema } from "@graphql/public" + +describe("wallet balance query validation", () => { + it("allows querying USD and USDT wallet balances with the same response name", () => { + const query = parse(` + query Me { + me { + defaultAccount { + wallets { + ... on UsdtWallet { + id + balance + } + ... on UsdWallet { + id + balance + } + } + } + } + } + `) + + const errors = validate(gqlMainSchema, query) + + expect(errors).toEqual([]) + }) +}) From b99fdf4b2096e7ed6d40113a7da2dcd16f845a27 Mon Sep 17 00:00:00 2001 From: Forge0x Date: Mon, 25 May 2026 16:46:46 -0400 Subject: [PATCH 15/22] feat(cutover): orchestrate cash wallet migration with client-aware presentation (#370) * feat(cutover): add cash wallet migration state primitives * feat(cutover): persist cash wallet cutover state * feat(cutover): add cash wallet write guard * feat(cutover): classify cash wallet migration candidates * feat(cutover): add cash wallet preflight summary * feat(cutover): collect cash wallet discovery results * feat(cutover): plan primary cash wallet migrations * feat(cutover): upsert planned migration records * feat(cutover): prepare primary migration batch * feat(cutover): start migration worker checkpoint * feat(cutover): record migration source balance * feat(cutover): create balance move invoice checkpoint * feat(cutover): send balance move payment checkpoint * feat(cutover): verify balance move checkpoint * feat(cutover): create fee reimbursement invoice checkpoint * feat(cutover): complete fee reimbursement checkpoint * feat(cutover): flip default wallet checkpoint * feat(cutover): complete migration worker checkpoints * feat(cutover): provision destination checkpoint * feat(cutover): dispatch migration worker steps * feat(cutover): run locked migration batches * feat(cutover): build migration step handlers * feat(cutover): wire migration runtime services * feat(cutover): orchestrate primary migration batches * feat(cutover): add migration lifecycle controls * fix(cutover): align migration indexes with run ids * feat(cutover): add operator command script * chore(cutover): format migration state helpers * feat(cutover): preview dry-run migration plan * fix(cutover): satisfy production build types * feat(cutover): add operator controls and verification * feat(cutover): add client-aware cash wallet presentation * chore(cutover): clean operator artifact whitespace --- dev/apollo-federation/supergraph.graphql | 24 + .../01-query-admin-state.bru | 48 + .../02-set-scheduled-pre.bru | 61 + .../03-set-in-progress.bru | 60 + .../cash-wallet-cutover/04-set-complete.bru | 60 + .../admin/cash-wallet-cutover/folder.bru | 4 + .../notoken/queries/cash-wallet-cutover.bru | 44 + operator-runs/eng-345-manual-347/findings.md | 6 + .../funding-retry-results.json | 97 + .../eng-345-manual-347/manifest.json | 78 + .../eng-345-manual-347/prep-348-results.json | 344 +++ operator-runs/eng-345-manual-347/progress.md | 45 + operator-runs/eng-345-manual-347/results.json | 95 + operator-runs/eng-345-manual-347/run.ts | 477 ++++ operator-runs/eng-345-manual-347/task_plan.md | 70 + .../verify-348-prep-results.json | 74 + .../cash-wallet-cutover/amount-conversion.ts | 54 + .../cash-wallet-cutover/client-capability.ts | 37 + src/app/cash-wallet-cutover/discovery.ts | 78 + src/app/cash-wallet-cutover/errors.ts | 11 + src/app/cash-wallet-cutover/executor.ts | 39 + src/app/cash-wallet-cutover/guard.ts | 77 + src/app/cash-wallet-cutover/handlers.ts | 173 ++ src/app/cash-wallet-cutover/index.ts | 23 + src/app/cash-wallet-cutover/index.types.d.ts | 67 + src/app/cash-wallet-cutover/lifecycle.ts | 174 ++ .../cash-wallet-cutover/migration-records.ts | 28 + src/app/cash-wallet-cutover/orchestrator.ts | 52 + src/app/cash-wallet-cutover/planner.ts | 41 + src/app/cash-wallet-cutover/preflight.ts | 53 + src/app/cash-wallet-cutover/prepare.ts | 63 + .../presentation-for-account.ts | 94 + src/app/cash-wallet-cutover/presentation.ts | 82 + src/app/cash-wallet-cutover/preview.ts | 54 + src/app/cash-wallet-cutover/runner.ts | 129 + .../cash-wallet-cutover/runtime-services.ts | 266 +++ src/app/cash-wallet-cutover/state-machine.ts | 43 + src/app/cash-wallet-cutover/worker.ts | 497 ++++ src/app/errors.ts | 2 + src/app/index.ts | 3 + src/graphql/admin/mutations.ts | 5 +- src/graphql/admin/queries.ts | 2 + .../mutation/cash-wallet-cutover-update.ts | 61 + src/graphql/admin/schema.graphql | 34 + .../types/payload/cash-wallet-cutover.ts | 13 + src/graphql/error-map.ts | 36 + src/graphql/public/queries.ts | 2 + .../mutation/intraledger-usd-payment-send.ts | 22 +- .../ln-noamount-usd-invoice-fee-probe.ts | 28 +- .../ln-noamount-usd-invoice-payment-send.ts | 19 +- .../root/mutation/ln-usd-invoice-create.ts | 21 +- .../root/mutation/ln-usd-invoice-fee-probe.ts | 19 +- .../root/mutation/onchain-usd-payment-send.ts | 31 +- .../root/query/account-default-wallet-id.ts | 14 +- .../root/query/account-default-wallet.ts | 23 +- src/graphql/public/schema.graphql | 20 + .../public/types/object/business-account.ts | 65 +- .../public/types/object/consumer-account.ts | 47 +- .../shared/root/query/cash-wallet-cutover.ts | 14 + .../types/object/cash-wallet-cutover.ts | 21 + src/graphql/shared/types/object/usd-wallet.ts | 35 +- .../types/scalar/cash-wallet-cutover-state.ts | 12 + src/scripts/cash-wallet-cutover.ts | 130 + src/servers/graphql-main-server.ts | 17 +- src/servers/index.files.d.ts | 1 + src/servers/middlewares/session.ts | 5 +- src/servers/ws-server.ts | 19 +- src/services/mongoose/cash-wallet-cutover.ts | 329 +++ src/services/mongoose/index.ts | 1 + src/services/mongoose/schema.ts | 62 + src/services/mongoose/schema.types.d.ts | 46 + .../amount-conversion.spec.ts | 50 + .../client-capability.spec.ts | 46 + .../cash-wallet-cutover/cutover-gate.spec.ts | 154 ++ .../discovery-collector.spec.ts | 86 + .../app/cash-wallet-cutover/discovery.spec.ts | 111 + .../app/cash-wallet-cutover/executor.spec.ts | 82 + .../app/cash-wallet-cutover/handlers.spec.ts | 231 ++ .../app/cash-wallet-cutover/lifecycle.spec.ts | 190 ++ .../migration-records.spec.ts | 70 + .../migration-state-machine.spec.ts | 55 + .../cash-wallet-cutover/orchestrator.spec.ts | 84 + .../app/cash-wallet-cutover/planner.spec.ts | 51 + .../app/cash-wallet-cutover/preflight.spec.ts | 65 + .../app/cash-wallet-cutover/prepare.spec.ts | 123 + .../presentation-for-account.spec.ts | 135 ++ .../cash-wallet-cutover/presentation.spec.ts | 120 + .../app/cash-wallet-cutover/preview.spec.ts | 81 + .../app/cash-wallet-cutover/runner.spec.ts | 126 + .../runtime-services.spec.ts | 222 ++ .../app/cash-wallet-cutover/worker.spec.ts | 863 +++++++ .../unit/graphql/cash-wallet-cutover.spec.ts | 81 + .../shared/types/object/usd-wallet.spec.ts | 17 + .../mongoose/cash-wallet-cutover.spec.ts | 242 ++ yarn.lock | 2088 ++++++++--------- 95 files changed, 9040 insertions(+), 1214 deletions(-) create mode 100644 dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/01-query-admin-state.bru create mode 100644 dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/02-set-scheduled-pre.bru create mode 100644 dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/03-set-in-progress.bru create mode 100644 dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/04-set-complete.bru create mode 100644 dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/folder.bru create mode 100644 dev/bruno/Flash GraphQL API/notoken/queries/cash-wallet-cutover.bru create mode 100644 operator-runs/eng-345-manual-347/findings.md create mode 100644 operator-runs/eng-345-manual-347/funding-retry-results.json create mode 100644 operator-runs/eng-345-manual-347/manifest.json create mode 100644 operator-runs/eng-345-manual-347/prep-348-results.json create mode 100644 operator-runs/eng-345-manual-347/progress.md create mode 100644 operator-runs/eng-345-manual-347/results.json create mode 100644 operator-runs/eng-345-manual-347/run.ts create mode 100644 operator-runs/eng-345-manual-347/task_plan.md create mode 100644 operator-runs/eng-345-manual-347/verify-348-prep-results.json create mode 100644 src/app/cash-wallet-cutover/amount-conversion.ts create mode 100644 src/app/cash-wallet-cutover/client-capability.ts create mode 100644 src/app/cash-wallet-cutover/discovery.ts create mode 100644 src/app/cash-wallet-cutover/errors.ts create mode 100644 src/app/cash-wallet-cutover/executor.ts create mode 100644 src/app/cash-wallet-cutover/guard.ts create mode 100644 src/app/cash-wallet-cutover/handlers.ts create mode 100644 src/app/cash-wallet-cutover/index.ts create mode 100644 src/app/cash-wallet-cutover/index.types.d.ts create mode 100644 src/app/cash-wallet-cutover/lifecycle.ts create mode 100644 src/app/cash-wallet-cutover/migration-records.ts create mode 100644 src/app/cash-wallet-cutover/orchestrator.ts create mode 100644 src/app/cash-wallet-cutover/planner.ts create mode 100644 src/app/cash-wallet-cutover/preflight.ts create mode 100644 src/app/cash-wallet-cutover/prepare.ts create mode 100644 src/app/cash-wallet-cutover/presentation-for-account.ts create mode 100644 src/app/cash-wallet-cutover/presentation.ts create mode 100644 src/app/cash-wallet-cutover/preview.ts create mode 100644 src/app/cash-wallet-cutover/runner.ts create mode 100644 src/app/cash-wallet-cutover/runtime-services.ts create mode 100644 src/app/cash-wallet-cutover/state-machine.ts create mode 100644 src/app/cash-wallet-cutover/worker.ts create mode 100644 src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts create mode 100644 src/graphql/admin/types/payload/cash-wallet-cutover.ts create mode 100644 src/graphql/shared/root/query/cash-wallet-cutover.ts create mode 100644 src/graphql/shared/types/object/cash-wallet-cutover.ts create mode 100644 src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts create mode 100644 src/scripts/cash-wallet-cutover.ts create mode 100644 src/services/mongoose/cash-wallet-cutover.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/amount-conversion.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/client-capability.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/cutover-gate.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/discovery-collector.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/discovery.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/executor.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/handlers.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/migration-records.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/migration-state-machine.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/orchestrator.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/planner.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/preflight.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/prepare.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/presentation-for-account.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/preview.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/runner.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/runtime-services.spec.ts create mode 100644 test/flash/unit/app/cash-wallet-cutover/worker.spec.ts create mode 100644 test/flash/unit/graphql/cash-wallet-cutover.spec.ts create mode 100644 test/flash/unit/graphql/shared/types/object/usd-wallet.spec.ts create mode 100644 test/flash/unit/services/mongoose/cash-wallet-cutover.spec.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 865c5fd87..baeee5a52 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -501,6 +501,29 @@ type CashoutOffer walletId: WalletId! } +type CashWalletCutover + @join__type(graph: PUBLIC) +{ + completedAt: Timestamp + cutoverVersion: Int! + pauseReason: String + pausedAt: Timestamp + runId: String + scheduledAt: Timestamp + startedAt: Timestamp + state: CashWalletCutoverState! + updatedAt: Timestamp! + updatedBy: String +} + +enum CashWalletCutoverState + @join__type(graph: PUBLIC) +{ + COMPLETE @join__enumValue(graph: PUBLIC) + IN_PROGRESS @join__enumValue(graph: PUBLIC) + PRE @join__enumValue(graph: PUBLIC) +} + """(Positive) Cent amount (1/100 of a dollar)""" scalar CentAmount @join__type(graph: PUBLIC) @@ -1651,6 +1674,7 @@ type Query btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! + cashWalletCutover: CashWalletCutover! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload diff --git a/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/01-query-admin-state.bru b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/01-query-admin-state.bru new file mode 100644 index 000000000..1cc27b2f2 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/01-query-admin-state.bru @@ -0,0 +1,48 @@ +meta { + name: 01-query-admin-state + type: graphql + seq: 1 +} + +post { + url: {{admin_url}} + body: graphql + auth: bearer +} + +auth:bearer { + token: {{admin_token}} +} + +body:graphql { + query CashWalletCutoverAdminState { + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pausedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } + } +} + +body:graphql:vars { + {} +} + +script:post-response { + test("admin cutover state returns without GraphQL errors", function () { + const jsonData = res.getBody() + expect(jsonData.errors).to.be.undefined + expect(jsonData.data.cashWalletCutover.state).to.be.oneOf([ + "PRE", + "IN_PROGRESS", + "COMPLETE", + ]) + }) +} diff --git a/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/02-set-scheduled-pre.bru b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/02-set-scheduled-pre.bru new file mode 100644 index 000000000..963c26430 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/02-set-scheduled-pre.bru @@ -0,0 +1,61 @@ +meta { + name: 02-set-scheduled-pre + type: graphql + seq: 2 +} + +post { + url: {{admin_url}} + body: graphql + auth: bearer +} + +auth:bearer { + token: {{admin_token}} +} + +body:graphql { + mutation CashWalletCutoverSetScheduledPre($input: CashWalletCutoverUpdateInput!) { + cashWalletCutoverUpdate(input: $input) { + errors { + message + } + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "state": "PRE", + "scheduledAt": "2026-05-22T15:00:00.000Z", + "cutoverVersion": 345, + "runId": "manual-eng-345", + "pauseReason": "manual ENG-345 preflight" + } + } +} + +script:post-response { + test("sets cutover config to PRE with schedule metadata", function () { + const jsonData = res.getBody() + expect(jsonData.errors).to.be.undefined + expect(jsonData.data.cashWalletCutoverUpdate.errors).to.eql([]) + + const config = jsonData.data.cashWalletCutoverUpdate.cashWalletCutover + expect(config.state).to.eql("PRE") + expect(config.cutoverVersion).to.eql(345) + expect(config.runId).to.eql("manual-eng-345") + }) +} diff --git a/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/03-set-in-progress.bru b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/03-set-in-progress.bru new file mode 100644 index 000000000..e44b497d4 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/03-set-in-progress.bru @@ -0,0 +1,60 @@ +meta { + name: 03-set-in-progress + type: graphql + seq: 3 +} + +post { + url: {{admin_url}} + body: graphql + auth: bearer +} + +auth:bearer { + token: {{admin_token}} +} + +body:graphql { + mutation CashWalletCutoverSetInProgress($input: CashWalletCutoverUpdateInput!) { + cashWalletCutoverUpdate(input: $input) { + errors { + message + } + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "state": "IN_PROGRESS", + "cutoverVersion": 345, + "runId": "manual-eng-345", + "pauseReason": "manual ENG-345 in-progress test" + } + } +} + +script:post-response { + test("sets cutover config to IN_PROGRESS", function () { + const jsonData = res.getBody() + expect(jsonData.errors).to.be.undefined + expect(jsonData.data.cashWalletCutoverUpdate.errors).to.eql([]) + + const config = jsonData.data.cashWalletCutoverUpdate.cashWalletCutover + expect(config.state).to.eql("IN_PROGRESS") + expect(config.cutoverVersion).to.eql(345) + expect(config.runId).to.eql("manual-eng-345") + }) +} diff --git a/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/04-set-complete.bru b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/04-set-complete.bru new file mode 100644 index 000000000..c8ab4e0f8 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/04-set-complete.bru @@ -0,0 +1,60 @@ +meta { + name: 04-set-complete + type: graphql + seq: 4 +} + +post { + url: {{admin_url}} + body: graphql + auth: bearer +} + +auth:bearer { + token: {{admin_token}} +} + +body:graphql { + mutation CashWalletCutoverSetComplete($input: CashWalletCutoverUpdateInput!) { + cashWalletCutoverUpdate(input: $input) { + errors { + message + } + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "state": "COMPLETE", + "cutoverVersion": 345, + "runId": "manual-eng-345", + "pauseReason": "manual ENG-345 complete test" + } + } +} + +script:post-response { + test("sets cutover config to COMPLETE", function () { + const jsonData = res.getBody() + expect(jsonData.errors).to.be.undefined + expect(jsonData.data.cashWalletCutoverUpdate.errors).to.eql([]) + + const config = jsonData.data.cashWalletCutoverUpdate.cashWalletCutover + expect(config.state).to.eql("COMPLETE") + expect(config.cutoverVersion).to.eql(345) + expect(config.runId).to.eql("manual-eng-345") + }) +} diff --git a/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/folder.bru b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/folder.bru new file mode 100644 index 000000000..c180d00b2 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/folder.bru @@ -0,0 +1,4 @@ +meta { + name: cash-wallet-cutover + seq: 10 +} diff --git a/dev/bruno/Flash GraphQL API/notoken/queries/cash-wallet-cutover.bru b/dev/bruno/Flash GraphQL API/notoken/queries/cash-wallet-cutover.bru new file mode 100644 index 000000000..1c6f7cd06 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/notoken/queries/cash-wallet-cutover.bru @@ -0,0 +1,44 @@ +meta { + name: cash-wallet-cutover + type: graphql + seq: 11 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +body:graphql { + query CashWalletCutoverPublicState { + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pausedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } + } +} + +body:graphql:vars { + {} +} + +script:post-response { + test("public cutover state returns without GraphQL errors", function () { + const jsonData = res.getBody() + expect(jsonData.errors).to.be.undefined + expect(jsonData.data.cashWalletCutover.state).to.be.oneOf([ + "PRE", + "IN_PROGRESS", + "COMPLETE", + ]) + }) +} diff --git a/operator-runs/eng-345-manual-347/findings.md b/operator-runs/eng-345-manual-347/findings.md new file mode 100644 index 000000000..79191ab38 --- /dev/null +++ b/operator-runs/eng-345-manual-347/findings.md @@ -0,0 +1,6 @@ +# Findings + +- `preparePrimaryCashWalletCutover` accepts injected repositories, so the test can scope discovery to the 10 created accounts by passing an `accountsRepo.listUnlockedAccounts()` generator for only those IDs. +- Account creation creates both USD and USDT checking wallets and defaults new accounts to USDT. For this test, each new account must be explicitly updated back to the USD wallet with `Accounts.updateDefaultWalletId`. +- Funding can use `Payments.intraledgerPaymentSendWalletIdForUsdWallet` from the funder account's USD wallet to each target legacy USD wallet. The amount argument is cents, so `$0.25` is `25` and `$0.01` is `1`. +- Cutover batch execution can use `CashWalletCutover.runPrimaryCashWalletCutoverBatch`; status and lifecycle can use the existing app lifecycle functions. diff --git a/operator-runs/eng-345-manual-347/funding-retry-results.json b/operator-runs/eng-345-manual-347/funding-retry-results.json new file mode 100644 index 000000000..0f9591d76 --- /dev/null +++ b/operator-runs/eng-345-manual-347/funding-retry-results.json @@ -0,0 +1,97 @@ +{ + "targetCents": 25, + "funderUsdWalletId": "37a3bce4-1930-484d-bbfc-279c1f8bfb66", + "results": [ + { + "index": 1, + "accountId": "6a11ada7e55310755eeb0257", + "legacyUsdWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + }, + { + "index": 2, + "accountId": "6a11ada8e55310755eeb0271", + "legacyUsdWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + }, + { + "index": 3, + "accountId": "6a11ada9e55310755eeb028b", + "legacyUsdWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + }, + { + "index": 4, + "accountId": "6a11ada9e55310755eeb02a5", + "legacyUsdWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + }, + { + "index": 5, + "accountId": "6a11adabe55310755eeb02bf", + "legacyUsdWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + }, + { + "index": 6, + "accountId": "6a11adace55310755eeb02d9", + "legacyUsdWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + }, + { + "index": 7, + "accountId": "6a11adade55310755eeb02f3", + "legacyUsdWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "beforeCents": 0, + "targetCents": 25, + "deltaCents": 25, + "status": { + "value": "success" + }, + "error": null, + "afterCents": 25 + } + ] +} diff --git a/operator-runs/eng-345-manual-347/manifest.json b/operator-runs/eng-345-manual-347/manifest.json new file mode 100644 index 000000000..e1af02bce --- /dev/null +++ b/operator-runs/eng-345-manual-347/manifest.json @@ -0,0 +1,78 @@ +{ + "cutoverVersion": 347, + "runId": "manual-eng-347", + "createdAt": "2026-05-23T13:37:44.318Z", + "funderUsdWalletId": "37a3bce4-1930-484d-bbfc-279c1f8bfb66", + "accounts": [ + { + "index": 1, + "phone": "+16509-recovered-01", + "kratosUserId": "03cc58c6-a325-4966-9cf0-a96a58a8b366", + "accountId": "6a11ada7e55310755eeb0257", + "accountUuid": "2da55813-e2a6-4bfc-946d-dd52af5b6a99", + "legacyUsdWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "destinationUsdtWalletId": "8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6", + "startingFundingCents": 25 + }, + { + "index": 2, + "phone": "+16509-recovered-02", + "kratosUserId": "4b7a6cea-7024-47d2-a4ba-f9df7d97aa7b", + "accountId": "6a11ada8e55310755eeb0271", + "accountUuid": "71b5a288-4a46-4e9d-858c-87012d55c12e", + "legacyUsdWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "destinationUsdtWalletId": "741a97b0-9612-4f34-af32-ed28c0a3b5fd", + "startingFundingCents": 25 + }, + { + "index": 3, + "phone": "+16509-recovered-03", + "kratosUserId": "cb7eee02-7cb5-41a3-a4a5-1210624cb309", + "accountId": "6a11ada9e55310755eeb028b", + "accountUuid": "a822f6c2-c8ed-4c6e-a5ce-20c93abaad66", + "legacyUsdWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "destinationUsdtWalletId": "144db453-fdb8-4180-b405-097104637644", + "startingFundingCents": 25 + }, + { + "index": 4, + "phone": "+16509-recovered-04", + "kratosUserId": "76fe0bc7-1a9c-440b-9586-317781c153f4", + "accountId": "6a11ada9e55310755eeb02a5", + "accountUuid": "87c4db3b-0aa8-47e3-942d-8fe845a6c6b7", + "legacyUsdWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "destinationUsdtWalletId": "ae3bddb2-1bb7-4019-8390-39694881c91b", + "startingFundingCents": 25 + }, + { + "index": 5, + "phone": "+16509-recovered-05", + "kratosUserId": "4736f580-cd98-4750-ab16-1da95e986dc9", + "accountId": "6a11adabe55310755eeb02bf", + "accountUuid": "bbf1af43-70ef-4e22-a40c-eba31289735a", + "legacyUsdWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "destinationUsdtWalletId": "fa7c7780-9246-4088-beb6-56fcd98e3209", + "startingFundingCents": 25 + }, + { + "index": 6, + "phone": "+16509-recovered-06", + "kratosUserId": "c5cab6ac-a56e-431b-a460-118d3627f7d3", + "accountId": "6a11adace55310755eeb02d9", + "accountUuid": "06ee59a8-8d59-4139-99d1-92378e5b8011", + "legacyUsdWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "destinationUsdtWalletId": "53884f7a-0a55-4406-8b60-3c6d68a6c180", + "startingFundingCents": 25 + }, + { + "index": 7, + "phone": "+16509-recovered-07", + "kratosUserId": "c1df1aa1-18c6-4ec9-97a6-9dfc5736be7f", + "accountId": "6a11adade55310755eeb02f3", + "accountUuid": "019555c0-b26a-468f-b11e-5bbe69048e02", + "legacyUsdWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "destinationUsdtWalletId": "80f0de1f-b4e6-4904-8c3f-d90bad763ce2", + "startingFundingCents": 25 + } + ] +} diff --git a/operator-runs/eng-345-manual-347/prep-348-results.json b/operator-runs/eng-345-manual-347/prep-348-results.json new file mode 100644 index 000000000..951b9cb85 --- /dev/null +++ b/operator-runs/eng-345-manual-347/prep-348-results.json @@ -0,0 +1,344 @@ +{ + "cutoverVersion": 348, + "runId": "manual-eng-348", + "flips": [ + { + "index": 1, + "accountId": "6a11ada7e55310755eeb0257", + "defaultWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + }, + { + "index": 2, + "accountId": "6a11ada8e55310755eeb0271", + "defaultWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + }, + { + "index": 3, + "accountId": "6a11ada9e55310755eeb028b", + "defaultWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + }, + { + "index": 4, + "accountId": "6a11ada9e55310755eeb02a5", + "defaultWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + }, + { + "index": 5, + "accountId": "6a11adabe55310755eeb02bf", + "defaultWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + }, + { + "index": 6, + "accountId": "6a11adace55310755eeb02d9", + "defaultWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + }, + { + "index": 7, + "accountId": "6a11adade55310755eeb02f3", + "defaultWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25" + } + ], + "configReset": { + "acknowledged": true, + "modifiedCount": 1, + "upsertedId": null, + "upsertedCount": 0, + "matchedCount": 1 + }, + "preview": { + "report": { + "cutoverVersion": 348, + "runId": "manual-eng-348", + "totalAccounts": 7, + "migrationCandidates": 7, + "alreadyUsdt": 0, + "residualLegacyUsd": 0, + "blockers": 0, + "blockerAccounts": [], + "canStart": true + }, + "plannedMigrations": [ + { + "accountId": "6a11ada7e55310755eeb0257", + "accountUuid": "2da55813-e2a6-4bfc-946d-dd52af5b6a99", + "legacyUsdWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "destinationUsdtWalletId": "8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6", + "previousDefaultWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada7e55310755eeb0257" + }, + { + "accountId": "6a11ada8e55310755eeb0271", + "accountUuid": "71b5a288-4a46-4e9d-858c-87012d55c12e", + "legacyUsdWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "destinationUsdtWalletId": "741a97b0-9612-4f34-af32-ed28c0a3b5fd", + "previousDefaultWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada8e55310755eeb0271" + }, + { + "accountId": "6a11ada9e55310755eeb028b", + "accountUuid": "a822f6c2-c8ed-4c6e-a5ce-20c93abaad66", + "legacyUsdWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "destinationUsdtWalletId": "144db453-fdb8-4180-b405-097104637644", + "previousDefaultWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada9e55310755eeb028b" + }, + { + "accountId": "6a11ada9e55310755eeb02a5", + "accountUuid": "87c4db3b-0aa8-47e3-942d-8fe845a6c6b7", + "legacyUsdWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "destinationUsdtWalletId": "ae3bddb2-1bb7-4019-8390-39694881c91b", + "previousDefaultWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada9e55310755eeb02a5" + }, + { + "accountId": "6a11adabe55310755eeb02bf", + "accountUuid": "bbf1af43-70ef-4e22-a40c-eba31289735a", + "legacyUsdWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "destinationUsdtWalletId": "fa7c7780-9246-4088-beb6-56fcd98e3209", + "previousDefaultWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adabe55310755eeb02bf" + }, + { + "accountId": "6a11adace55310755eeb02d9", + "accountUuid": "06ee59a8-8d59-4139-99d1-92378e5b8011", + "legacyUsdWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "destinationUsdtWalletId": "53884f7a-0a55-4406-8b60-3c6d68a6c180", + "previousDefaultWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adace55310755eeb02d9" + }, + { + "accountId": "6a11adade55310755eeb02f3", + "accountUuid": "019555c0-b26a-468f-b11e-5bbe69048e02", + "legacyUsdWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "destinationUsdtWalletId": "80f0de1f-b4e6-4904-8c3f-d90bad763ce2", + "previousDefaultWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adade55310755eeb02f3" + } + ] + }, + "prepared": { + "report": { + "cutoverVersion": 348, + "runId": "manual-eng-348", + "totalAccounts": 7, + "migrationCandidates": 7, + "alreadyUsdt": 0, + "residualLegacyUsd": 0, + "blockers": 0, + "blockerAccounts": [], + "canStart": true + }, + "plannedMigrations": [ + { + "accountId": "6a11ada7e55310755eeb0257", + "accountUuid": "2da55813-e2a6-4bfc-946d-dd52af5b6a99", + "legacyUsdWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "destinationUsdtWalletId": "8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6", + "previousDefaultWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada7e55310755eeb0257" + }, + { + "accountId": "6a11ada8e55310755eeb0271", + "accountUuid": "71b5a288-4a46-4e9d-858c-87012d55c12e", + "legacyUsdWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "destinationUsdtWalletId": "741a97b0-9612-4f34-af32-ed28c0a3b5fd", + "previousDefaultWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada8e55310755eeb0271" + }, + { + "accountId": "6a11ada9e55310755eeb028b", + "accountUuid": "a822f6c2-c8ed-4c6e-a5ce-20c93abaad66", + "legacyUsdWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "destinationUsdtWalletId": "144db453-fdb8-4180-b405-097104637644", + "previousDefaultWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada9e55310755eeb028b" + }, + { + "accountId": "6a11ada9e55310755eeb02a5", + "accountUuid": "87c4db3b-0aa8-47e3-942d-8fe845a6c6b7", + "legacyUsdWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "destinationUsdtWalletId": "ae3bddb2-1bb7-4019-8390-39694881c91b", + "previousDefaultWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada9e55310755eeb02a5" + }, + { + "accountId": "6a11adabe55310755eeb02bf", + "accountUuid": "bbf1af43-70ef-4e22-a40c-eba31289735a", + "legacyUsdWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "destinationUsdtWalletId": "fa7c7780-9246-4088-beb6-56fcd98e3209", + "previousDefaultWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adabe55310755eeb02bf" + }, + { + "accountId": "6a11adace55310755eeb02d9", + "accountUuid": "06ee59a8-8d59-4139-99d1-92378e5b8011", + "legacyUsdWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "destinationUsdtWalletId": "53884f7a-0a55-4406-8b60-3c6d68a6c180", + "previousDefaultWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adace55310755eeb02d9" + }, + { + "accountId": "6a11adade55310755eeb02f3", + "accountUuid": "019555c0-b26a-468f-b11e-5bbe69048e02", + "legacyUsdWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "destinationUsdtWalletId": "80f0de1f-b4e6-4904-8c3f-d90bad763ce2", + "previousDefaultWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adade55310755eeb02f3" + } + ], + "migrations": [ + { + "id": "24353ef3-66ba-4acc-9365-1ad6e83301f0", + "accountId": "6a11ada7e55310755eeb0257", + "accountUuid": "2da55813-e2a6-4bfc-946d-dd52af5b6a99", + "legacyUsdWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "destinationUsdtWalletId": "8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6", + "previousDefaultWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada7e55310755eeb0257", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.370Z" + }, + { + "id": "d47e3b77-39d0-49ea-a5eb-e0dbbf5a306c", + "accountId": "6a11ada8e55310755eeb0271", + "accountUuid": "71b5a288-4a46-4e9d-858c-87012d55c12e", + "legacyUsdWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "destinationUsdtWalletId": "741a97b0-9612-4f34-af32-ed28c0a3b5fd", + "previousDefaultWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada8e55310755eeb0271", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.382Z" + }, + { + "id": "b5b42c93-519a-4103-b7fb-a1ff5a3dd17d", + "accountId": "6a11ada9e55310755eeb028b", + "accountUuid": "a822f6c2-c8ed-4c6e-a5ce-20c93abaad66", + "legacyUsdWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "destinationUsdtWalletId": "144db453-fdb8-4180-b405-097104637644", + "previousDefaultWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada9e55310755eeb028b", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.393Z" + }, + { + "id": "1eafc20f-3e53-4d72-ba25-48cd8d5c5c24", + "accountId": "6a11ada9e55310755eeb02a5", + "accountUuid": "87c4db3b-0aa8-47e3-942d-8fe845a6c6b7", + "legacyUsdWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "destinationUsdtWalletId": "ae3bddb2-1bb7-4019-8390-39694881c91b", + "previousDefaultWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11ada9e55310755eeb02a5", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.401Z" + }, + { + "id": "78fdd4b6-9c81-4883-a0e0-0bab20491c17", + "accountId": "6a11adabe55310755eeb02bf", + "accountUuid": "bbf1af43-70ef-4e22-a40c-eba31289735a", + "legacyUsdWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "destinationUsdtWalletId": "fa7c7780-9246-4088-beb6-56fcd98e3209", + "previousDefaultWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adabe55310755eeb02bf", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.406Z" + }, + { + "id": "9cf2b55b-d118-4b2a-a322-9c857e2dee8d", + "accountId": "6a11adace55310755eeb02d9", + "accountUuid": "06ee59a8-8d59-4139-99d1-92378e5b8011", + "legacyUsdWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "destinationUsdtWalletId": "53884f7a-0a55-4406-8b60-3c6d68a6c180", + "previousDefaultWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adace55310755eeb02d9", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.414Z" + }, + { + "id": "20e0769a-6c20-4475-bb0a-9eda26698668", + "accountId": "6a11adade55310755eeb02f3", + "accountUuid": "019555c0-b26a-468f-b11e-5bbe69048e02", + "legacyUsdWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "destinationUsdtWalletId": "80f0de1f-b4e6-4904-8c3f-d90bad763ce2", + "previousDefaultWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": "not_started", + "idempotencyKey": "cash-wallet-cutover:manual-eng-348:6a11adade55310755eeb02f3", + "attempts": 0, + "updatedAt": "2026-05-23T14:49:36.424Z" + } + ] + }, + "status": { + "config": { + "state": "pre", + "updatedBy": "manual-local", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "updatedAt": "2026-05-23T14:49:36.246Z" + }, + "countsByStatus": { + "not_started": 7 + } + } +} diff --git a/operator-runs/eng-345-manual-347/progress.md b/operator-runs/eng-345-manual-347/progress.md new file mode 100644 index 000000000..2455d031d --- /dev/null +++ b/operator-runs/eng-345-manual-347/progress.md @@ -0,0 +1,45 @@ +- 2026-05-23T13:37:13.813Z ERROR Cannot read properties of undefined (reading 'findOne') +- 2026-05-23T13:37:44.318Z created account 1: 6a11ada7e55310755eeb0257 USD=0a4d1c55-d8ec-4685-8457-216d41569d61 USDT=8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6 funding=25 +- 2026-05-23T13:37:45.186Z created account 2: 6a11ada8e55310755eeb0271 USD=08ee5f13-b4c3-4ff5-835a-55dd786d3887 USDT=741a97b0-9612-4f34-af32-ed28c0a3b5fd funding=25 +- 2026-05-23T13:37:45.866Z created account 3: 6a11ada9e55310755eeb028b USD=697b8583-6e1d-47d5-aba0-8b2c8aa6bc32 USDT=144db453-fdb8-4180-b405-097104637644 funding=25 +- 2026-05-23T13:37:47.835Z created account 4: 6a11ada9e55310755eeb02a5 USD=dbdfba0c-ead0-4c94-96f8-e15130b7f796 USDT=ae3bddb2-1bb7-4019-8390-39694881c91b funding=25 +- 2026-05-23T13:37:48.713Z created account 5: 6a11adabe55310755eeb02bf USD=9756a443-d30d-4b1c-a797-933ec2ced1d0 USDT=fa7c7780-9246-4088-beb6-56fcd98e3209 funding=25 +- 2026-05-23T13:37:49.430Z created account 6: 6a11adace55310755eeb02d9 USD=ecaf103c-d024-419a-8bad-b0c61e8068be USDT=53884f7a-0a55-4406-8b60-3c6d68a6c180 funding=25 +- 2026-05-23T13:37:50.263Z created account 7: 6a11adade55310755eeb02f3 USD=8a3e1bd4-4d2d-4f97-b201-d58d3723a53c USDT=80f0de1f-b4e6-4904-8c3f-d90bad763ce2 funding=25 +- 2026-05-23T13:37:50.789Z ERROR +- 2026-05-23T13:39:59.481Z ERROR +- 2026-05-23T13:41:36.710Z ERROR +- 2026-05-23T13:42:04.155Z wrote verification results to /Users/dread/Documents/Island-Bitcoin/Flash/flash/.worktrees/eng-345-review/operator-runs/eng-345-manual-347/results.json +- 2026-05-23T13:42:26.832Z ERROR +- 2026-05-23T13:44:28.884Z ERROR +- 2026-05-23T13:50:13.799Z preview planned=7 +- 2026-05-23T13:50:27.921Z prepared migrations=7 +- 2026-05-23T13:50:44.208Z reset singleton cutover config to pre for manual-eng-347 +- 2026-05-23T13:50:48.388Z started state=in_progress +- 2026-05-23T13:51:03.235Z batch 1: {"attempted":7,"advanced":7,"failed":0,"skipped":0} +- 2026-05-23T13:51:03.298Z batch 2: {"attempted":7,"advanced":7,"failed":0,"skipped":0} +- 2026-05-23T13:51:06.709Z batch 3: {"attempted":7,"advanced":7,"failed":0,"skipped":0} +- 2026-05-23T13:51:06.840Z batch 4: {"attempted":7,"advanced":7,"failed":0,"skipped":0} +- 2026-05-23T13:51:08.144Z batch 5: {"attempted":7,"advanced":7,"failed":0,"skipped":0} +- 2026-05-23T13:51:08.238Z batch 6: {"attempted":7,"advanced":7,"failed":0,"skipped":0} +- 2026-05-23T13:51:08.268Z all migrations complete after batch 6 +- 2026-05-23T13:51:25.286Z completed lifecycle state=complete +- 2026-05-23T13:51:31.895Z wrote verification results to /Users/dread/Documents/Island-Bitcoin/Flash/flash/.worktrees/eng-345-review/operator-runs/eng-345-manual-347/results.json +- 2026-05-23T14:05:39.454Z funding retry account 1: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:05:42.219Z funding retry account 2: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:05:44.498Z funding retry account 3: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:05:46.786Z funding retry account 4: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:05:49.479Z funding retry account 5: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:05:50.986Z funding retry account 6: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:05:52.277Z funding retry account 7: before=0 delta=25 status=[object Object] after=25 +- 2026-05-23T14:06:31.393Z wrote verification results to /Users/dread/Documents/Island-Bitcoin/Flash/flash/.worktrees/eng-345-review/operator-runs/eng-345-manual-347/results.json +- 2026-05-23T14:49:35.240Z reset account 1 defaultWalletId=0a4d1c55-d8ec-4685-8457-216d41569d61 fundedUsdCents=25 +- 2026-05-23T14:49:35.433Z reset account 2 defaultWalletId=08ee5f13-b4c3-4ff5-835a-55dd786d3887 fundedUsdCents=25 +- 2026-05-23T14:49:35.582Z reset account 3 defaultWalletId=697b8583-6e1d-47d5-aba0-8b2c8aa6bc32 fundedUsdCents=25 +- 2026-05-23T14:49:35.729Z reset account 4 defaultWalletId=dbdfba0c-ead0-4c94-96f8-e15130b7f796 fundedUsdCents=25 +- 2026-05-23T14:49:35.950Z reset account 5 defaultWalletId=9756a443-d30d-4b1c-a797-933ec2ced1d0 fundedUsdCents=25 +- 2026-05-23T14:49:36.092Z reset account 6 defaultWalletId=ecaf103c-d024-419a-8bad-b0c61e8068be fundedUsdCents=25 +- 2026-05-23T14:49:36.245Z reset account 7 defaultWalletId=8a3e1bd4-4d2d-4f97-b201-d58d3723a53c fundedUsdCents=25 +- 2026-05-23T14:49:36.256Z reset singleton cutover config to pre for manual-eng-348 +- 2026-05-23T14:49:36.323Z preview manual-eng-348 planned=7 +- 2026-05-23T14:49:36.431Z prepared manual-eng-348 migrations=7 diff --git a/operator-runs/eng-345-manual-347/results.json b/operator-runs/eng-345-manual-347/results.json new file mode 100644 index 000000000..57d76e89c --- /dev/null +++ b/operator-runs/eng-345-manual-347/results.json @@ -0,0 +1,95 @@ +{ + "status": { + "config": { + "state": "complete", + "startedAt": "2026-05-23T13:50:48.355Z", + "completedAt": "2026-05-23T13:51:25.254Z", + "updatedBy": "manual-local", + "cutoverVersion": 347, + "runId": "manual-eng-347", + "updatedAt": "2026-05-23T13:51:25.281Z" + }, + "countsByStatus": { + "complete": 7 + } + }, + "accounts": [ + { + "index": 1, + "accountId": "6a11ada7e55310755eeb0257", + "expectedFundingCents": 25, + "defaultWalletId": "8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "destinationUsdtWalletId": "8a97ec4c-bf67-4a74-a5c2-bc8cf28e99c6", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 2, + "accountId": "6a11ada8e55310755eeb0271", + "expectedFundingCents": 25, + "defaultWalletId": "741a97b0-9612-4f34-af32-ed28c0a3b5fd", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "destinationUsdtWalletId": "741a97b0-9612-4f34-af32-ed28c0a3b5fd", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 3, + "accountId": "6a11ada9e55310755eeb028b", + "expectedFundingCents": 25, + "defaultWalletId": "144db453-fdb8-4180-b405-097104637644", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "destinationUsdtWalletId": "144db453-fdb8-4180-b405-097104637644", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 4, + "accountId": "6a11ada9e55310755eeb02a5", + "expectedFundingCents": 25, + "defaultWalletId": "ae3bddb2-1bb7-4019-8390-39694881c91b", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "destinationUsdtWalletId": "ae3bddb2-1bb7-4019-8390-39694881c91b", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 5, + "accountId": "6a11adabe55310755eeb02bf", + "expectedFundingCents": 25, + "defaultWalletId": "fa7c7780-9246-4088-beb6-56fcd98e3209", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "destinationUsdtWalletId": "fa7c7780-9246-4088-beb6-56fcd98e3209", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 6, + "accountId": "6a11adace55310755eeb02d9", + "expectedFundingCents": 25, + "defaultWalletId": "53884f7a-0a55-4406-8b60-3c6d68a6c180", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "destinationUsdtWalletId": "53884f7a-0a55-4406-8b60-3c6d68a6c180", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 7, + "accountId": "6a11adade55310755eeb02f3", + "expectedFundingCents": 25, + "defaultWalletId": "80f0de1f-b4e6-4904-8c3f-d90bad763ce2", + "defaultIsDestinationUsdt": true, + "legacyUsdWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "destinationUsdtWalletId": "80f0de1f-b4e6-4904-8c3f-d90bad763ce2", + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + } + ] +} diff --git a/operator-runs/eng-345-manual-347/run.ts b/operator-runs/eng-345-manual-347/run.ts new file mode 100644 index 000000000..3438416d3 --- /dev/null +++ b/operator-runs/eng-345-manual-347/run.ts @@ -0,0 +1,477 @@ +import fs from "fs/promises" +import path from "path" +import { randomUUID } from "crypto" + +import { getDefaultAccountsConfig } from "@config" + +import { Accounts, CashWalletCutover, Payments, Wallets } from "@app" +import { getBalanceForWallet } from "@app/wallets" +import { WalletCurrency } from "@domain/shared" +import { PaymentSendStatus } from "@domain/bitcoin/lightning" +import { WalletType } from "@domain/wallets" + +import { setupMongoConnection } from "@services/mongodb" +import { + AccountsRepository, + CashWalletCutoverRepository, + WalletsRepository, +} from "@services/mongoose" +import { Account, CashWalletCutoverConfig } from "@services/mongoose/schema" + +type TargetAccount = { + index: number + phone: string + kratosUserId: string + accountId: string + accountUuid?: string + legacyUsdWalletId: string + destinationUsdtWalletId: string + startingFundingCents: number +} + +type Manifest = { + cutoverVersion: number + runId: string + createdAt: string + funderUsdWalletId?: string + accounts: TargetAccount[] +} + +const CUTOVER_VERSION = 347 +const RUN_ID = "manual-eng-347" +const ACCOUNT_COUNT = 10 +const OUTPUT_DIR = path.resolve("operator-runs/eng-345-manual-347") +const MANIFEST_PATH = path.join(OUTPUT_DIR, "manifest.json") +const RESULTS_PATH = path.join(OUTPUT_DIR, "results.json") +const PROGRESS_PATH = path.join(OUTPUT_DIR, "progress.md") + +const logProgress = async (line: string) => { + await fs.appendFile(PROGRESS_PATH, `- ${new Date().toISOString()} ${line}\n`) +} + +const throwIfError = (result: T | Error): T => { + if (result instanceof Error) throw result + return result +} + +const loadManifest = async (): Promise => { + const raw = await fs.readFile(MANIFEST_PATH, "utf8") + return JSON.parse(raw) as Manifest +} + +const saveManifest = async (manifest: Manifest) => { + await fs.writeFile(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}\n`) +} + +const listWalletsForAccount = async (accountId: string) => { + const wallets = throwIfError( + await WalletsRepository().listByAccountId(accountId as AccountId), + ) + const usd = wallets.find((wallet) => wallet.currency === WalletCurrency.Usd) + const usdt = wallets.find((wallet) => wallet.currency === WalletCurrency.Usdt) + + if (!usd) throw new Error(`Missing USD wallet for account ${accountId}`) + if (!usdt) throw new Error(`Missing USDT wallet for account ${accountId}`) + + return { usd, usdt } +} + +const funderUsdWalletId = async (): Promise => { + const funder = await Account.findOne({ role: "funder" }) + if (!funder) throw new Error("Missing funder account") + + const wallets = throwIfError( + await WalletsRepository().listByAccountId(funder._id.toString() as AccountId), + ) + const usdWallet = wallets.find((wallet) => wallet.currency === WalletCurrency.Usd) + if (!usdWallet) throw new Error("Missing funder USD wallet") + + return usdWallet.id +} + +const createAccounts = async () => { + await fs.mkdir(OUTPUT_DIR, { recursive: true }) + + const config = getDefaultAccountsConfig() + const existing = await fs + .readFile(MANIFEST_PATH, "utf8") + .then((raw) => JSON.parse(raw) as Manifest) + .catch(() => undefined) + const funderWalletId = existing?.funderUsdWalletId ?? (await funderUsdWalletId()) + const accounts: TargetAccount[] = existing?.accounts ?? [] + const stamp = Date.now().toString().slice(-7) + + for (let i = accounts.length + 1; i <= ACCOUNT_COUNT; i += 1) { + const suffix = `${stamp}${String(i).padStart(2, "0")}` + const phone = `+16509${suffix}` as PhoneNumber + const kratosUserId = randomUUID() as UserId + + const account = throwIfError( + await Accounts.createAccountWithPhoneIdentifier({ + newAccountInfo: { kratosUserId, phone }, + config, + }), + ) + + const { usd, usdt } = await listWalletsForAccount(account.id) + + throwIfError( + await Accounts.updateDefaultWalletId({ + accountId: account.id, + walletId: usd.id, + }), + ) + + const fundingCents = i <= 8 ? 25 : i === 9 ? 1 : 0 + + accounts.push({ + index: i, + phone, + kratosUserId, + accountId: account.id, + accountUuid: account.uuid, + legacyUsdWalletId: usd.id, + destinationUsdtWalletId: usdt.id, + startingFundingCents: fundingCents, + }) + + await logProgress( + `created account ${i}: ${account.id} USD=${usd.id} USDT=${usdt.id} funding=${fundingCents}`, + ) + + await saveManifest({ + cutoverVersion: CUTOVER_VERSION, + runId: RUN_ID, + createdAt: existing?.createdAt ?? new Date().toISOString(), + funderUsdWalletId: funderWalletId, + accounts, + }) + } + + const manifest: Manifest = { + cutoverVersion: CUTOVER_VERSION, + runId: RUN_ID, + createdAt: existing?.createdAt ?? new Date().toISOString(), + funderUsdWalletId: funderWalletId, + accounts, + } + await saveManifest(manifest) + console.log(JSON.stringify(manifest, null, 2)) +} + +const completePartialAccounts = async () => { + const manifest = await loadManifest() + const existingIds = new Set(manifest.accounts.map((account) => account.accountId)) + const partials = await Account.find({ + role: "user", + defaultWalletId: { $exists: false }, + created_at: { $gte: new Date(Date.now() - 60 * 60 * 1000) }, + }).sort({ created_at: 1 }) + + for (const partial of partials) { + if (manifest.accounts.length >= ACCOUNT_COUNT) break + const accountId = partial._id.toString() + if (existingIds.has(accountId)) continue + + const usd = throwIfError( + await WalletsRepository().persistNew({ + accountId: accountId as AccountId, + type: WalletType.Checking, + currency: WalletCurrency.Usd, + }), + ) + const usdt = throwIfError( + await WalletsRepository().persistNew({ + accountId: accountId as AccountId, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }), + ) + throwIfError( + await Accounts.updateDefaultWalletId({ + accountId: accountId as AccountId, + walletId: usd.id, + }), + ) + + const index = manifest.accounts.length + 1 + const fundingCents = index <= 8 ? 25 : index === 9 ? 1 : 0 + manifest.accounts.push({ + index, + phone: `partial-${index}`, + kratosUserId: partial.kratosUserId, + accountId, + accountUuid: partial.id, + legacyUsdWalletId: usd.id, + destinationUsdtWalletId: usdt.id, + startingFundingCents: fundingCents, + }) + existingIds.add(accountId) + await saveManifest(manifest) + await logProgress( + `completed partial account ${index}: ${accountId} USD=${usd.id} USDT=${usdt.id} funding=${fundingCents}`, + ) + } + + console.log(JSON.stringify(manifest, null, 2)) +} + +const fundAccounts = async () => { + const manifest = await loadManifest() + if (!manifest.funderUsdWalletId) { + manifest.funderUsdWalletId = await funderUsdWalletId() + await saveManifest(manifest) + } + + for (const account of manifest.accounts) { + if (account.startingFundingCents === 0) { + await logProgress(`left account ${account.index} unfunded`) + continue + } + + const status = throwIfError( + await Payments.intraledgerPaymentSendWalletIdForUsdWallet({ + senderWalletId: manifest.funderUsdWalletId, + recipientWalletId: account.legacyUsdWalletId, + amount: account.startingFundingCents, + memo: `ENG-345 ${RUN_ID} seed ${account.index}`, + }), + ) + + if (status !== PaymentSendStatus.Success && status !== PaymentSendStatus.Pending) { + throw new Error(`Funding account ${account.index} returned ${status}`) + } + + await logProgress( + `funded account ${account.index} ${account.legacyUsdWalletId} with ${account.startingFundingCents} cents status=${status}`, + ) + } +} + +const scopedAccountsRepo = (targetIds: Set) => ({ + async *listUnlockedAccounts() { + for (const accountId of targetIds) { + yield throwIfError(await AccountsRepository().findById(accountId as AccountId)) + } + }, +}) + +const preview = async () => { + const manifest = await loadManifest() + const result = throwIfError( + await CashWalletCutover.previewPrimaryCashWalletCutover({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + accountsRepo: scopedAccountsRepo(new Set(manifest.accounts.map((a) => a.accountId))), + walletsRepo: WalletsRepository(), + }), + ) + console.log(JSON.stringify(result, null, 2)) + await logProgress(`preview planned=${result.plannedMigrations.length}`) +} + +const prepare = async () => { + const manifest = await loadManifest() + const result = throwIfError( + await CashWalletCutover.preparePrimaryCashWalletCutover({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + accountsRepo: scopedAccountsRepo(new Set(manifest.accounts.map((a) => a.accountId))), + walletsRepo: WalletsRepository(), + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + console.log(JSON.stringify(result, null, 2)) + await logProgress(`prepared migrations=${result.migrations.length}`) +} + +const start = async () => { + const manifest = await loadManifest() + const result = throwIfError( + await CashWalletCutover.startPrimaryCashWalletCutover({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + actor: "manual-local", + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + console.log(JSON.stringify(result, null, 2)) + await logProgress(`started state=${result.state}`) +} + +const runBatches = async () => { + const manifest = await loadManifest() + const batches = [] + + for (let i = 1; i <= 40; i += 1) { + const result = throwIfError( + await CashWalletCutover.runPrimaryCashWalletCutoverBatch({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + workerId: "manual-local", + limit: ACCOUNT_COUNT, + lockStaleBefore: new Date(Date.now() - 300_000), + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + batches.push(result) + await logProgress(`batch ${i}: ${JSON.stringify(result)}`) + + const status = throwIfError( + await CashWalletCutover.getPrimaryCashWalletCutoverStatus({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + + if (result.failed > 0) { + console.log(JSON.stringify({ batches, status }, null, 2)) + throw new Error(`Batch ${i} failed`) + } + + if (Object.keys(status.countsByStatus).length === 1 && status.countsByStatus.complete) { + console.log(JSON.stringify({ batches, status }, null, 2)) + await logProgress(`all migrations complete after batch ${i}`) + return + } + + if (result.attempted === 0) { + console.log(JSON.stringify({ batches, status }, null, 2)) + throw new Error("No runnable migrations, but run is not complete") + } + } + + throw new Error("Exceeded maximum batch count") +} + +const complete = async () => { + const manifest = await loadManifest() + const result = throwIfError( + await CashWalletCutover.completePrimaryCashWalletCutover({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + actor: "manual-local", + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + console.log(JSON.stringify(result, null, 2)) + await logProgress(`completed lifecycle state=${result.state}`) +} + +const status = async () => { + const manifest = await loadManifest() + const result = throwIfError( + await CashWalletCutover.getPrimaryCashWalletCutoverStatus({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + console.log(JSON.stringify(result, null, 2)) +} + +const resetConfig = async () => { + const manifest = await loadManifest() + const result = await CashWalletCutoverConfig.updateOne( + { _id: "cash_wallet_cutover" }, + { + $set: { + state: "pre", + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + updatedBy: "manual-local", + updatedAt: new Date(), + }, + $unset: { + scheduledAt: "", + startedAt: "", + completedAt: "", + pausedAt: "", + pauseReason: "", + }, + }, + { upsert: true }, + ) + console.log(JSON.stringify(result, null, 2)) + await logProgress(`reset singleton cutover config to pre for ${manifest.runId}`) +} + +const verify = async () => { + const manifest = await loadManifest() + const rows = [] + + for (const target of manifest.accounts) { + const account = throwIfError( + await AccountsRepository().findById(target.accountId as AccountId), + ) + const usdBalance = throwIfError( + await getBalanceForWallet({ + walletId: target.legacyUsdWalletId as WalletId, + currency: WalletCurrency.Usd, + }), + ) + const usdtBalance = throwIfError( + await getBalanceForWallet({ + walletId: target.destinationUsdtWalletId as WalletId, + currency: WalletCurrency.Usdt, + }), + ) + + rows.push({ + index: target.index, + accountId: target.accountId, + expectedFundingCents: target.startingFundingCents, + defaultWalletId: account.defaultWalletId, + defaultIsDestinationUsdt: account.defaultWalletId === target.destinationUsdtWalletId, + legacyUsdWalletId: target.legacyUsdWalletId, + destinationUsdtWalletId: target.destinationUsdtWalletId, + legacyUsdBalanceCents: usdBalance.asCents(), + destinationUsdtBalanceMicros: usdtBalance.asSmallestUnits(), + }) + } + + const statusResult = throwIfError( + await CashWalletCutover.getPrimaryCashWalletCutoverStatus({ + cutoverVersion: manifest.cutoverVersion, + runId: manifest.runId, + migrationsRepo: CashWalletCutoverRepository(), + }), + ) + + const result = { status: statusResult, accounts: rows } + await fs.writeFile(RESULTS_PATH, `${JSON.stringify(result, null, 2)}\n`) + console.log(JSON.stringify(result, null, 2)) + await logProgress(`wrote verification results to ${RESULTS_PATH}`) +} + +const commands: Record Promise> = { + "create-accounts": createAccounts, + "complete-partials": completePartialAccounts, + fund: fundAccounts, + preview, + prepare, + start, + "run-batches": runBatches, + complete, + status, + "reset-config": resetConfig, + verify, +} + +setupMongoConnection() + .then(async (mongoose) => { + const command = process.argv.find((arg) => commands[arg]) + if (!command || !commands[command]) { + throw new Error(`Expected command: ${Object.keys(commands).join(", ")}`) + } + + await commands[command]() + await mongoose?.connection.close() + process.exit(0) + }) + .catch(async (error) => { + await logProgress(`ERROR ${error instanceof Error ? error.message : String(error)}`) + console.error(error) + process.exit(1) + }) diff --git a/operator-runs/eng-345-manual-347/task_plan.md b/operator-runs/eng-345-manual-347/task_plan.md new file mode 100644 index 000000000..a7240b9cf --- /dev/null +++ b/operator-runs/eng-345-manual-347/task_plan.md @@ -0,0 +1,70 @@ +# Task Plan: ENG-345 Fresh 7-Account Manual Cutover + +## Goal +Run the full cash-wallet cutover pipeline on the 7 fresh local accounts that were successfully created with legacy USD default wallets. + +## Current Phase +Phase 6 + +## Phases + +### Phase 1: Discovery +- [x] Find existing account creation/funding helpers +- [x] Confirm database and service config for the local run +- [x] Document reusable commands +- **Status:** complete + +### Phase 2: Test Data Setup +- [x] Create 7 new accounts +- [x] Capture account IDs and USD/USDT wallet IDs for 7 complete accounts +- [x] Set 7 complete accounts' `defaultWalletId` to legacy USD wallet ID +- [x] Leave all 7 legacy USD wallets at zero balance +- **Status:** complete + +### Phase 3: Cutover Pipeline +- [x] Preview run +- [x] Prepare run +- [x] Start run +- [x] Run batches until all migrations reach terminal state or failure +- [x] Complete lifecycle if all migrations complete +- **Status:** complete + +### Phase 4: Verification +- [x] Verify migration counts +- [x] Verify account default wallet pointers changed to USDT +- [x] Verify source/destination amounts for zero-balance accounts +- [x] Document any failures and recovery actions +- **Status:** complete + +### Phase 5: Report +- [x] Summarize setup, commands, and final status +- [x] Update session memory +- **Status:** complete + +### Phase 6: Reset and Prep Funded Rerun +- [x] Verify seven funded legacy USD balances +- [x] Reset seven account `defaultWalletId` values to legacy USD wallets +- [x] Create fresh cutover prep run for the same seven accounts +- [x] Verify migration prep count and account pointers +- **Status:** complete + +## Key Parameters +- Worktree: `/Users/dread/Documents/Island-Bitcoin/Flash/flash/.worktrees/eng-345-review` +- Planned cutoverVersion: `347` +- Planned runId: `manual-eng-347` +- Account count: `7` +- Funding: `7 x $0.00` + +## Decisions Made +| Decision | Rationale | +|----------|-----------| +| Use a fresh run/version instead of rewinding manual-eng-346 | manual-eng-346 already moved funds and completed; fresh run gives clean manual-test evidence | +| Convert test to 7 zero-balance accounts | Dread requested changing the plan to a 7-account cutover after IBEX write failures blocked creating/funding 10 accounts | +| Use `cutoverVersion=348`, `runId=manual-eng-348` for the funded rerun prep | Version 347 already completed as the zero-balance cutover, so a new run keeps evidence separated | + +## Errors Encountered +| Error | Attempt | Resolution | +|-------|---------|------------| +| Config loader tried to read `create-accounts` as a YAML file; `Account` model import was undefined | 1 | Patched operator script to locate command anywhere in argv and import `Account` from `@services/mongoose/schema`; rerun with command before `--configPath` | +| IBEX fetch error while creating the 8th account left 7 complete accounts and one partial account without wallets/default | 2 | Reconstructed a manifest for the 7 known-good accounts, excluded the partial account, and patched script to save/resume manifest incrementally | +| IBEX write path continued returning blank `FetchError` after cooldown for partial wallet creation and funding invoice creation | 3 | Stopped rather than running an incomplete/fabricated 10-account cutover; verified reads still work and documented resume state | diff --git a/operator-runs/eng-345-manual-347/verify-348-prep-results.json b/operator-runs/eng-345-manual-347/verify-348-prep-results.json new file mode 100644 index 000000000..cb35272d1 --- /dev/null +++ b/operator-runs/eng-345-manual-347/verify-348-prep-results.json @@ -0,0 +1,74 @@ +{ + "cutoverVersion": 348, + "runId": "manual-eng-348", + "status": { + "config": { + "state": "pre", + "updatedBy": "manual-local", + "cutoverVersion": 348, + "runId": "manual-eng-348", + "updatedAt": "2026-05-23T14:49:36.246Z" + }, + "countsByStatus": { + "not_started": 7 + } + }, + "accounts": [ + { + "index": 1, + "accountId": "6a11ada7e55310755eeb0257", + "defaultWalletId": "0a4d1c55-d8ec-4685-8457-216d41569d61", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 2, + "accountId": "6a11ada8e55310755eeb0271", + "defaultWalletId": "08ee5f13-b4c3-4ff5-835a-55dd786d3887", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 3, + "accountId": "6a11ada9e55310755eeb028b", + "defaultWalletId": "697b8583-6e1d-47d5-aba0-8b2c8aa6bc32", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 4, + "accountId": "6a11ada9e55310755eeb02a5", + "defaultWalletId": "dbdfba0c-ead0-4c94-96f8-e15130b7f796", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 5, + "accountId": "6a11adabe55310755eeb02bf", + "defaultWalletId": "9756a443-d30d-4b1c-a797-933ec2ced1d0", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 6, + "accountId": "6a11adace55310755eeb02d9", + "defaultWalletId": "ecaf103c-d024-419a-8bad-b0c61e8068be", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + }, + { + "index": 7, + "accountId": "6a11adade55310755eeb02f3", + "defaultWalletId": "8a3e1bd4-4d2d-4f97-b201-d58d3723a53c", + "defaultIsLegacyUsd": true, + "legacyUsdBalanceCents": "25", + "destinationUsdtBalanceMicros": "0" + } + ] +} diff --git a/src/app/cash-wallet-cutover/amount-conversion.ts b/src/app/cash-wallet-cutover/amount-conversion.ts new file mode 100644 index 000000000..fd6bd4e09 --- /dev/null +++ b/src/app/cash-wallet-cutover/amount-conversion.ts @@ -0,0 +1,54 @@ +import { InvalidCashWalletCutoverAmountError } from "./errors" + +const USDT_MICROS_PER_USD_CENT = 10_000n + +const parseNonNegativeInteger = ( + value: string, +): bigint | InvalidCashWalletCutoverAmountError => { + if (!/^\d+$/.test(value)) { + return new InvalidCashWalletCutoverAmountError( + `Invalid non-negative integer amount: ${value}`, + ) + } + return BigInt(value) +} + +export const usdCentsToUsdtMicros = ( + usdCents: string, +): string | InvalidCashWalletCutoverAmountError => { + const parsed = parseNonNegativeInteger(usdCents) + if (parsed instanceof Error) return parsed + return (parsed * USDT_MICROS_PER_USD_CENT).toString() +} + +export const feeUsdCentsToUsdtMicros = usdCentsToUsdtMicros + +export const usdtMicrosToUsdCentsCeil = ( + usdtMicros: string, +): string | InvalidCashWalletCutoverAmountError => { + const parsed = parseNonNegativeInteger(usdtMicros) + if (parsed instanceof Error) return parsed + if (parsed === 0n) return "0" + return ((parsed + USDT_MICROS_PER_USD_CENT - 1n) / USDT_MICROS_PER_USD_CENT).toString() +} + +export const destinationShortfallUsdtMicros = ({ + targetUsdtMicros, + startingUsdtMicros, + currentUsdtMicros, +}: { + targetUsdtMicros: string + startingUsdtMicros: string + currentUsdtMicros: string +}): string | InvalidCashWalletCutoverAmountError => { + const target = parseNonNegativeInteger(targetUsdtMicros) + if (target instanceof Error) return target + const starting = parseNonNegativeInteger(startingUsdtMicros) + if (starting instanceof Error) return starting + const current = parseNonNegativeInteger(currentUsdtMicros) + if (current instanceof Error) return current + + const received = current > starting ? current - starting : 0n + if (received >= target) return "0" + return (target - received).toString() +} diff --git a/src/app/cash-wallet-cutover/client-capability.ts b/src/app/cash-wallet-cutover/client-capability.ts new file mode 100644 index 000000000..e30b95fb5 --- /dev/null +++ b/src/app/cash-wallet-cutover/client-capability.ts @@ -0,0 +1,37 @@ +export const CASH_WALLET_USDT_CLIENT_CAPABILITY = "cash-wallet-usdt-v1" + +export type CashWalletPresentation = "legacy_compat" | "usdt" + +export type CashWalletClientCapabilities = { + cashWalletPresentation: CashWalletPresentation + hasUsdtCashWalletSupport: boolean +} + +export const DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES: CashWalletClientCapabilities = { + cashWalletPresentation: "legacy_compat", + hasUsdtCashWalletSupport: false, +} + +export const parseCashWalletClientCapabilities = ( + headers: Record, +): CashWalletClientCapabilities => { + const values = Object.entries(headers).flatMap(([key, raw]) => { + if (key.toLowerCase() !== "x-flash-client-capabilities") return [] + if (typeof raw === "string") return [raw] + if (Array.isArray(raw)) + return raw.filter((value): value is string => typeof value === "string") + return [] + }) + + const capabilities = values + .flatMap((value) => value.split(",")) + .map((value) => value.trim().toLowerCase()) + + const hasUsdtCashWalletSupport = capabilities.includes( + CASH_WALLET_USDT_CLIENT_CAPABILITY, + ) + + if (!hasUsdtCashWalletSupport) return DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES + + return { cashWalletPresentation: "usdt", hasUsdtCashWalletSupport } +} diff --git a/src/app/cash-wallet-cutover/discovery.ts b/src/app/cash-wallet-cutover/discovery.ts new file mode 100644 index 000000000..14304c10b --- /dev/null +++ b/src/app/cash-wallet-cutover/discovery.ts @@ -0,0 +1,78 @@ +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +export type CashWalletCutoverDiscoveryStatus = + | "legacy_default" + | "already_usdt" + | "residual_legacy_usd" + | "missing_legacy_usd" + | "missing_destination_usdt" + +export type CashWalletCutoverDiscovery = { + status: CashWalletCutoverDiscoveryStatus + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId?: WalletId + destinationUsdtWalletId?: WalletId + previousDefaultWalletId: WalletId +} + +export const classifyCashWalletsForCutover = ({ + account, + wallets, +}: { + account: Account + wallets: Wallet[] +}): CashWalletCutoverDiscovery => { + const legacyUsdWallet = wallets.find( + (wallet) => + wallet.type === WalletType.Checking && wallet.currency === WalletCurrency.Usd, + ) + const destinationUsdtWallet = wallets.find( + (wallet) => + wallet.type === WalletType.Checking && wallet.currency === WalletCurrency.Usdt, + ) + + const base = { + accountId: account.id, + accountUuid: account.uuid, + legacyUsdWalletId: legacyUsdWallet?.id, + destinationUsdtWalletId: destinationUsdtWallet?.id, + previousDefaultWalletId: account.defaultWalletId, + } + + if (!legacyUsdWallet) return { ...base, status: "missing_legacy_usd" } + if (!destinationUsdtWallet) return { ...base, status: "missing_destination_usdt" } + + if (account.defaultWalletId === legacyUsdWallet.id) { + return { ...base, status: "legacy_default" } + } + + if (account.defaultWalletId === destinationUsdtWallet.id) { + return { ...base, status: "already_usdt" } + } + + return { ...base, status: "residual_legacy_usd" } +} + +export const discoverCashWalletCutoverAccounts = async ({ + accountsRepo, + walletsRepo, +}: { + accountsRepo: Pick + walletsRepo: Pick +}): Promise => { + const accounts = accountsRepo.listUnlockedAccounts() + if (accounts instanceof Error) return accounts + + const discoveries: CashWalletCutoverDiscovery[] = [] + + for await (const account of accounts) { + const wallets = await walletsRepo.listByAccountId(account.id) + if (wallets instanceof Error) return wallets + + discoveries.push(classifyCashWalletsForCutover({ account, wallets })) + } + + return discoveries +} diff --git a/src/app/cash-wallet-cutover/errors.ts b/src/app/cash-wallet-cutover/errors.ts new file mode 100644 index 000000000..373dcd742 --- /dev/null +++ b/src/app/cash-wallet-cutover/errors.ts @@ -0,0 +1,11 @@ +import { DomainError, ValidationError } from "@domain/shared" + +export class InvalidCashWalletCutoverAmountError extends ValidationError {} +export class InvalidCashWalletMigrationTransitionError extends ValidationError {} +export class InvalidCashWalletCutoverStateTransitionError extends ValidationError {} +export class CashWalletCutoverInProgressError extends ValidationError {} +export class CashWalletMigrationFailedError extends DomainError {} +export class CashWalletMissingLegacyUsdWalletError extends DomainError {} +export class CashWalletMissingUsdtWalletError extends DomainError {} +export class CashWalletCutoverPreflightError extends DomainError {} +export class CashWalletCutoverTreasuryInsufficientBalanceError extends DomainError {} diff --git a/src/app/cash-wallet-cutover/executor.ts b/src/app/cash-wallet-cutover/executor.ts new file mode 100644 index 000000000..5df68aa00 --- /dev/null +++ b/src/app/cash-wallet-cutover/executor.ts @@ -0,0 +1,39 @@ +type RunnableCashWalletMigrationStatus = Exclude< + CashWalletMigrationStatus, + | "complete" + | "failed" + | "requires_operator_review" + | "skipped_already_migrated" + | "rollback_started" + | "rolled_back" +> + +type CashWalletMigrationStepHandler = ( + migration: CashWalletMigration, +) => Promise + +export type CashWalletMigrationStepHandlers = Record< + RunnableCashWalletMigrationStatus, + CashWalletMigrationStepHandler +> + +const terminalStatuses: CashWalletMigrationStatus[] = [ + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rollback_started", + "rolled_back", +] + +export const executeCashWalletMigrationStep = async ({ + migration, + handlers, +}: { + migration: CashWalletMigration + handlers: CashWalletMigrationStepHandlers +}): Promise => { + if (terminalStatuses.includes(migration.status)) return migration + + return handlers[migration.status as RunnableCashWalletMigrationStatus](migration) +} diff --git a/src/app/cash-wallet-cutover/guard.ts b/src/app/cash-wallet-cutover/guard.ts new file mode 100644 index 000000000..214968d4c --- /dev/null +++ b/src/app/cash-wallet-cutover/guard.ts @@ -0,0 +1,77 @@ +import { + CashWalletCutoverInProgressError, + CashWalletMigrationFailedError, +} from "./errors" +import { CashWalletClientCapabilities } from "./client-capability" + +export { CashWalletCutoverInProgressError, CashWalletMigrationFailedError } + +export type CashWalletCutoverRoute = "legacy_usd" | "usdt" +export type CashWalletCutoverPresentation = "legacy_usd" | "legacy_usd_compat" | "usdt" + +export type CashWalletCutoverDecision = { + presentation: CashWalletCutoverPresentation +} + +const ACTIVE_STATUSES: CashWalletMigrationStatus[] = [ + "started", + "provisioned", + "balance_read", + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", + "rollback_started", +] + +export const evaluateCashWalletCutoverGuard = ({ + cutover, + migration, +}: { + cutover: CashWalletCutoverConfig + migration?: CashWalletMigration | null +}): { route: CashWalletCutoverRoute } | ApplicationError => { + if (cutover.state === "pre") return { route: "legacy_usd" } + if (cutover.state === "complete") return { route: "usdt" } + + if (!migration || migration.status === "not_started") return { route: "legacy_usd" } + if ( + migration.status === "complete" || + migration.status === "skipped_already_migrated" + ) { + return { route: "usdt" } + } + if (migration.status === "failed" || migration.status === "requires_operator_review") { + return new CashWalletMigrationFailedError() + } + if (ACTIVE_STATUSES.includes(migration.status)) { + return new CashWalletCutoverInProgressError() + } + + return { route: "legacy_usd" } +} + +export const evaluateCashWalletCutoverPresentation = ({ + cutover, + migration, + client, +}: { + cutover: CashWalletCutoverConfig + migration?: CashWalletMigration | null + client: CashWalletClientCapabilities +}): CashWalletCutoverDecision | ApplicationError => { + const guard = evaluateCashWalletCutoverGuard({ cutover, migration }) + if (guard instanceof Error) return guard + + if (guard.route === "legacy_usd") { + return { presentation: "legacy_usd" } + } + + return { + presentation: client.hasUsdtCashWalletSupport ? "usdt" : "legacy_usd_compat", + } +} diff --git a/src/app/cash-wallet-cutover/handlers.ts b/src/app/cash-wallet-cutover/handlers.ts new file mode 100644 index 000000000..20d32ff3f --- /dev/null +++ b/src/app/cash-wallet-cutover/handlers.ts @@ -0,0 +1,173 @@ +import { + completeCashWalletMigration, + createCashWalletMigrationBalanceMoveInvoice, + createCashWalletMigrationFeeReimbursementInvoice, + flipCashWalletMigrationDefaultPointer, + markCashWalletMigrationBalanceMoveSent, + markCashWalletMigrationFeeReimbursed, + provisionCashWalletMigrationDestination, + recordCashWalletMigrationBalance, + sendCashWalletMigrationBalanceMovePayment, + sendCashWalletMigrationFeeReimbursementPayment, + skipCashWalletMigrationFeeReimbursement, + startCashWalletMigration, + verifyCashWalletMigrationBalanceMove, + verifyCashWalletMigrationLegacyZero, +} from "./worker" +import { CashWalletMigrationStepHandlers } from "./executor" + +type CashWalletMigrationTransitionRepository = Parameters< + typeof startCashWalletMigration +>[0]["migrationsRepo"] + +type CashWalletMigrationHandlerServices = { + now(): Date + provisioningService: Parameters< + typeof provisionCashWalletMigrationDestination + >[0]["provisioningService"] + balanceReader: { + readSourceBalanceUsdCents( + migration: CashWalletMigration, + ): Promise + readDestinationBalanceUsdtMicros( + migration: CashWalletMigration, + ): Promise + } + invoiceService: Parameters< + typeof createCashWalletMigrationBalanceMoveInvoice + >[0]["invoiceService"] & + Parameters[0]["invoiceService"] + paymentService: Parameters< + typeof sendCashWalletMigrationBalanceMovePayment + >[0]["paymentService"] + balanceVerifier: Parameters< + typeof verifyCashWalletMigrationBalanceMove + >[0]["balanceVerifier"] + feeService: { + readFeeAmountUsdtMicros( + migration: CashWalletMigration, + ): Promise + } + treasuryService: { + getTreasuryWalletId(): Promise + } + pointerService: Parameters< + typeof flipCashWalletMigrationDefaultPointer + >[0]["pointerService"] + legacyWalletVerifier: Parameters< + typeof verifyCashWalletMigrationLegacyZero + >[0]["legacyWalletVerifier"] +} + +export const createCashWalletMigrationStepHandlers = ({ + migrationsRepo, + services, +}: { + migrationsRepo: CashWalletMigrationTransitionRepository + services: CashWalletMigrationHandlerServices +}): CashWalletMigrationStepHandlers => ({ + not_started: (migration) => + startCashWalletMigration({ + migration, + migrationsRepo, + startedAt: services.now(), + }), + started: (migration) => + provisionCashWalletMigrationDestination({ + migration, + migrationsRepo, + provisioningService: services.provisioningService, + }), + provisioned: async (migration) => { + const sourceBalanceUsdCents = + await services.balanceReader.readSourceBalanceUsdCents(migration) + if (sourceBalanceUsdCents instanceof Error) return sourceBalanceUsdCents + const destinationStartingBalanceUsdtMicros = + await services.balanceReader.readDestinationBalanceUsdtMicros(migration) + if (destinationStartingBalanceUsdtMicros instanceof Error) { + return destinationStartingBalanceUsdtMicros + } + return recordCashWalletMigrationBalance({ + migration, + migrationsRepo, + sourceBalanceUsdCents, + destinationStartingBalanceUsdtMicros, + }) + }, + balance_read: (migration) => { + if (migration.destinationAmountUsdtMicros === "0") { + return flipCashWalletMigrationDefaultPointer({ + migration, + migrationsRepo, + pointerService: services.pointerService, + }) + } + + return createCashWalletMigrationBalanceMoveInvoice({ + migration, + migrationsRepo, + invoiceService: services.invoiceService, + }) + }, + invoice_created: (migration) => + sendCashWalletMigrationBalanceMovePayment({ + migration, + migrationsRepo, + paymentService: services.paymentService, + }), + balance_move_sending: (migration) => + markCashWalletMigrationBalanceMoveSent({ migration, migrationsRepo }), + balance_move_sent: (migration) => + verifyCashWalletMigrationBalanceMove({ + migration, + migrationsRepo, + balanceVerifier: services.balanceVerifier, + }), + balance_move_verified: async (migration) => { + const feeAmountUsdtMicros = + await services.feeService.readFeeAmountUsdtMicros(migration) + if (feeAmountUsdtMicros instanceof Error) return feeAmountUsdtMicros + if (feeAmountUsdtMicros === "0") { + return skipCashWalletMigrationFeeReimbursement({ + migration, + migrationsRepo, + }) + } + return createCashWalletMigrationFeeReimbursementInvoice({ + migration, + migrationsRepo, + invoiceService: services.invoiceService, + feeAmountUsdtMicros, + }) + }, + fee_reimbursement_invoice_created: async (migration) => { + const treasuryWalletId = await services.treasuryService.getTreasuryWalletId() + if (treasuryWalletId instanceof Error) return treasuryWalletId + return sendCashWalletMigrationFeeReimbursementPayment({ + migration, + migrationsRepo, + paymentService: services.paymentService, + treasuryWalletId, + }) + }, + fee_reimbursement_sending: (migration) => + markCashWalletMigrationFeeReimbursed({ migration, migrationsRepo }), + fee_reimbursed: (migration) => + flipCashWalletMigrationDefaultPointer({ + migration, + migrationsRepo, + pointerService: services.pointerService, + }), + pointer_flipped: (migration) => + verifyCashWalletMigrationLegacyZero({ + migration, + migrationsRepo, + legacyWalletVerifier: services.legacyWalletVerifier, + }), + legacy_zero_verified: (migration) => + completeCashWalletMigration({ + migration, + migrationsRepo, + completedAt: services.now(), + }), +}) diff --git a/src/app/cash-wallet-cutover/index.ts b/src/app/cash-wallet-cutover/index.ts new file mode 100644 index 000000000..eae3f76bd --- /dev/null +++ b/src/app/cash-wallet-cutover/index.ts @@ -0,0 +1,23 @@ +export * from "./amount-conversion" +export * from "./client-capability" +export * from "./errors" +export * from "./presentation" +export * from "./presentation-for-account" +export * from "./state-machine" +export { + evaluateCashWalletCutoverGuard, + evaluateCashWalletCutoverPresentation, +} from "./guard" +export * from "./discovery" +export * from "./preflight" +export * from "./planner" +export * from "./migration-records" +export * from "./prepare" +export * from "./worker" +export * from "./executor" +export * from "./runner" +export * from "./handlers" +export * from "./runtime-services" +export * from "./orchestrator" +export * from "./lifecycle" +export * from "./preview" diff --git a/src/app/cash-wallet-cutover/index.types.d.ts b/src/app/cash-wallet-cutover/index.types.d.ts new file mode 100644 index 000000000..3f16650b7 --- /dev/null +++ b/src/app/cash-wallet-cutover/index.types.d.ts @@ -0,0 +1,67 @@ +type CashWalletCutoverState = "pre" | "in_progress" | "complete" + +type CashWalletMigrationStatus = + | "not_started" + | "started" + | "provisioned" + | "balance_read" + | "invoice_created" + | "balance_move_sending" + | "balance_move_sent" + | "balance_move_verified" + | "fee_reimbursement_invoice_created" + | "fee_reimbursement_sending" + | "fee_reimbursed" + | "pointer_flipped" + | "legacy_zero_verified" + | "complete" + | "failed" + | "requires_operator_review" + | "skipped_already_migrated" + | "rollback_started" + | "rolled_back" + +type CashWalletCutoverConfig = { + state: CashWalletCutoverState + scheduledAt?: Date + startedAt?: Date + completedAt?: Date + pausedAt?: Date + pauseReason?: string + updatedBy?: string + cutoverVersion: number + runId?: string + updatedAt: Date +} + +type CashWalletMigration = { + id: string + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + previousDefaultWalletId?: WalletId + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + sourceBalanceUsdCents?: string + destinationAmountUsdtMicros?: string + destinationStartingBalanceUsdtMicros?: string + feeAmountUsdCents?: string + feeAmountUsdtMicros?: string + balanceMoveInvoicePaymentRequest?: string + balanceMoveInvoicePaymentHash?: string + balanceMovePaymentTransactionId?: string + feeReimbursementInvoicePaymentRequest?: string + feeReimbursementInvoicePaymentHash?: string + feeReimbursementPaymentTransactionId?: string + estimatedFee?: boolean + idempotencyKey: string + attempts: number + lastError?: string + lockedAt?: Date + lockedBy?: string + startedAt?: Date + completedAt?: Date + updatedAt: Date +} diff --git a/src/app/cash-wallet-cutover/lifecycle.ts b/src/app/cash-wallet-cutover/lifecycle.ts new file mode 100644 index 000000000..80e4fd848 --- /dev/null +++ b/src/app/cash-wallet-cutover/lifecycle.ts @@ -0,0 +1,174 @@ +import { CashWalletCutoverRepository } from "@services/mongoose" + +import { + CashWalletCutoverInProgressError, + CashWalletMigrationFailedError, + InvalidCashWalletCutoverStateTransitionError, +} from "./errors" + +const migrationStatuses: CashWalletMigrationStatus[] = [ + "not_started", + "started", + "provisioned", + "balance_read", + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", + "legacy_zero_verified", + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rollback_started", + "rolled_back", +] + +type CashWalletCutoverLifecycleRepository = { + getConfig: () => Promise + updateConfig: ( + patch: Partial, + actor?: string, + ) => Promise + listRunnableMigrations: ({ + cutoverVersion, + runId, + limit, + }: { + cutoverVersion: number + runId: string + limit?: number + }) => Promise + countByStatus: ({ + cutoverVersion, + runId, + status, + }: { + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + }) => Promise +} + +export type CashWalletCutoverStatusReport = { + config: CashWalletCutoverConfig + countsByStatus: Partial> +} + +export const startPrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + actor, + now = new Date(), + migrationsRepo = CashWalletCutoverRepository(), +}: { + cutoverVersion: number + runId: string + actor: string + now?: Date + migrationsRepo?: CashWalletCutoverLifecycleRepository +}): Promise => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) return config + + if (config.state === "complete") { + return new InvalidCashWalletCutoverStateTransitionError( + "Cash wallet cutover is already complete", + ) + } + + if (config.state === "in_progress") { + if (config.runId === runId && config.cutoverVersion === cutoverVersion) return config + return new CashWalletCutoverInProgressError( + "Cash wallet cutover is already in progress", + ) + } + + return migrationsRepo.updateConfig( + { + state: "in_progress", + cutoverVersion, + runId, + startedAt: now, + scheduledAt: undefined, + pausedAt: undefined, + pauseReason: undefined, + }, + actor, + ) +} + +export const completePrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + actor, + now = new Date(), + migrationsRepo = CashWalletCutoverRepository(), +}: { + cutoverVersion: number + runId: string + actor: string + now?: Date + migrationsRepo?: CashWalletCutoverLifecycleRepository +}): Promise => { + const failedCount = await migrationsRepo.countByStatus({ + cutoverVersion, + runId, + status: "failed", + }) + if (failedCount instanceof Error) return failedCount + if (failedCount > 0) return new CashWalletMigrationFailedError() + + const reviewCount = await migrationsRepo.countByStatus({ + cutoverVersion, + runId, + status: "requires_operator_review", + }) + if (reviewCount instanceof Error) return reviewCount + if (reviewCount > 0) return new CashWalletMigrationFailedError() + + const runnable = await migrationsRepo.listRunnableMigrations({ + cutoverVersion, + runId, + limit: 1, + }) + if (runnable instanceof Error) return runnable + if (runnable.length > 0) return new CashWalletCutoverInProgressError() + + return migrationsRepo.updateConfig( + { + state: "complete", + cutoverVersion, + runId, + completedAt: now, + }, + actor, + ) +} + +export const getPrimaryCashWalletCutoverStatus = async ({ + cutoverVersion, + runId, + migrationsRepo = CashWalletCutoverRepository(), +}: { + cutoverVersion: number + runId: string + migrationsRepo?: CashWalletCutoverLifecycleRepository +}): Promise => { + const config = await migrationsRepo.getConfig() + if (config instanceof Error) return config + + const countsByStatus: Partial> = {} + + for (const status of migrationStatuses) { + const count = await migrationsRepo.countByStatus({ cutoverVersion, runId, status }) + if (count instanceof Error) return count + if (count > 0) countsByStatus[status] = count + } + + return { config, countsByStatus } +} diff --git a/src/app/cash-wallet-cutover/migration-records.ts b/src/app/cash-wallet-cutover/migration-records.ts new file mode 100644 index 000000000..9ac33b8d0 --- /dev/null +++ b/src/app/cash-wallet-cutover/migration-records.ts @@ -0,0 +1,28 @@ +import { PrimaryCashWalletMigrationPlan } from "./planner" + +type CashWalletMigrationRecordsRepository = { + upsertMigration( + args: PrimaryCashWalletMigrationPlan, + ): Promise +} + +export type { CashWalletMigrationRecordsRepository } + +export const upsertPrimaryCashWalletMigrationRecords = async ({ + migrationsRepo, + plans, +}: { + migrationsRepo: CashWalletMigrationRecordsRepository + plans: PrimaryCashWalletMigrationPlan[] +}): Promise => { + const migrations: CashWalletMigration[] = [] + + for (const plan of plans) { + const migration = await migrationsRepo.upsertMigration(plan) + if (migration instanceof Error) return migration + + migrations.push(migration) + } + + return migrations +} diff --git a/src/app/cash-wallet-cutover/orchestrator.ts b/src/app/cash-wallet-cutover/orchestrator.ts new file mode 100644 index 000000000..89d8203c6 --- /dev/null +++ b/src/app/cash-wallet-cutover/orchestrator.ts @@ -0,0 +1,52 @@ +import { CashWalletCutoverRepository } from "@services/mongoose" + +import { createCashWalletMigrationStepHandlers } from "./handlers" +import { createCashWalletMigrationRuntimeServices } from "./runtime-services" +import { executeCashWalletMigrationStep } from "./executor" +import { runCashWalletMigrationBatch } from "./runner" + +type PrimaryCashWalletCutoverBatchRepository = Parameters< + typeof runCashWalletMigrationBatch +>[0]["migrationsRepo"] & + Parameters[0]["migrationsRepo"] + +type PrimaryCashWalletCutoverRuntimeServices = Parameters< + typeof createCashWalletMigrationStepHandlers +>[0]["services"] + +export const runPrimaryCashWalletCutoverBatch = ({ + cutoverVersion, + runId, + workerId, + limit, + lockStaleBefore, + migrationsRepo = CashWalletCutoverRepository(), + runtimeServices = createCashWalletMigrationRuntimeServices(), +}: { + cutoverVersion: number + runId: string + workerId: string + limit?: number + lockStaleBefore: Date + migrationsRepo?: PrimaryCashWalletCutoverBatchRepository + runtimeServices?: PrimaryCashWalletCutoverRuntimeServices +}) => { + const handlers = createCashWalletMigrationStepHandlers({ + migrationsRepo, + services: runtimeServices, + }) + + return runCashWalletMigrationBatch({ + cutoverVersion, + runId, + workerId, + limit, + lockStaleBefore, + migrationsRepo, + executor: (migration) => + executeCashWalletMigrationStep({ + migration, + handlers, + }), + }) +} diff --git a/src/app/cash-wallet-cutover/planner.ts b/src/app/cash-wallet-cutover/planner.ts new file mode 100644 index 000000000..0a1e2031c --- /dev/null +++ b/src/app/cash-wallet-cutover/planner.ts @@ -0,0 +1,41 @@ +import { CashWalletCutoverDiscovery } from "./discovery" + +type PrimaryCashWalletMigrationPlan = { + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + previousDefaultWalletId: WalletId + cutoverVersion: number + runId: string + idempotencyKey: string +} + +export type { PrimaryCashWalletMigrationPlan } + +export const buildPrimaryCashWalletMigrationPlan = ({ + cutoverVersion, + runId, + discoveries, +}: { + cutoverVersion: number + runId: string + discoveries: CashWalletCutoverDiscovery[] +}): PrimaryCashWalletMigrationPlan[] => + discoveries.flatMap((discovery) => { + if (discovery.status !== "legacy_default") return [] + if (!discovery.legacyUsdWalletId || !discovery.destinationUsdtWalletId) return [] + + return [ + { + accountId: discovery.accountId, + accountUuid: discovery.accountUuid, + legacyUsdWalletId: discovery.legacyUsdWalletId, + destinationUsdtWalletId: discovery.destinationUsdtWalletId, + previousDefaultWalletId: discovery.previousDefaultWalletId, + cutoverVersion, + runId, + idempotencyKey: `cash-wallet-cutover:${runId}:${discovery.accountId}`, + }, + ] + }) diff --git a/src/app/cash-wallet-cutover/preflight.ts b/src/app/cash-wallet-cutover/preflight.ts new file mode 100644 index 000000000..df05bfddf --- /dev/null +++ b/src/app/cash-wallet-cutover/preflight.ts @@ -0,0 +1,53 @@ +import { CashWalletCutoverDiscovery } from "./discovery" + +type CashWalletCutoverPreflightBlocker = { + accountId: AccountId + reason: "missing_legacy_usd" | "missing_destination_usdt" +} + +type CashWalletCutoverPreflightReport = { + cutoverVersion: number + runId: string + totalAccounts: number + migrationCandidates: number + alreadyUsdt: number + residualLegacyUsd: number + blockers: number + blockerAccounts: CashWalletCutoverPreflightBlocker[] + canStart: boolean +} + +export type { CashWalletCutoverPreflightReport } + +export const buildCashWalletCutoverPreflightReport = ({ + cutoverVersion, + runId, + discoveries, +}: { + cutoverVersion: number + runId: string + discoveries: CashWalletCutoverDiscovery[] +}): CashWalletCutoverPreflightReport => { + const blockerAccounts = discoveries.flatMap(({ accountId, status }) => { + if (status !== "missing_legacy_usd" && status !== "missing_destination_usdt") { + return [] + } + + return [{ accountId, reason: status }] + }) + + return { + cutoverVersion, + runId, + totalAccounts: discoveries.length, + migrationCandidates: discoveries.filter(({ status }) => status === "legacy_default") + .length, + alreadyUsdt: discoveries.filter(({ status }) => status === "already_usdt").length, + residualLegacyUsd: discoveries.filter( + ({ status }) => status === "residual_legacy_usd", + ).length, + blockers: blockerAccounts.length, + blockerAccounts, + canStart: blockerAccounts.length === 0, + } +} diff --git a/src/app/cash-wallet-cutover/prepare.ts b/src/app/cash-wallet-cutover/prepare.ts new file mode 100644 index 000000000..35793b208 --- /dev/null +++ b/src/app/cash-wallet-cutover/prepare.ts @@ -0,0 +1,63 @@ +import { + buildCashWalletCutoverPreflightReport, + CashWalletCutoverPreflightReport, +} from "./preflight" +import { discoverCashWalletCutoverAccounts } from "./discovery" +import { + buildPrimaryCashWalletMigrationPlan, + PrimaryCashWalletMigrationPlan, +} from "./planner" +import { + upsertPrimaryCashWalletMigrationRecords, + CashWalletMigrationRecordsRepository, +} from "./migration-records" + +type PreparePrimaryCashWalletCutoverResult = { + report: CashWalletCutoverPreflightReport + plannedMigrations: PrimaryCashWalletMigrationPlan[] + migrations: CashWalletMigration[] +} + +export const preparePrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + accountsRepo, + walletsRepo, + migrationsRepo, +}: { + cutoverVersion: number + runId: string + accountsRepo: Pick + walletsRepo: Pick + migrationsRepo: CashWalletMigrationRecordsRepository +}): Promise => { + const discoveries = await discoverCashWalletCutoverAccounts({ + accountsRepo, + walletsRepo, + }) + if (discoveries instanceof Error) return discoveries + + const report = buildCashWalletCutoverPreflightReport({ + cutoverVersion, + runId, + discoveries, + }) + + if (!report.canStart) { + return { report, plannedMigrations: [], migrations: [] } + } + + const plannedMigrations = buildPrimaryCashWalletMigrationPlan({ + cutoverVersion, + runId, + discoveries, + }) + + const migrations = await upsertPrimaryCashWalletMigrationRecords({ + migrationsRepo, + plans: plannedMigrations, + }) + if (migrations instanceof Error) return migrations + + return { report, plannedMigrations, migrations } +} diff --git a/src/app/cash-wallet-cutover/presentation-for-account.ts b/src/app/cash-wallet-cutover/presentation-for-account.ts new file mode 100644 index 000000000..53483ea19 --- /dev/null +++ b/src/app/cash-wallet-cutover/presentation-for-account.ts @@ -0,0 +1,94 @@ +import { WalletsRepository, CashWalletCutoverRepository } from "@services/mongoose" + +import { CashWalletClientCapabilities } from "./client-capability" +import { CashWalletCutoverPreflightError } from "./errors" +import { evaluateCashWalletCutoverPresentation } from "./guard" +import { + CashWalletPresentationResult, + resolveCashWalletPresentation, +} from "./presentation" + +type CashWalletPresentationMigrationsRepository = { + getConfig: () => Promise + findMigrationByAccountId: ({ + accountId, + cutoverVersion, + runId, + }: { + accountId: AccountId + cutoverVersion: number + runId: string + }) => Promise +} + +type CashWalletPresentationWalletsRepository = { + listByAccountId: (accountId: AccountId) => Promise +} + +export const resolveCashWalletPresentationForAccount = async ({ + account, + client, + migrationsRepo = CashWalletCutoverRepository(), + walletsRepo = WalletsRepository(), +}: { + account: Account + client: CashWalletClientCapabilities + migrationsRepo?: CashWalletPresentationMigrationsRepository + walletsRepo?: CashWalletPresentationWalletsRepository +}): Promise => { + const cutover = await migrationsRepo.getConfig() + if (cutover instanceof Error) return cutover + + let migration: CashWalletMigration | null | undefined + if (cutover.state === "in_progress") { + if (!cutover.runId) return new CashWalletCutoverPreflightError() + + const foundMigration = await migrationsRepo.findMigrationByAccountId({ + accountId: account.id, + cutoverVersion: cutover.cutoverVersion, + runId: cutover.runId, + }) + if (foundMigration instanceof Error) return foundMigration + migration = foundMigration + } + + const decision = evaluateCashWalletCutoverPresentation({ + cutover, + migration, + client, + }) + if (decision instanceof Error) return decision + + const wallets = await walletsRepo.listByAccountId(account.id) + if (wallets instanceof Error) return wallets + + return resolveCashWalletPresentation({ decision, wallets }) +} + +export const resolveCashWalletMutationWalletIdForAccount = async ({ + account, + walletId, + client, + migrationsRepo, + walletsRepo, +}: { + account: Account + walletId: WalletId + client: CashWalletClientCapabilities + migrationsRepo?: CashWalletPresentationMigrationsRepository + walletsRepo?: CashWalletPresentationWalletsRepository +}): Promise => { + const presentation = await resolveCashWalletPresentationForAccount({ + account, + client, + migrationsRepo, + walletsRepo, + }) + if (presentation instanceof Error) return presentation + + if (walletId === presentation.legacyUsdWallet?.id) { + return presentation.activeSettlementWallet.id + } + + return walletId +} diff --git a/src/app/cash-wallet-cutover/presentation.ts b/src/app/cash-wallet-cutover/presentation.ts new file mode 100644 index 000000000..7c41478a8 --- /dev/null +++ b/src/app/cash-wallet-cutover/presentation.ts @@ -0,0 +1,82 @@ +import { WalletCurrency } from "@domain/shared" + +import { CashWalletCutoverDecision } from "./guard" +import { + CashWalletMissingLegacyUsdWalletError, + CashWalletMissingUsdtWalletError, +} from "./errors" + +export type CashWalletPresentationResult = { + wallets: Wallet[] + defaultWalletId: WalletId + legacyUsdWallet?: Wallet + activeSettlementWallet: Wallet +} + +export const resolveCashWalletPresentation = ({ + decision, + wallets, +}: { + decision: CashWalletCutoverDecision + wallets: Wallet[] +}): CashWalletPresentationResult | ApplicationError => { + const legacyUsdWallet = wallets.find((wallet) => wallet.currency === WalletCurrency.Usd) + const usdtWallet = wallets.find((wallet) => wallet.currency === WalletCurrency.Usdt) + const nonCashWallets = wallets.filter( + (wallet) => + wallet.currency !== WalletCurrency.Usd && wallet.currency !== WalletCurrency.Usdt, + ) + + if (decision.presentation === "usdt") { + if (!usdtWallet) return new CashWalletMissingUsdtWalletError() + + return { + wallets: [...nonCashWallets, usdtWallet], + defaultWalletId: usdtWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + } + } + + if (!legacyUsdWallet) return new CashWalletMissingLegacyUsdWalletError() + + if (decision.presentation === "legacy_usd_compat") { + if (!usdtWallet) return new CashWalletMissingUsdtWalletError() + + return { + wallets: [...nonCashWallets, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + } + } + + return { + wallets: [...nonCashWallets, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: legacyUsdWallet, + } +} + +export const cashWalletTransactionWalletIdsForPresentation = ({ + walletIds, + presentation, +}: { + walletIds?: WalletId[] + presentation: CashWalletPresentationResult +}): WalletId[] => { + const selectedWalletIds = walletIds ?? presentation.wallets.map((wallet) => wallet.id) + + if (!presentation.legacyUsdWallet) return selectedWalletIds + + return Array.from( + new Set( + selectedWalletIds.map((walletId) => + walletId === presentation.legacyUsdWallet?.id + ? presentation.activeSettlementWallet.id + : walletId, + ), + ), + ) +} diff --git a/src/app/cash-wallet-cutover/preview.ts b/src/app/cash-wallet-cutover/preview.ts new file mode 100644 index 000000000..2cca59bc9 --- /dev/null +++ b/src/app/cash-wallet-cutover/preview.ts @@ -0,0 +1,54 @@ +import { AccountsRepository, WalletsRepository } from "@services/mongoose" + +import { discoverCashWalletCutoverAccounts } from "./discovery" +import { + buildCashWalletCutoverPreflightReport, + CashWalletCutoverPreflightReport, +} from "./preflight" +import { + buildPrimaryCashWalletMigrationPlan, + PrimaryCashWalletMigrationPlan, +} from "./planner" + +export const previewPrimaryCashWalletCutover = async ({ + cutoverVersion, + runId, + accountsRepo = AccountsRepository(), + walletsRepo = WalletsRepository(), +}: { + cutoverVersion: number + runId: string + accountsRepo?: Pick + walletsRepo?: Pick +}): Promise< + | { + report: CashWalletCutoverPreflightReport + plannedMigrations: PrimaryCashWalletMigrationPlan[] + } + | RepositoryError +> => { + const discoveries = await discoverCashWalletCutoverAccounts({ + accountsRepo, + walletsRepo, + }) + if (discoveries instanceof Error) return discoveries + + const report = buildCashWalletCutoverPreflightReport({ + cutoverVersion, + runId, + discoveries, + }) + + if (!report.canStart) { + return { report, plannedMigrations: [] } + } + + return { + report, + plannedMigrations: buildPrimaryCashWalletMigrationPlan({ + cutoverVersion, + runId, + discoveries, + }), + } +} diff --git a/src/app/cash-wallet-cutover/runner.ts b/src/app/cash-wallet-cutover/runner.ts new file mode 100644 index 000000000..c937496f0 --- /dev/null +++ b/src/app/cash-wallet-cutover/runner.ts @@ -0,0 +1,129 @@ +type CashWalletMigrationBatchRepository = { + listRunnableMigrations(args: { + cutoverVersion: number + runId: string + limit?: number + }): Promise + acquireMigrationLock(args: { + id: string + workerId: string + staleBefore: Date + cutoverVersion: number + runId: string + }): Promise + releaseMigrationLock(args: { + id: string + workerId: string + cutoverVersion: number + runId: string + }): Promise + markMigrationFailed(args: { + id: string + workerId: string + cutoverVersion: number + runId: string + error: Error + status: "failed" | "requires_operator_review" + }): Promise +} + +type CashWalletMigrationBatchExecutor = ( + migration: CashWalletMigration, +) => Promise + +type CashWalletMigrationBatchResult = { + attempted: number + advanced: number + failed: number + skipped: number +} + +const AMBIGUOUS_SIDE_EFFECT_STATUSES: CashWalletMigrationStatus[] = [ + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", +] + +const failureStatusForMigration = ( + status: CashWalletMigrationStatus, +): "failed" | "requires_operator_review" => + AMBIGUOUS_SIDE_EFFECT_STATUSES.includes(status) ? "requires_operator_review" : "failed" + +export const runCashWalletMigrationBatch = async ({ + cutoverVersion, + runId, + workerId, + limit, + lockStaleBefore, + migrationsRepo, + executor, +}: { + cutoverVersion: number + runId: string + workerId: string + limit?: number + lockStaleBefore: Date + migrationsRepo: CashWalletMigrationBatchRepository + executor: CashWalletMigrationBatchExecutor +}): Promise => { + const migrations = await migrationsRepo.listRunnableMigrations({ + cutoverVersion, + runId, + limit, + }) + if (migrations instanceof Error) return migrations + + const result: CashWalletMigrationBatchResult = { + attempted: 0, + advanced: 0, + failed: 0, + skipped: 0, + } + + for (const migration of migrations) { + result.attempted += 1 + + const locked = await migrationsRepo.acquireMigrationLock({ + id: migration.id, + workerId, + staleBefore: lockStaleBefore, + cutoverVersion, + runId, + }) + if (locked instanceof Error) { + result.skipped += 1 + continue + } + + const step = await executor(locked) + if (step instanceof Error) { + result.failed += 1 + const marked = await migrationsRepo.markMigrationFailed({ + id: locked.id, + workerId, + cutoverVersion, + runId, + error: step, + status: failureStatusForMigration(locked.status), + }) + if (marked instanceof Error) return marked + continue + } else { + result.advanced += 1 + } + + await migrationsRepo.releaseMigrationLock({ + id: locked.id, + workerId, + cutoverVersion, + runId, + }) + } + + return result +} diff --git a/src/app/cash-wallet-cutover/runtime-services.ts b/src/app/cash-wallet-cutover/runtime-services.ts new file mode 100644 index 000000000..776149ba5 --- /dev/null +++ b/src/app/cash-wallet-cutover/runtime-services.ts @@ -0,0 +1,266 @@ +import { addWalletIfNonexistent, updateDefaultWalletId } from "@app/accounts" +import { + addInvoiceForRecipientForUsdWallet, + getBalanceForWallet, +} from "@app/wallets" +import { decodeInvoice } from "@domain/bitcoin/lightning" +import { InvalidWalletId } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" +import { AccountsRepository } from "@services/mongoose" +import Ibex from "@services/ibex/client" +import { UnexpectedIbexResponse } from "@services/ibex/errors" +import { getFunderWalletId } from "@services/ledger/caching" + +import { + CashWalletMigrationFailedError, + InvalidCashWalletCutoverAmountError, + InvalidCashWalletMigrationTransitionError, +} from "./errors" +import { destinationShortfallUsdtMicros } from "./amount-conversion" + +const CUTOVER_IBEX_INVOICE_EXPIRATION_SECONDS = 15 * 60 + +type RuntimeServiceDependencies = { + now?: () => Date + addWalletIfNonexistent?: typeof addWalletIfNonexistent + updateDefaultWalletId?: typeof updateDefaultWalletId + getBalanceForWallet?: typeof getBalanceForWallet + createInvoice?: typeof addInvoiceForRecipientForUsdWallet + createNoAmountInvoice?: typeof Ibex.addInvoice + payInvoice?: typeof Ibex.payInvoice + accountsRepo?: Pick, "findById"> + getTreasuryWalletId?: () => Promise +} + +const isUsdAmount = (amount: unknown): amount is USDAmount => amount instanceof USDAmount +const isUsdtAmount = (amount: unknown): amount is USDTAmount => + amount instanceof USDTAmount + +const ibexInvoiceToDomainInvoice = (response: Awaited>) => { + if (response instanceof Error) return response + + const invoiceString = response.invoice?.bolt11 + if (!invoiceString) return new UnexpectedIbexResponse("Could not find invoice.") + + const decodedInvoice = decodeInvoice(invoiceString) + if (decodedInvoice instanceof Error) return decodedInvoice + + return decodedInvoice +} + +export const createCashWalletMigrationRuntimeServices = ( + deps: RuntimeServiceDependencies = {}, +) => { + const addWallet = deps.addWalletIfNonexistent ?? addWalletIfNonexistent + const updateDefaultWallet = deps.updateDefaultWalletId ?? updateDefaultWalletId + const balanceForWallet = deps.getBalanceForWallet ?? getBalanceForWallet + const invoiceForRecipient = deps.createInvoice ?? addInvoiceForRecipientForUsdWallet + const noAmountInvoiceForRecipient = deps.createNoAmountInvoice ?? Ibex.addInvoice + const payInvoice = deps.payInvoice ?? Ibex.payInvoice + const accountsRepo = deps.accountsRepo ?? AccountsRepository() + + return { + now: deps.now ?? (() => new Date()), + provisioningService: { + ensureDestinationWallet: async ({ + accountId, + destinationUsdtWalletId, + }: { + accountId: AccountId + destinationUsdtWalletId: WalletId + }): Promise => { + const wallet = await addWallet({ + accountId, + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + if (wallet instanceof Error) return wallet + if (wallet.id !== destinationUsdtWalletId) return new InvalidWalletId() + return true + }, + }, + balanceReader: { + readSourceBalanceUsdCents: async ( + migration: CashWalletMigration, + ): Promise => { + const balance = await balanceForWallet({ + walletId: migration.legacyUsdWalletId, + currency: WalletCurrency.Usd, + }) + if (balance instanceof Error) return balance + if (!isUsdAmount(balance)) { + return new InvalidCashWalletCutoverAmountError("Expected USD balance") + } + return balance.asCents() + }, + readDestinationBalanceUsdtMicros: async ( + migration: CashWalletMigration, + ): Promise => { + const balance = await balanceForWallet({ + walletId: migration.destinationUsdtWalletId, + currency: WalletCurrency.Usdt, + }) + if (balance instanceof Error) return balance + if (!isUsdtAmount(balance)) { + return new InvalidCashWalletCutoverAmountError("Expected USDT balance") + } + return balance.asSmallestUnits() + }, + }, + invoiceService: { + createInvoice: ({ + recipientWalletId, + amount, + memo, + }: { + recipientWalletId: WalletId + amount: string + memo: string + }) => + invoiceForRecipient({ + recipientWalletId, + amount: amount as FractionalCentAmount, + memo, + }), + createNoAmountInvoice: ({ + recipientWalletId, + memo, + }: { + recipientWalletId: WalletId + memo: string + }) => + noAmountInvoiceForRecipient({ + accountId: recipientWalletId, + memo, + expiration: CUTOVER_IBEX_INVOICE_EXPIRATION_SECONDS as Seconds, + }).then(ibexInvoiceToDomainInvoice), + }, + paymentService: { + payInvoice: async ({ + senderWalletId, + paymentRequest, + senderAmountUsdCents, + }: { + senderWalletId: WalletId + paymentRequest: string + senderAmountUsdCents?: string + }): Promise<{ transactionId: IbexTransactionId } | ApplicationError> => { + const send = + senderAmountUsdCents === undefined + ? undefined + : USDAmount.cents(senderAmountUsdCents) + if (send instanceof Error) return send + + const payment = await payInvoice({ + accountId: senderWalletId as IbexAccountId, + invoice: paymentRequest as Bolt11, + send, + }) + if (payment instanceof Error) return payment + + const transactionId = payment.transaction?.id + if (!transactionId) { + return new UnexpectedIbexResponse("Payment transaction id not found") + } + return { transactionId: transactionId as IbexTransactionId } + }, + }, + balanceVerifier: { + verifyBalanceMove: async ({ + legacyUsdWalletId, + }: { + legacyUsdWalletId: WalletId + }): Promise => { + const balance = await balanceForWallet({ + walletId: legacyUsdWalletId, + currency: WalletCurrency.Usd, + }) + if (balance instanceof Error) return balance + if (!isUsdAmount(balance) || !balance.isZero()) { + return new CashWalletMigrationFailedError("Legacy USD wallet is not zero") + } + return true + }, + }, + feeService: { + readFeeAmountUsdtMicros: async ( + migration: CashWalletMigration, + ): Promise => { + if (migration.balanceMovePaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMovePaymentTransactionId is required before reading fee amount", + ) + } + + if (migration.destinationAmountUsdtMicros === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "destinationAmountUsdtMicros is required before reading fee amount", + ) + } + + if (migration.destinationStartingBalanceUsdtMicros === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "destinationStartingBalanceUsdtMicros is required before reading fee amount", + ) + } + + const currentBalance = await balanceForWallet({ + walletId: migration.destinationUsdtWalletId, + currency: WalletCurrency.Usdt, + }) + if (currentBalance instanceof Error) return currentBalance + if (!isUsdtAmount(currentBalance)) { + return new InvalidCashWalletCutoverAmountError("Expected USDT balance") + } + + return destinationShortfallUsdtMicros({ + targetUsdtMicros: migration.destinationAmountUsdtMicros, + startingUsdtMicros: migration.destinationStartingBalanceUsdtMicros, + currentUsdtMicros: currentBalance.asSmallestUnits(), + }) + }, + }, + treasuryService: { + getTreasuryWalletId: deps.getTreasuryWalletId ?? getFunderWalletId, + }, + pointerService: { + flipDefaultWallet: async ({ + accountId, + destinationWalletId, + }: { + accountId: AccountId + destinationWalletId: WalletId + }): Promise<{ previousDefaultWalletId: WalletId } | ApplicationError> => { + const account = await accountsRepo.findById(accountId) + if (account instanceof Error) return account + + const previousDefaultWalletId = account.defaultWalletId + const updated = await updateDefaultWallet({ + accountId, + walletId: destinationWalletId, + }) + if (updated instanceof Error) return updated + + return { previousDefaultWalletId } + }, + }, + legacyWalletVerifier: { + verifyLegacyWalletZero: async ({ + legacyUsdWalletId, + }: { + legacyUsdWalletId: WalletId + }): Promise => { + const balance = await balanceForWallet({ + walletId: legacyUsdWalletId, + currency: WalletCurrency.Usd, + }) + if (balance instanceof Error) return balance + if (!isUsdAmount(balance) || !balance.isZero()) { + return new CashWalletMigrationFailedError("Legacy USD wallet is not zero") + } + return true + }, + }, + } +} diff --git a/src/app/cash-wallet-cutover/state-machine.ts b/src/app/cash-wallet-cutover/state-machine.ts new file mode 100644 index 000000000..6bd152b8d --- /dev/null +++ b/src/app/cash-wallet-cutover/state-machine.ts @@ -0,0 +1,43 @@ +import { InvalidCashWalletMigrationTransitionError } from "./errors" + +const transitions: Partial< + Record +> = { + not_started: ["started"], + started: ["provisioned", "failed"], + provisioned: ["balance_read", "failed", "skipped_already_migrated"], + balance_read: ["invoice_created", "pointer_flipped", "failed"], + invoice_created: ["balance_move_sending", "failed", "requires_operator_review"], + balance_move_sending: ["balance_move_sent", "failed", "requires_operator_review"], + balance_move_sent: ["balance_move_verified", "failed", "requires_operator_review"], + balance_move_verified: [ + "fee_reimbursement_invoice_created", + "fee_reimbursed", + "failed", + "requires_operator_review", + ], + fee_reimbursement_invoice_created: [ + "fee_reimbursement_sending", + "failed", + "requires_operator_review", + ], + fee_reimbursement_sending: ["fee_reimbursed", "failed", "requires_operator_review"], + fee_reimbursed: ["pointer_flipped", "failed"], + pointer_flipped: ["legacy_zero_verified", "failed"], + legacy_zero_verified: ["complete", "failed"], + rollback_started: ["rolled_back", "failed"], +} + +export const assertCanTransition = ( + from: CashWalletMigrationStatus, + to: CashWalletMigrationStatus, +): true | InvalidCashWalletMigrationTransitionError => { + if (transitions[from]?.includes(to)) return true + return new InvalidCashWalletMigrationTransitionError( + `Invalid migration transition: ${from} -> ${to}`, + ) +} + +export const nextResumeStatus = ( + status: CashWalletMigrationStatus, +): CashWalletMigrationStatus => status diff --git a/src/app/cash-wallet-cutover/worker.ts b/src/app/cash-wallet-cutover/worker.ts new file mode 100644 index 000000000..67b26250f --- /dev/null +++ b/src/app/cash-wallet-cutover/worker.ts @@ -0,0 +1,497 @@ +import { assertCanTransition } from "./state-machine" +import { usdCentsToUsdtMicros, usdtMicrosToUsdCentsCeil } from "./amount-conversion" +import { + InvalidCashWalletCutoverAmountError, + InvalidCashWalletMigrationTransitionError, +} from "./errors" + +type CashWalletMigrationTransitionRepository = { + transitionMigration(args: { + id: string + from: CashWalletMigrationStatus + to: CashWalletMigrationStatus + cutoverVersion: number + runId: string + patch?: Partial + }): Promise +} + +type CashWalletMigrationInvoiceService = { + createInvoice(args: { + recipientWalletId: WalletId + amount: string + memo: string + }): Promise +} + +type CashWalletMigrationNoAmountInvoiceService = { + createNoAmountInvoice(args: { + recipientWalletId: WalletId + memo: string + }): Promise +} + +type CashWalletMigrationPaymentService = { + payInvoice(args: { + senderWalletId: WalletId + paymentRequest: string + senderAmountUsdCents?: string + }): Promise<{ transactionId: IbexTransactionId } | ApplicationError> +} + +type CashWalletMigrationBalanceVerifier = { + verifyBalanceMove(args: { + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + sourceBalanceUsdCents?: string + destinationAmountUsdtMicros?: string + transactionId: IbexTransactionId + }): Promise +} + +type CashWalletMigrationPointerService = { + flipDefaultWallet(args: { + accountId: AccountId + destinationWalletId: WalletId + }): Promise<{ previousDefaultWalletId: WalletId } | ApplicationError> +} + +type CashWalletMigrationLegacyWalletVerifier = { + verifyLegacyWalletZero(args: { + legacyUsdWalletId: WalletId + }): Promise +} + +type CashWalletMigrationProvisioningService = { + ensureDestinationWallet(args: { + accountId: AccountId + destinationUsdtWalletId: WalletId + }): Promise +} + +export const startCashWalletMigration = async ({ + migration, + migrationsRepo, + startedAt, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository + startedAt: Date +}): Promise => { + const transition = assertCanTransition(migration.status, "started") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "started", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { startedAt }, + }) +} + +export const provisionCashWalletMigrationDestination = async ({ + migration, + provisioningService, + migrationsRepo, +}: { + migration: CashWalletMigration + provisioningService: CashWalletMigrationProvisioningService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "provisioned") + if (transition instanceof Error) return transition + + const provisioned = await provisioningService.ensureDestinationWallet({ + accountId: migration.accountId, + destinationUsdtWalletId: migration.destinationUsdtWalletId, + }) + if (provisioned instanceof Error) return provisioned + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "provisioned", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const recordCashWalletMigrationBalance = async ({ + migration, + migrationsRepo, + sourceBalanceUsdCents, + destinationStartingBalanceUsdtMicros, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository + sourceBalanceUsdCents: string + destinationStartingBalanceUsdtMicros: string +}): Promise => { + const destinationAmountUsdtMicros = usdCentsToUsdtMicros(sourceBalanceUsdCents) + if (destinationAmountUsdtMicros instanceof Error) return destinationAmountUsdtMicros + + const transition = assertCanTransition(migration.status, "balance_read") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_read", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + sourceBalanceUsdCents, + destinationAmountUsdtMicros, + destinationStartingBalanceUsdtMicros, + }, + }) +} + +export const sendCashWalletMigrationBalanceMovePayment = async ({ + migration, + paymentService, + migrationsRepo, +}: { + migration: CashWalletMigration + paymentService: CashWalletMigrationPaymentService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "balance_move_sending") + if (transition instanceof Error) return transition + + if (migration.balanceMoveInvoicePaymentRequest === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMoveInvoicePaymentRequest is required before balance move payment sending", + ) + } + + if (migration.sourceBalanceUsdCents === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "sourceBalanceUsdCents is required before balance move payment sending", + ) + } + + const payment = await paymentService.payInvoice({ + senderWalletId: migration.legacyUsdWalletId, + paymentRequest: migration.balanceMoveInvoicePaymentRequest, + senderAmountUsdCents: migration.sourceBalanceUsdCents, + }) + if (payment instanceof Error) return payment + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_move_sending", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + balanceMovePaymentTransactionId: payment.transactionId, + }, + }) +} + +export const markCashWalletMigrationBalanceMoveSent = async ({ + migration, + migrationsRepo, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "balance_move_sent") + if (transition instanceof Error) return transition + + if (migration.balanceMovePaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMovePaymentTransactionId is required before marking balance move sent", + ) + } + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_move_sent", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const verifyCashWalletMigrationBalanceMove = async ({ + migration, + balanceVerifier, + migrationsRepo, +}: { + migration: CashWalletMigration + balanceVerifier: CashWalletMigrationBalanceVerifier + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "balance_move_verified") + if (transition instanceof Error) return transition + + if (migration.balanceMovePaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "balanceMovePaymentTransactionId is required before verifying balance move", + ) + } + + const verified = await balanceVerifier.verifyBalanceMove({ + legacyUsdWalletId: migration.legacyUsdWalletId, + destinationUsdtWalletId: migration.destinationUsdtWalletId, + sourceBalanceUsdCents: migration.sourceBalanceUsdCents, + destinationAmountUsdtMicros: migration.destinationAmountUsdtMicros, + transactionId: migration.balanceMovePaymentTransactionId as IbexTransactionId, + }) + if (verified instanceof Error) return verified + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "balance_move_verified", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const createCashWalletMigrationBalanceMoveInvoice = async ({ + migration, + invoiceService, + migrationsRepo, +}: { + migration: CashWalletMigration + invoiceService: CashWalletMigrationNoAmountInvoiceService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "invoice_created") + if (transition instanceof Error) return transition + + if (migration.destinationAmountUsdtMicros === undefined) { + return new InvalidCashWalletCutoverAmountError( + "destinationAmountUsdtMicros is required before balance move invoice creation", + ) + } + + const invoice = await invoiceService.createNoAmountInvoice({ + recipientWalletId: migration.destinationUsdtWalletId, + memo: `cash-wallet-cutover:${migration.runId}:${migration.id}:balance-move`, + }) + if (invoice instanceof Error) return invoice + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "invoice_created", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + balanceMoveInvoicePaymentRequest: invoice.paymentRequest, + balanceMoveInvoicePaymentHash: invoice.paymentHash, + }, + }) +} + +export const createCashWalletMigrationFeeReimbursementInvoice = async ({ + migration, + invoiceService, + migrationsRepo, + feeAmountUsdtMicros, +}: { + migration: CashWalletMigration + invoiceService: CashWalletMigrationInvoiceService + migrationsRepo: CashWalletMigrationTransitionRepository + feeAmountUsdtMicros: string +}): Promise => { + const feeAmountUsdCents = usdtMicrosToUsdCentsCeil(feeAmountUsdtMicros) + if (feeAmountUsdCents instanceof Error) return feeAmountUsdCents + + const transition = assertCanTransition( + migration.status, + "fee_reimbursement_invoice_created", + ) + if (transition instanceof Error) return transition + + const invoice = await invoiceService.createInvoice({ + recipientWalletId: migration.destinationUsdtWalletId, + amount: feeAmountUsdtMicros, + memo: `cash-wallet-cutover:${migration.runId}:${migration.id}:fee-reimbursement`, + }) + if (invoice instanceof Error) return invoice + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursement_invoice_created", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + feeAmountUsdCents, + feeAmountUsdtMicros, + feeReimbursementInvoicePaymentRequest: invoice.paymentRequest, + feeReimbursementInvoicePaymentHash: invoice.paymentHash, + }, + }) +} + +export const skipCashWalletMigrationFeeReimbursement = async ({ + migration, + migrationsRepo, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "fee_reimbursed") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursed", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + feeAmountUsdCents: "0", + feeAmountUsdtMicros: "0", + }, + }) +} + +export const sendCashWalletMigrationFeeReimbursementPayment = async ({ + migration, + treasuryWalletId, + paymentService, + migrationsRepo, +}: { + migration: CashWalletMigration + treasuryWalletId: WalletId + paymentService: CashWalletMigrationPaymentService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "fee_reimbursement_sending") + if (transition instanceof Error) return transition + + if (migration.feeReimbursementInvoicePaymentRequest === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "feeReimbursementInvoicePaymentRequest is required before fee reimbursement sending", + ) + } + + const payment = await paymentService.payInvoice({ + senderWalletId: treasuryWalletId, + paymentRequest: migration.feeReimbursementInvoicePaymentRequest, + }) + if (payment instanceof Error) return payment + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursement_sending", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + feeReimbursementPaymentTransactionId: payment.transactionId, + }, + }) +} + +export const markCashWalletMigrationFeeReimbursed = async ({ + migration, + migrationsRepo, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "fee_reimbursed") + if (transition instanceof Error) return transition + + if (migration.feeReimbursementPaymentTransactionId === undefined) { + return new InvalidCashWalletMigrationTransitionError( + "feeReimbursementPaymentTransactionId is required before marking fee reimbursed", + ) + } + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "fee_reimbursed", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const flipCashWalletMigrationDefaultPointer = async ({ + migration, + pointerService, + migrationsRepo, +}: { + migration: CashWalletMigration + pointerService: CashWalletMigrationPointerService + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "pointer_flipped") + if (transition instanceof Error) return transition + + const pointer = await pointerService.flipDefaultWallet({ + accountId: migration.accountId, + destinationWalletId: migration.destinationUsdtWalletId, + }) + if (pointer instanceof Error) return pointer + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "pointer_flipped", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { + previousDefaultWalletId: pointer.previousDefaultWalletId, + }, + }) +} + +export const verifyCashWalletMigrationLegacyZero = async ({ + migration, + legacyWalletVerifier, + migrationsRepo, +}: { + migration: CashWalletMigration + legacyWalletVerifier: CashWalletMigrationLegacyWalletVerifier + migrationsRepo: CashWalletMigrationTransitionRepository +}): Promise => { + const transition = assertCanTransition(migration.status, "legacy_zero_verified") + if (transition instanceof Error) return transition + + const verified = await legacyWalletVerifier.verifyLegacyWalletZero({ + legacyUsdWalletId: migration.legacyUsdWalletId, + }) + if (verified instanceof Error) return verified + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "legacy_zero_verified", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + }) +} + +export const completeCashWalletMigration = async ({ + migration, + migrationsRepo, + completedAt, +}: { + migration: CashWalletMigration + migrationsRepo: CashWalletMigrationTransitionRepository + completedAt: Date +}): Promise => { + const transition = assertCanTransition(migration.status, "complete") + if (transition instanceof Error) return transition + + return migrationsRepo.transitionMigration({ + id: migration.id, + from: migration.status, + to: "complete", + cutoverVersion: migration.cutoverVersion, + runId: migration.runId, + patch: { completedAt }, + }) +} diff --git a/src/app/errors.ts b/src/app/errors.ts index 6e747daf6..4740230f4 100644 --- a/src/app/errors.ts +++ b/src/app/errors.ts @@ -19,6 +19,7 @@ import * as PubSubErrors from "@domain/pubsub/errors" import * as CaptchaErrors from "@domain/captcha/errors" import * as AuthenticationErrors from "@domain/authentication/errors" import * as UserErrors from "@domain/users/errors" +import * as CashWalletCutoverErrors from "@app/cash-wallet-cutover/errors" import * as LedgerFacadeErrors from "@services/ledger/domain/errors" import * as KratosErrors from "@services/kratos/errors" @@ -51,6 +52,7 @@ export const ApplicationErrors = { ...CaptchaErrors, ...AuthenticationErrors, ...UserErrors, + ...CashWalletCutoverErrors, ...KratosErrors, ...LedgerFacadeErrors, diff --git a/src/app/index.ts b/src/app/index.ts index b006e8446..46afb0463 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -14,6 +14,7 @@ import * as WalletsMod from "./wallets" import * as PaymentsMod from "./payments" import * as MerchantsMod from "./merchants" import * as SwapMod from "./swap" +import * as CashWalletCutoverMod from "./cash-wallet-cutover" const allFunctions = { Accounts: { ...AccountsMod }, @@ -30,6 +31,7 @@ const allFunctions = { Payments: { ...PaymentsMod }, Merchants: { ...MerchantsMod }, Swap: { ...SwapMod }, + CashWalletCutover: { ...CashWalletCutoverMod }, } as const let subModule: keyof typeof allFunctions @@ -60,4 +62,5 @@ export const { Payments, Merchants, Swap, + CashWalletCutover, } = allFunctions diff --git a/src/graphql/admin/mutations.ts b/src/graphql/admin/mutations.ts index 070a0d7c7..fcf3f6ce1 100644 --- a/src/graphql/admin/mutations.ts +++ b/src/graphql/admin/mutations.ts @@ -3,6 +3,7 @@ import { GT } from "@graphql/index" import AccountUpdateLevelMutation from "@graphql/admin/root/mutation/account-update-level" import AccountUpdateStatusMutation from "@graphql/admin/root/mutation/account-update-status" import BusinessUpdateMapInfoMutation from "@graphql/admin/root/mutation/business-update-map-info" +import CashWalletCutoverUpdateMutation from "@graphql/admin/root/mutation/cash-wallet-cutover-update" import UserUpdatePhoneMutation from "./root/mutation/user-update-phone" import BusinessDeleteMapInfoMutation from "./root/mutation/delete-business-map" @@ -13,8 +14,7 @@ import MerchantMapDeleteMutation from "./root/mutation/merchant-map-delete" import MerchantMapValidateMutation from "./root/mutation/merchant-map-validate" export const mutationFields = { - unauthed: { - }, + unauthed: {}, authed: { userUpdatePhone: UserUpdatePhoneMutation, accountUpdateLevel: AccountUpdateLevelMutation, @@ -25,6 +25,7 @@ export const mutationFields = { businessDeleteMapInfo: BusinessDeleteMapInfoMutation, sendNotification: SendNotificationMutation, cashoutNotificationSend: sendCashoutSettledNotification, + cashWalletCutoverUpdate: CashWalletCutoverUpdateMutation, }, } diff --git a/src/graphql/admin/queries.ts b/src/graphql/admin/queries.ts index a069657df..deafdfd9f 100644 --- a/src/graphql/admin/queries.ts +++ b/src/graphql/admin/queries.ts @@ -1,4 +1,5 @@ import { GT } from "@graphql/index" +import CashWalletCutoverQuery from "@graphql/shared/root/query/cash-wallet-cutover" import AllLevelsQuery from "./root/query/all-levels" import LightningInvoiceQuery from "./root/query/lightning-invoice" @@ -36,6 +37,7 @@ export const queryFields = { idDocumentReadUrl: IdDocumentReadUrlQuery, notificationTopics: NotificationTopicsQuery, bridgeReconciliationOrphans: BridgeReconciliationOrphansQuery, + cashWalletCutover: CashWalletCutoverQuery, }, } diff --git a/src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts b/src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts new file mode 100644 index 000000000..ff2df55e8 --- /dev/null +++ b/src/graphql/admin/root/mutation/cash-wallet-cutover-update.ts @@ -0,0 +1,61 @@ +import { GT } from "@graphql/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import CashWalletCutoverPayload from "@graphql/admin/types/payload/cash-wallet-cutover" +import CashWalletCutoverState from "@graphql/shared/types/scalar/cash-wallet-cutover-state" +import Timestamp from "@graphql/shared/types/scalar/timestamp" +import { CashWalletCutoverRepository } from "@services/mongoose/cash-wallet-cutover" + +const CashWalletCutoverUpdateInput = GT.Input({ + name: "CashWalletCutoverUpdateInput", + fields: () => ({ + state: { type: GT.NonNull(CashWalletCutoverState) }, + scheduledAt: { type: Timestamp }, + cutoverVersion: { type: GT.Int }, + runId: { type: GT.String }, + pauseReason: { type: GT.String }, + }), +}) + +const CashWalletCutoverUpdateMutation = GT.Field< + null, + GraphQLAdminContext, + { + input: { + state: CashWalletCutoverState | Error + scheduledAt?: Date | Error + cutoverVersion?: number + runId?: string + pauseReason?: string + } + } +>({ + type: GT.NonNull(CashWalletCutoverPayload), + args: { + input: { type: GT.NonNull(CashWalletCutoverUpdateInput) }, + }, + resolve: async (_, { input }, ctx) => { + if (input.state instanceof Error) { + return { errors: [{ message: input.state.message }] } + } + if (input.scheduledAt instanceof Error) { + return { errors: [{ message: input.scheduledAt.message }] } + } + + const patch: Partial = { + state: input.state, + scheduledAt: input.scheduledAt, + cutoverVersion: input.cutoverVersion, + runId: input.runId, + pauseReason: input.pauseReason, + } + + const result = await CashWalletCutoverRepository().updateConfig(patch, ctx.user.id) + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { errors: [], cashWalletCutover: result } + }, +}) + +export default CashWalletCutoverUpdateMutation diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index 87eed84b2..46da132ac 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -136,6 +136,38 @@ input BusinessUpdateMapInfoInput { username: Username! } +type CashWalletCutover { + completedAt: Timestamp + cutoverVersion: Int! + pauseReason: String + pausedAt: Timestamp + runId: String + scheduledAt: Timestamp + startedAt: Timestamp + state: CashWalletCutoverState! + updatedAt: Timestamp! + updatedBy: String +} + +type CashWalletCutoverPayload { + cashWalletCutover: CashWalletCutover + errors: [Error!]! +} + +enum CashWalletCutoverState { + COMPLETE + IN_PROGRESS + PRE +} + +input CashWalletCutoverUpdateInput { + cutoverVersion: Int + pauseReason: String + runId: String + scheduledAt: Timestamp + state: CashWalletCutoverState! +} + input CashoutNotificationSendInput { accountId: String! amount: Int! @@ -278,6 +310,7 @@ type Mutation { accountUpdateStatus(input: AccountUpdateStatusInput!): AccountDetailPayload! businessDeleteMapInfo(input: BusinessDeleteMapInfoInput!): AccountDetailPayload! businessUpdateMapInfo(input: BusinessUpdateMapInfoInput!): AccountDetailPayload! + cashWalletCutoverUpdate(input: CashWalletCutoverUpdateInput!): CashWalletCutoverPayload! cashoutNotificationSend(input: CashoutNotificationSendInput!): SuccessPayload! merchantMapDelete(input: MerchantMapDeleteInput!): MerchantPayload! merchantMapValidate(input: MerchantMapValidateInput!): MerchantPayload! @@ -335,6 +368,7 @@ type Query { accountDetailsByUsername(username: Username!): AuditedAccount! allLevels: [AccountLevel!]! bridgeReconciliationOrphans(limit: Int = 50, orphanType: String = null, status: String = null): [BridgeReconciliationOrphan!]! + cashWalletCutover: CashWalletCutover! idDocumentReadUrl( """Storage key of the ID document file""" fileKey: String! diff --git a/src/graphql/admin/types/payload/cash-wallet-cutover.ts b/src/graphql/admin/types/payload/cash-wallet-cutover.ts new file mode 100644 index 000000000..5f209898c --- /dev/null +++ b/src/graphql/admin/types/payload/cash-wallet-cutover.ts @@ -0,0 +1,13 @@ +import { GT } from "@graphql/index" +import IError from "@graphql/shared/types/abstract/error" +import CashWalletCutoverObject from "@graphql/shared/types/object/cash-wallet-cutover" + +const CashWalletCutoverPayload = GT.Object({ + name: "CashWalletCutoverPayload", + fields: () => ({ + errors: { type: GT.NonNullList(IError) }, + cashWalletCutover: { type: CashWalletCutoverObject }, + }), +}) + +export default CashWalletCutoverPayload diff --git a/src/graphql/error-map.ts b/src/graphql/error-map.ts index 9f93e8875..480711d87 100644 --- a/src/graphql/error-map.ts +++ b/src/graphql/error-map.ts @@ -792,6 +792,42 @@ export const mapError = (error: ApplicationError): CustomApolloError => { case "InvalidLnurlError": return new InvalidLnurlError({ message: error.message, logger: baseLogger }) + case "CashWalletCutoverInProgressError": + message = "Cash Wallet cutover is in progress. Please try again shortly." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletMigrationFailedError": + message = "Cash Wallet migration needs support review." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletCutoverPreflightError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletCutoverTreasuryInsufficientBalanceError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletMissingLegacyUsdWalletError": + message = "Legacy USD Cash Wallet is missing for this account." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "CashWalletMissingUsdtWalletError": + message = "USDT Cash Wallet is missing for this account." + return new ValidationInternalError({ message, logger: baseLogger }) + + case "InvalidCashWalletCutoverAmountError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "InvalidCashWalletMigrationTransitionError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + + case "InvalidCashWalletCutoverStateTransitionError": + message = error.message + return new ValidationInternalError({ message, logger: baseLogger }) + case "UnknownCaptchaError": message = `Unknown error occurred (code: ${error.name}${ error.message ? ": " + error.message : "" diff --git a/src/graphql/public/queries.ts b/src/graphql/public/queries.ts index 8f8e7f46f..7a401d97e 100644 --- a/src/graphql/public/queries.ts +++ b/src/graphql/public/queries.ts @@ -1,4 +1,5 @@ import { GT } from "@graphql/index" +import CashWalletCutoverQuery from "@graphql/shared/root/query/cash-wallet-cutover" import MeQuery from "@graphql/public/root/query/me" import GlobalsQuery from "@graphql/public/root/query/globals" @@ -44,6 +45,7 @@ export const queryFields = { npubByUsername: NpubByUserNameQuery, isFlashNpub: IsFlashNpubQuery, supportedBanks: SupportedBanksQuery, + cashWalletCutover: CashWalletCutoverQuery, }, authed: { atAccountLevel: { diff --git a/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts b/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts index 7c5b4dd2a..cf663acc1 100644 --- a/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts +++ b/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts @@ -1,4 +1,5 @@ import { Payments } from "@app" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import { checkedToWalletId } from "@domain/wallets" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import { GT } from "@graphql/index" @@ -9,7 +10,6 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import dedent from "dedent" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" // import { RequestInit, Response } from 'node-fetch' -import { EmailService } from "@services/email" const IntraLedgerUsdPaymentSendInput = GT.Input({ name: "IntraLedgerUsdPaymentSendInput", @@ -34,7 +34,11 @@ const IntraLedgerUsdPaymentSendMutation = GT.Field { + resolve: async ( + _, + args, + { domainAccount, cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { const { walletId, recipientWalletId, amount, memo } = args.input for (const input of [walletId, recipientWalletId, amount, memo]) { if (input instanceof Error) { @@ -52,11 +56,23 @@ const IntraLedgerUsdPaymentSendMutation = GT.Field({ extensions: { complexity: 120, }, @@ -30,7 +31,7 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ args: { input: { type: GT.NonNull(LnNoAmountUsdInvoiceFeeProbeInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, paymentRequest, amount } = args.input for (const input of [walletId, paymentRequest, amount]) { @@ -39,6 +40,15 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ } } + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, + walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedWalletId)] } + } + // FLASH FORK: create IBEX fee estimation instead of Galoy fee estimation // const { result: feeSatAmount, error } = // await Payments.getNoAmountLightningFeeEstimationForUsdWallet({ @@ -49,17 +59,19 @@ const LnNoAmountUsdInvoiceFeeProbeMutation = GT.Field({ // TODO: Move Ibex call to Payments interface const checkedAmount = await usdWalletAmountFromWalletId({ - walletId, + walletId: routedWalletId, amount: amount.toString(), }) if (checkedAmount instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(checkedAmount)] } } - const resp: IbexFeeEstimation | IbexError = await Ibex.getLnFeeEstimation({ - invoice: paymentRequest as Bolt11, - send: checkedAmount, - }) - if (resp instanceof IbexError) return { errors: [mapAndParseErrorForGqlResponse(resp)] } + const resp: IbexFeeEstimation | IbexError = + await Ibex.getLnFeeEstimation({ + invoice: paymentRequest as Bolt11, + send: checkedAmount, + }) + if (resp instanceof IbexError) + return { errors: [mapAndParseErrorForGqlResponse(resp)] } // if (resp.amount === undefined) return new UnexpectedIbexResponse("Unable to parse fee.") // const feeSatAmount: PaymentAmount = { diff --git a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts index e8d8d6227..12298ec34 100644 --- a/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts +++ b/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts @@ -14,6 +14,7 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { usdWalletAmountFromWalletId } from "@app/wallets" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import Ibex from "@services/ibex/client" import { IbexError } from "@services/ibex/errors" @@ -63,7 +64,7 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< args: { input: { type: GT.NonNull(LnNoAmountUsdInvoicePaymentInput) }, }, - resolve: async (_, args, { domainAccount }) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, paymentRequest, amount, memo } = args.input if (walletId instanceof InputValidationError) { @@ -90,8 +91,20 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< if (!domainAccount) throw new Error("Authentication required") // eslint-disable-next-line @typescript-eslint/no-explicit-any - const usCents = await usdWalletAmountFromWalletId({ + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { + status: "failed", + errors: [mapAndParseErrorForGqlResponse(routedWalletId)], + } + } + + const usCents = await usdWalletAmountFromWalletId({ + walletId: routedWalletId, amount: amount.toString(), }) if (usCents instanceof Error) { @@ -102,7 +115,7 @@ const LnNoAmountUsdInvoicePaymentSendMutation = GT.Field< } const PayLightningInvoice = await Ibex.payInvoice({ invoice: paymentRequest as Bolt11, - accountId: walletId, + accountId: routedWalletId, send: usCents, }) diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-create.ts b/src/graphql/public/root/mutation/ln-usd-invoice-create.ts index 2cbedab73..064a5d4d3 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-create.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-create.ts @@ -9,6 +9,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import LnInvoicePayload from "@graphql/public/types/payload/ln-invoice" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import { Wallets } from "@app/index" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" const LnUsdInvoiceCreateInput = GT.Input({ @@ -18,7 +19,10 @@ const LnUsdInvoiceCreateInput = GT.Input({ type: GT.NonNull(WalletId), description: "Wallet ID for a USD wallet belonging to the current user.", }, - amount: { type: GT.NonNull(FractionalCentAmount), description: "Amount in USD cents." }, + amount: { + type: GT.NonNull(FractionalCentAmount), + description: "Amount in USD cents.", + }, memo: { type: Memo, description: "Optional memo for the lightning invoice." }, expiresIn: { type: Minutes, @@ -27,7 +31,7 @@ const LnUsdInvoiceCreateInput = GT.Input({ }), }) -const LnUsdInvoiceCreateMutation = GT.Field({ +const LnUsdInvoiceCreateMutation = GT.Field({ extensions: { complexity: 120, }, @@ -39,7 +43,7 @@ const LnUsdInvoiceCreateMutation = GT.Field({ args: { input: { type: GT.NonNull(LnUsdInvoiceCreateInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, amount, memo, expiresIn } = args.input for (const input of [walletId, amount, memo, expiresIn]) { @@ -48,8 +52,17 @@ const LnUsdInvoiceCreateMutation = GT.Field({ } } - const invoice = await Wallets.addInvoiceForSelfForUsdWallet({ + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedWalletId)] } + } + + const invoice = await Wallets.addInvoiceForSelfForUsdWallet({ + walletId: routedWalletId, amount, memo, expiresIn, diff --git a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts index ccfb2c7bb..c1a06e346 100644 --- a/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts +++ b/src/graphql/public/root/mutation/ln-usd-invoice-fee-probe.ts @@ -3,6 +3,7 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import CentAmountPayload from "@graphql/public/types/payload/cent-amount" import LnPaymentRequest from "@graphql/shared/types/scalar/ln-payment-request" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" import { checkedToWalletId } from "@domain/wallets" @@ -53,7 +54,7 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< args: { input: { type: GT.NonNull(LnUsdInvoiceFeeProbeInput) }, }, - resolve: async (_, args) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, paymentRequest } = args.input if (walletId instanceof Error) { @@ -68,6 +69,15 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< if (walletIdChecked instanceof Error) return { errors: [mapAndParseErrorForGqlResponse(walletIdChecked)] } + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, + walletId: walletIdChecked, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(routedWalletId)] } + } + // FLASH FORK: create IBEX fee estimation instead of Galoy fee estimation // const { result: feeSatAmount, error } = // await Payments.getLightningFeeEstimationForUsdWallet({ @@ -75,7 +85,7 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< // uncheckedPaymentRequest: paymentRequest, // }) - const wallet = await WalletsRepository().findById(walletIdChecked) + const wallet = await WalletsRepository().findById(routedWalletId) if (wallet instanceof Error) { return { errors: [mapAndParseErrorForGqlResponse(wallet)] } } @@ -84,8 +94,9 @@ const LnUsdInvoiceFeeProbeMutation = GT.Field< invoice: paymentRequest as Bolt11, currency: wallet.currency, }) - if (resp instanceof IbexError) return { errors: [mapAndParseErrorForGqlResponse(resp)] } - + if (resp instanceof IbexError) + return { errors: [mapAndParseErrorForGqlResponse(resp)] } + return { errors: [], invoiceAmount: resp.invoice, diff --git a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts index b45224a6c..f85b02c55 100644 --- a/src/graphql/public/root/mutation/onchain-usd-payment-send.ts +++ b/src/graphql/public/root/mutation/onchain-usd-payment-send.ts @@ -12,6 +12,7 @@ import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fract import { PaymentSendStatus } from "@domain/bitcoin/lightning" import { Wallets } from "@app/index" import { usdWalletAmountFromWalletId } from "@app/wallets" +import { resolveCashWalletMutationWalletIdForAccount } from "@app/cash-wallet-cutover" const OnChainUsdPaymentSendInput = GT.Input({ name: "OnChainUsdPaymentSendInput", @@ -47,7 +48,7 @@ const OnChainUsdPaymentSendMutation = GT.Field< args: { input: { type: GT.NonNull(OnChainUsdPaymentSendInput) }, }, - resolve: async (_, args, { domainAccount }) => { + resolve: async (_, args, { domainAccount, cashWalletClientCapabilities }) => { const { walletId, address, amount, memo, speed } = args.input if (walletId instanceof Error) { @@ -67,8 +68,20 @@ const OnChainUsdPaymentSendMutation = GT.Field< } if (!domainAccount) throw new Error("Authentication required") - const usdAmount = await usdWalletAmountFromWalletId({ + const routedWalletId = await resolveCashWalletMutationWalletIdForAccount({ + account: domainAccount, walletId, + client: cashWalletClientCapabilities, + }) + if (routedWalletId instanceof Error) { + return { + status: PaymentSendStatus.Failure.value, + errors: [mapAndParseErrorForGqlResponse(routedWalletId)], + } + } + + const usdAmount = await usdWalletAmountFromWalletId({ + walletId: routedWalletId, amount: amount.toString(), }) if (usdAmount instanceof Error) { @@ -77,24 +90,24 @@ const OnChainUsdPaymentSendMutation = GT.Field< errors: [mapAndParseErrorForGqlResponse(usdAmount)], } } - + const result = await Wallets.payOnChainByWalletId({ senderAccount: domainAccount, - senderWalletId: walletId, + senderWalletId: routedWalletId, amount: usdAmount, address, speed, memo, }) if (result instanceof Error) { - return { - status: PaymentSendStatus.Failure.value, - errors: [mapAndParseErrorForGqlResponse(result)] + return { + status: PaymentSendStatus.Failure.value, + errors: [mapAndParseErrorForGqlResponse(result)], } } return { - status: result.status.value, - errors: [] + status: result.status.value, + errors: [], } }, }) diff --git a/src/graphql/public/root/query/account-default-wallet-id.ts b/src/graphql/public/root/query/account-default-wallet-id.ts index 23763edda..f91ebc4a1 100644 --- a/src/graphql/public/root/query/account-default-wallet-id.ts +++ b/src/graphql/public/root/query/account-default-wallet-id.ts @@ -1,10 +1,11 @@ +import { resolveCashWalletPresentationForAccount } from "@app/cash-wallet-cutover" import { mapError } from "@graphql/error-map" import { GT } from "@graphql/index" import Username from "@graphql/shared/types/scalar/username" import WalletId from "@graphql/shared/types/scalar/wallet-id" import { AccountsRepository } from "@services/mongoose" -const AccountDefaultWalletIdQuery = GT.Field({ +const AccountDefaultWalletIdQuery = GT.Field({ deprecationReason: "will be migrated to AccountDefaultWalletId", type: GT.NonNull(WalletId), args: { @@ -12,7 +13,7 @@ const AccountDefaultWalletIdQuery = GT.Field({ type: GT.NonNull(Username), }, }, - resolve: async (_, args) => { + resolve: async (_, args, { cashWalletClientCapabilities }) => { const { username } = args if (username instanceof Error) { @@ -24,8 +25,13 @@ const AccountDefaultWalletIdQuery = GT.Field({ throw mapError(account) } - const walletId = account.defaultWalletId - return walletId + const presentation = await resolveCashWalletPresentationForAccount({ + account, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.defaultWalletId }, }) diff --git a/src/graphql/public/root/query/account-default-wallet.ts b/src/graphql/public/root/query/account-default-wallet.ts index 1387c0169..8d5b12f47 100644 --- a/src/graphql/public/root/query/account-default-wallet.ts +++ b/src/graphql/public/root/query/account-default-wallet.ts @@ -1,4 +1,4 @@ -import { Wallets } from "@app" +import { resolveCashWalletPresentationForAccount } from "@app/cash-wallet-cutover" import { CouldNotFindWalletFromUsernameAndCurrencyError } from "@domain/errors" import { mapError } from "@graphql/error-map" import { GT } from "@graphql/index" @@ -7,7 +7,7 @@ import WalletCurrency from "@graphql/shared/types/scalar/wallet-currency" import PublicWallet from "@graphql/public/types/abstract/public-wallet" import { AccountsRepository } from "@services/mongoose" -const AccountDefaultWalletQuery = GT.Field({ +const AccountDefaultWalletQuery = GT.Field({ type: GT.NonNull(PublicWallet), args: { username: { @@ -15,7 +15,7 @@ const AccountDefaultWalletQuery = GT.Field({ }, walletCurrency: { type: WalletCurrency }, }, - resolve: async (_, args) => { + resolve: async (_, args, { cashWalletClientCapabilities }) => { const { username, walletCurrency } = args if (username instanceof Error) { @@ -27,16 +27,21 @@ const AccountDefaultWalletQuery = GT.Field({ throw mapError(account) } - const wallets = await Wallets.listWalletsByAccountId(account.id) - if (wallets instanceof Error) { - throw mapError(wallets) - } + const presentation = await resolveCashWalletPresentationForAccount({ + account, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) if (!walletCurrency) { - return wallets.find((wallet) => wallet.id === account.defaultWalletId) + return presentation.wallets.find( + (wallet) => wallet.id === presentation.defaultWalletId, + ) } - const wallet = wallets.find((wallet) => wallet.currency === walletCurrency) + const wallet = presentation.wallets.find( + (wallet) => wallet.currency === walletCurrency, + ) if (!wallet) { throw mapError(new CouldNotFindWalletFromUsernameAndCurrencyError(username)) } diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index e0bac3237..30c15eada 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -358,6 +358,25 @@ input CaptchaRequestAuthCodeInput { validationCode: String! } +type CashWalletCutover { + completedAt: Timestamp + cutoverVersion: Int! + pauseReason: String + pausedAt: Timestamp + runId: String + scheduledAt: Timestamp + startedAt: Timestamp + state: CashWalletCutoverState! + updatedAt: Timestamp! + updatedBy: String +} + +enum CashWalletCutoverState { + COMPLETE + IN_PROGRESS + PRE +} + type CashoutOffer { """The rate used when withdrawing to a JMD bank account""" exchangeRate: JMDCents @@ -1296,6 +1315,7 @@ type Query { btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") btcPriceList(range: PriceGraphRange!): [PricePoint] businessMapMarkers: [MapMarker!]! + cashWalletCutover: CashWalletCutover! currencyList: [Currency!]! globals: Globals isFlashNpub(input: IsFlashNpubInput!): IsFlashNpubPayload diff --git a/src/graphql/public/types/object/business-account.ts b/src/graphql/public/types/object/business-account.ts index 97daf3670..a9980a89e 100644 --- a/src/graphql/public/types/object/business-account.ts +++ b/src/graphql/public/types/object/business-account.ts @@ -1,4 +1,8 @@ -import { Accounts, Prices, Wallets } from "@app" +import { Accounts, Prices } from "@app" +import { + cashWalletTransactionWalletIdsForPresentation, + resolveCashWalletPresentationForAccount, +} from "@app/cash-wallet-cutover" import { majorToMinorUnit, @@ -15,8 +19,6 @@ import { checkedConnectionArgs, } from "@graphql/connections" -import { WalletsRepository } from "@services/mongoose" - import IAccount from "../abstract/account" import Wallet from "../../../shared/types/abstract/wallet" @@ -30,7 +32,7 @@ import { TransactionConnection } from "../../../shared/types/object/transaction" import RealtimePrice from "./realtime-price" import { NotificationSettings } from "./notification-settings" -const BusinessAccount = GT.Object({ +const BusinessAccount = GT.Object({ name: "BusinessAccount", interfaces: () => [IAccount], isTypeOf: () => false, @@ -42,15 +44,36 @@ const BusinessAccount = GT.Object({ wallets: { type: GT.NonNullList(Wallet), - resolve: async (source: Account) => { - return Wallets.listWalletsByAccountId(source.id) + resolve: async ( + source: Account, + args, + { cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.wallets }, }, defaultWalletId: { type: GT.NonNull(WalletId), - resolve: (source, args, { domainAccount }: { domainAccount: Account }) => - domainAccount.defaultWalletId, + resolve: async ( + source: Account, + args, + { cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.defaultWalletId + }, }, level: { @@ -60,8 +83,7 @@ const BusinessAccount = GT.Object({ displayCurrency: { type: GT.NonNull(DisplayCurrency), - resolve: (source, args, { domainAccount }: { domainAccount: Account }) => - domainAccount.displayCurrency, + resolve: (source, args, { domainAccount }) => domainAccount.displayCurrency, }, realtimePrice: { @@ -123,21 +145,28 @@ const BusinessAccount = GT.Object({ type: GT.List(WalletId), }, }, - resolve: async (source, args) => { + resolve: async ( + source: Account, + args, + { cashWalletClientCapabilities }: GraphQLPublicContextAuth, + ) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs } + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + let { walletIds } = args - if (!walletIds) { - const wallets = await WalletsRepository().listByAccountId(source.id) - if (wallets instanceof Error) { - throw mapError(wallets) - } - walletIds = wallets.map((wallet) => wallet.id) - } + walletIds = cashWalletTransactionWalletIdsForPresentation({ + walletIds, + presentation, + }) const { result, error } = await Accounts.getTransactionsForAccountByWalletIds({ account: source, diff --git a/src/graphql/public/types/object/consumer-account.ts b/src/graphql/public/types/object/consumer-account.ts index 53e3e5a17..660b3a999 100644 --- a/src/graphql/public/types/object/consumer-account.ts +++ b/src/graphql/public/types/object/consumer-account.ts @@ -1,4 +1,8 @@ -import { Accounts, Prices, Wallets } from "@app" +import { Accounts, Prices } from "@app" +import { + cashWalletTransactionWalletIdsForPresentation, + resolveCashWalletPresentationForAccount, +} from "@app/cash-wallet-cutover" import { majorToMinorUnit, @@ -21,8 +25,6 @@ import WalletId from "@graphql/shared/types/scalar/wallet-id" import RealtimePrice from "@graphql/public/types/object/realtime-price" import DisplayCurrency from "@graphql/shared/types/scalar/display-currency" -import { WalletsRepository } from "@services/mongoose" - import { listEndpoints } from "@app/callback" import AccountLevel from "../../../shared/types/scalar/account-level" @@ -54,14 +56,28 @@ const ConsumerAccount = GT.Object({ wallets: { type: GT.NonNullList(Wallet), - resolve: async (source) => { - return Wallets.listWalletsByAccountId(source.id) + resolve: async (source, args, { cashWalletClientCapabilities }) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.wallets }, }, defaultWalletId: { type: GT.NonNull(WalletId), - resolve: (source) => source.defaultWalletId, + resolve: async (source, args, { cashWalletClientCapabilities }) => { + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + return presentation.defaultWalletId + }, }, displayCurrency: { @@ -145,21 +161,24 @@ const ConsumerAccount = GT.Object({ type: GT.List(WalletId), }, }, - resolve: async (source, args) => { + resolve: async (source, args, { cashWalletClientCapabilities }) => { const paginationArgs = checkedConnectionArgs(args) if (paginationArgs instanceof Error) { throw paginationArgs } + const presentation = await resolveCashWalletPresentationForAccount({ + account: source, + client: cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + let { walletIds } = args - if (!walletIds) { - const wallets = await WalletsRepository().listByAccountId(source.id) - if (wallets instanceof Error) { - throw mapError(wallets) - } - walletIds = wallets.map((wallet) => wallet.id) - } + walletIds = cashWalletTransactionWalletIdsForPresentation({ + walletIds, + presentation, + }) const { result, error } = await Accounts.getTransactionsForAccountByWalletIds({ account: source, diff --git a/src/graphql/shared/root/query/cash-wallet-cutover.ts b/src/graphql/shared/root/query/cash-wallet-cutover.ts new file mode 100644 index 000000000..71ca79e82 --- /dev/null +++ b/src/graphql/shared/root/query/cash-wallet-cutover.ts @@ -0,0 +1,14 @@ +import { GT } from "@graphql/index" +import CashWalletCutoverObject from "@graphql/shared/types/object/cash-wallet-cutover" +import { CashWalletCutoverRepository } from "@services/mongoose/cash-wallet-cutover" + +const CashWalletCutoverQuery = GT.Field({ + type: GT.NonNull(CashWalletCutoverObject), + resolve: async () => { + const config = await CashWalletCutoverRepository().getConfig() + if (config instanceof Error) throw config + return config + }, +}) + +export default CashWalletCutoverQuery diff --git a/src/graphql/shared/types/object/cash-wallet-cutover.ts b/src/graphql/shared/types/object/cash-wallet-cutover.ts new file mode 100644 index 000000000..6570e1b69 --- /dev/null +++ b/src/graphql/shared/types/object/cash-wallet-cutover.ts @@ -0,0 +1,21 @@ +import { GT } from "@graphql/index" +import Timestamp from "@graphql/shared/types/scalar/timestamp" +import CashWalletCutoverState from "@graphql/shared/types/scalar/cash-wallet-cutover-state" + +const CashWalletCutoverObject = GT.Object({ + name: "CashWalletCutover", + fields: () => ({ + state: { type: GT.NonNull(CashWalletCutoverState) }, + scheduledAt: { type: Timestamp }, + startedAt: { type: Timestamp }, + completedAt: { type: Timestamp }, + pausedAt: { type: Timestamp }, + pauseReason: { type: GT.String }, + cutoverVersion: { type: GT.NonNull(GT.Int) }, + runId: { type: GT.String }, + updatedBy: { type: GT.String }, + updatedAt: { type: GT.NonNull(Timestamp) }, + }), +}) + +export default CashWalletCutoverObject diff --git a/src/graphql/shared/types/object/usd-wallet.ts b/src/graphql/shared/types/object/usd-wallet.ts index 27096a000..496527e95 100644 --- a/src/graphql/shared/types/object/usd-wallet.ts +++ b/src/graphql/shared/types/object/usd-wallet.ts @@ -9,6 +9,7 @@ import { mapError } from "@graphql/error-map" import FractionalCentAmount from "@graphql/public/types/scalar/cent-amount-fraction" import { Wallets } from "@app" +import { resolveCashWalletPresentationForAccount } from "@app/cash-wallet-cutover" import { WalletCurrency as WalletCurrencyDomain, USDTAmount } from "@domain/shared" import { WalletType } from "@domain/wallets" @@ -22,6 +23,15 @@ import Lnurl from "../scalar/lnurl" import { TransactionConnection } from "./transaction" +export const usdtMicrosToUsdCents = (usdtMicros: bigint | number | string): number => { + const [wholeMicros, fractionalMicros] = usdtMicros.toString().split(".") + if (fractionalMicros && !/^0+$/.test(fractionalMicros)) { + throw new Error(`Cannot convert fractional USDT micros ${usdtMicros} to USD cents`) + } + + return Number(BigInt(wholeMicros) / 10_000n) +} + const UsdWallet = GT.Object({ name: "UsdWallet", description: @@ -49,17 +59,34 @@ const UsdWallet = GT.Object({ }, balance: { type: FractionalCentAmount, - resolve: async (source) => { + resolve: async (source, args, ctx) => { if (source.type === WalletType.External) return null + let balanceWallet = source + + if ( + "cashWalletClientCapabilities" in ctx && + ctx.domainAccount?.id === source.accountId + ) { + const presentation = await resolveCashWalletPresentationForAccount({ + account: ctx.domainAccount, + client: ctx.cashWalletClientCapabilities, + }) + if (presentation instanceof Error) throw mapError(presentation) + + if (source.id === presentation.legacyUsdWallet?.id) { + balanceWallet = presentation.activeSettlementWallet + } + } + const balance = await Wallets.getBalanceForWallet({ - walletId: source.id, - currency: source.currency, + walletId: balanceWallet.id, + currency: balanceWallet.currency, }) if (balance instanceof Error) { throw mapError(balance) } if (balance instanceof USDTAmount) { - return Number(balance.asSmallestUnits(8)) + return usdtMicrosToUsdCents(balance.asSmallestUnits()) } return Number(balance.asCents(8)) }, diff --git a/src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts b/src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts new file mode 100644 index 000000000..831a377f8 --- /dev/null +++ b/src/graphql/shared/types/scalar/cash-wallet-cutover-state.ts @@ -0,0 +1,12 @@ +import { GT } from "@graphql/index" + +const CashWalletCutoverState = GT.Enum({ + name: "CashWalletCutoverState", + values: { + PRE: { value: "pre" }, + IN_PROGRESS: { value: "in_progress" }, + COMPLETE: { value: "complete" }, + }, +}) + +export default CashWalletCutoverState diff --git a/src/scripts/cash-wallet-cutover.ts b/src/scripts/cash-wallet-cutover.ts new file mode 100644 index 000000000..3bd9a173a --- /dev/null +++ b/src/scripts/cash-wallet-cutover.ts @@ -0,0 +1,130 @@ +#!/usr/bin/env node + +import yargs from "yargs" +import { hideBin } from "yargs/helpers" + +import { CashWalletCutover } from "@app" +import { setupMongoConnection } from "@services/mongodb" +import { + AccountsRepository, + CashWalletCutoverRepository, + WalletsRepository, +} from "@services/mongoose" +import { baseLogger } from "@services/logger" + +const args = yargs(hideBin(process.argv)) + .command("preview", "discover accounts and print the migration plan without writes") + .command("prepare", "discover accounts and upsert migration records") + .command("start", "mark a prepared cutover run in progress") + .command("run-batch", "run one locked migration worker batch") + .command("status", "print cutover config and migration counts") + .command("complete", "mark cutover complete after all migrations finish") + .demandCommand(1) + .option("cutover-version", { type: "number", demandOption: true }) + .option("run-id", { type: "string", demandOption: true }) + .option("operator", { type: "string", default: "unknown" }) + .option("worker-id", { type: "string", default: `worker-${process.pid}` }) + .option("limit", { type: "number", default: 25 }) + .option("lock-stale-seconds", { type: "number", default: 300 }) + .option("configPath", { type: "string", demandOption: true }) + .parseSync() + +const repository = CashWalletCutoverRepository() + +const toJson = (result: unknown) => { + console.log(JSON.stringify(result, null, 2)) +} + +const run = async () => { + const command = args._[0] + const cutoverVersion = args["cutover-version"] + const runId = args["run-id"] + + switch (command) { + case "preview": { + const result = await CashWalletCutover.previewPrimaryCashWalletCutover({ + cutoverVersion, + runId, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "prepare": { + const result = await CashWalletCutover.preparePrimaryCashWalletCutover({ + cutoverVersion, + runId, + accountsRepo: AccountsRepository(), + walletsRepo: WalletsRepository(), + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "start": { + const result = await CashWalletCutover.startPrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor: args.operator, + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "run-batch": { + const result = await CashWalletCutover.runPrimaryCashWalletCutoverBatch({ + cutoverVersion, + runId, + workerId: args["worker-id"], + limit: args.limit, + lockStaleBefore: new Date(Date.now() - args["lock-stale-seconds"] * 1000), + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "status": { + const result = await CashWalletCutover.getPrimaryCashWalletCutoverStatus({ + cutoverVersion, + runId, + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + case "complete": { + const result = await CashWalletCutover.completePrimaryCashWalletCutover({ + cutoverVersion, + runId, + actor: args.operator, + migrationsRepo: repository, + }) + if (result instanceof Error) throw result + toJson(result) + return + } + + default: + throw new Error(`Unsupported cash wallet cutover command: ${command}`) + } +} + +setupMongoConnection() + .then(async (mongoose) => { + await run() + await mongoose?.connection.close() + process.exit(0) + }) + .catch((error) => { + baseLogger.error({ error }, "Cash wallet cutover operator command failed") + process.exit(1) + }) diff --git a/src/servers/graphql-main-server.ts b/src/servers/graphql-main-server.ts index a0c3b1598..dfbeb53e6 100644 --- a/src/servers/graphql-main-server.ts +++ b/src/servers/graphql-main-server.ts @@ -6,7 +6,6 @@ import { AuthorizationError } from "@graphql/error" import { gqlMainSchema, mutationFields, queryFields } from "@graphql/public" import { bootstrap } from "@app/bootstrap" -import { activateLndHealthCheck } from "@services/lnd/health" import { baseLogger } from "@services/logger" import { setupMongoConnection } from "@services/mongodb" import { shield } from "graphql-shield" @@ -20,6 +19,7 @@ import { import { NextFunction, Request, Response } from "express" import { parseIps } from "@domain/accounts-ips" +import { parseCashWalletClientCapabilities } from "@app/cash-wallet-cutover/client-capability" import { startApolloServerForAdminSchema } from "./graphql-admin-server" import { isAuthenticated, startApolloServer } from "./graphql-server" @@ -44,8 +44,12 @@ const setGqlContext = async ( tokenPayload, ip, }) + const cashWalletClientCapabilities = parseCashWalletClientCapabilities(req.headers) - req.gqlContext = gqlContext + req.gqlContext = { + ...gqlContext, + cashWalletClientCapabilities, + } return addAttributesToCurrentSpanAndPropagate( { @@ -56,6 +60,11 @@ const setGqlContext = async ( [SemanticAttributes.HTTP_USER_AGENT]: req.headers["user-agent"], [ACCOUNT_USERNAME]: gqlContext?.domainAccount?.username, [SemanticAttributes.ENDUSER_ID]: tokenPayload?.sub, + "cash_wallet.client_presentation": + cashWalletClientCapabilities.cashWalletPresentation, + "cash_wallet.client_usdt_supported": String( + cashWalletClientCapabilities.hasUsdtCashWalletSupport, + ), }, next, ) @@ -101,9 +110,9 @@ export async function startApolloServerForCoreSchema() { if (require.main === module) { setupMongoConnection(true) .then(async () => { - // activateLndHealthCheck() // + // activateLndHealthCheck() - const res = await bootstrap() + await bootstrap() // if (res instanceof Error) throw res await Promise.race([ diff --git a/src/servers/index.files.d.ts b/src/servers/index.files.d.ts index 29513578f..c918274f8 100644 --- a/src/servers/index.files.d.ts +++ b/src/servers/index.files.d.ts @@ -13,6 +13,7 @@ type GraphQLPublicContext = { domainAccount: Account | undefined ip: IpAddress | undefined sessionId: SessionId | undefined + cashWalletClientCapabilities: import("@app/cash-wallet-cutover/client-capability").CashWalletClientCapabilities } type GraphQLPublicContextAuth = Omit & { diff --git a/src/servers/middlewares/session.ts b/src/servers/middlewares/session.ts index 03201a40a..2631ce286 100644 --- a/src/servers/middlewares/session.ts +++ b/src/servers/middlewares/session.ts @@ -1,6 +1,7 @@ import DataLoader from "dataloader" import { Accounts, Transactions } from "@app" +import { DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES } from "@app/cash-wallet-cutover" import { recordExceptionInCurrentSpan } from "@services/tracing" import jsonwebtoken from "jsonwebtoken" @@ -67,8 +68,7 @@ export const sessionPublicContext = async ({ error: txnMetadata, }) return keys.map(() => undefined) - } - else if (txnMetadata instanceof Error) { + } else if (txnMetadata instanceof Error) { recordExceptionInCurrentSpan({ error: txnMetadata, level: txnMetadata.level, @@ -88,5 +88,6 @@ export const sessionPublicContext = async ({ domainAccount, ip, sessionId, + cashWalletClientCapabilities: DEFAULT_CASH_WALLET_CLIENT_CAPABILITIES, } } diff --git a/src/servers/ws-server.ts b/src/servers/ws-server.ts index 9255a7f46..c98cbb28a 100644 --- a/src/servers/ws-server.ts +++ b/src/servers/ws-server.ts @@ -10,6 +10,7 @@ import jsonwebtoken from "jsonwebtoken" import { parseIps } from "@domain/accounts-ips" import { ErrorLevel } from "@domain/shared" +import { parseCashWalletClientCapabilities } from "@app/cash-wallet-cutover" import jwksRsa from "jwks-rsa" @@ -60,12 +61,16 @@ const getContext = async ( fnName: "getContext", fn: async () => { const connectionParams = ctx.connectionParams + const cashWalletClientCapabilities = parseCashWalletClientCapabilities({ + ...ctx.extra?.request?.headers, + ...connectionParams, + }) // TODO: check if nginx pass the ip to the header // TODO: ip not been used currently for subscription. // implement some rate limiting. const ipString = UNSECURE_IP_FROM_REQUEST_OBJECT - ? connectionParams?.ip ?? ctx.extra?.request?.socket?.remoteAddress + ? (connectionParams?.ip ?? ctx.extra?.request?.socket?.remoteAddress) : connectionParams?.["x-real-ip"] || connectionParams?.["x-forwarded-for"] const ip = parseIps(ipString) @@ -82,10 +87,14 @@ const getContext = async ( sub: kratosCookieRes.kratosUserId, } - return sessionPublicContext({ + const context = await sessionPublicContext({ tokenPayload, ip, }) + return { + ...context, + cashWalletClientCapabilities, + } } const kratosToken = authz?.slice(7) as AuthToken @@ -106,10 +115,14 @@ const getContext = async ( return false } - return sessionPublicContext({ + const context = await sessionPublicContext({ tokenPayload, ip, }) + return { + ...context, + cashWalletClientCapabilities, + } }, })() } diff --git a/src/services/mongoose/cash-wallet-cutover.ts b/src/services/mongoose/cash-wallet-cutover.ts new file mode 100644 index 000000000..7db76ae4b --- /dev/null +++ b/src/services/mongoose/cash-wallet-cutover.ts @@ -0,0 +1,329 @@ +import { randomUUID } from "crypto" + +import { CouldNotUpdateError } from "@domain/errors" + +import { parseRepositoryError } from "./utils" +import { CashWalletCutoverConfig, CashWalletMigration } from "./schema" + +const CONFIG_ID = "cash_wallet_cutover" + +const TERMINAL_STATUSES: CashWalletMigrationStatus[] = [ + "complete", + "failed", + "requires_operator_review", + "skipped_already_migrated", + "rolled_back", +] + +type UpsertMigrationArgs = { + accountId: AccountId + accountUuid?: AccountUuid + legacyUsdWalletId: WalletId + destinationUsdtWalletId: WalletId + previousDefaultWalletId?: WalletId + cutoverVersion: number + runId: string + idempotencyKey: string +} + +type TransitionMigrationArgs = { + id: string + from: CashWalletMigrationStatus + to: CashWalletMigrationStatus + cutoverVersion: number + runId: string + patch?: Partial +} + +type LockMigrationArgs = { + id: string + workerId: string + staleBefore: Date + cutoverVersion: number + runId: string +} + +type MarkMigrationFailedArgs = Omit & { + error: Error + status: "failed" | "requires_operator_review" +} + +const defaultConfig = (): CashWalletCutoverConfig => ({ + state: "pre", + cutoverVersion: 1, + updatedAt: new Date(0), +}) + +const resultToConfig = ( + record: CashWalletCutoverConfigRecord, +): CashWalletCutoverConfig => ({ + state: record.state, + scheduledAt: record.scheduledAt, + startedAt: record.startedAt, + completedAt: record.completedAt, + pausedAt: record.pausedAt, + pauseReason: record.pauseReason, + updatedBy: record.updatedBy, + cutoverVersion: record.cutoverVersion, + runId: record.runId, + updatedAt: record.updatedAt, +}) + +const resultToMigration = (record: CashWalletMigrationRecord): CashWalletMigration => ({ + id: record._id, + accountId: record.accountId as AccountId, + accountUuid: record.accountUuid as AccountUuid | undefined, + legacyUsdWalletId: record.legacyUsdWalletId as WalletId, + destinationUsdtWalletId: record.destinationUsdtWalletId as WalletId, + previousDefaultWalletId: record.previousDefaultWalletId as WalletId | undefined, + cutoverVersion: record.cutoverVersion, + runId: record.runId, + status: record.status, + sourceBalanceUsdCents: record.sourceBalanceUsdCents, + destinationAmountUsdtMicros: record.destinationAmountUsdtMicros, + destinationStartingBalanceUsdtMicros: record.destinationStartingBalanceUsdtMicros, + feeAmountUsdCents: record.feeAmountUsdCents, + feeAmountUsdtMicros: record.feeAmountUsdtMicros, + balanceMoveInvoicePaymentRequest: record.balanceMoveInvoicePaymentRequest, + balanceMoveInvoicePaymentHash: record.balanceMoveInvoicePaymentHash, + balanceMovePaymentTransactionId: record.balanceMovePaymentTransactionId, + feeReimbursementInvoicePaymentRequest: record.feeReimbursementInvoicePaymentRequest, + feeReimbursementInvoicePaymentHash: record.feeReimbursementInvoicePaymentHash, + feeReimbursementPaymentTransactionId: record.feeReimbursementPaymentTransactionId, + estimatedFee: record.estimatedFee, + idempotencyKey: record.idempotencyKey, + attempts: record.attempts, + lastError: record.lastError, + lockedAt: record.lockedAt, + lockedBy: record.lockedBy, + startedAt: record.startedAt, + completedAt: record.completedAt, + updatedAt: record.updatedAt, +}) + +export const CashWalletCutoverRepository = () => { + const getConfig = async (): Promise => { + try { + const result = await CashWalletCutoverConfig.findById(CONFIG_ID) + if (!result) return defaultConfig() + return resultToConfig(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const updateConfig = async ( + patch: Partial, + actor?: string, + ): Promise => { + try { + const result = await CashWalletCutoverConfig.findOneAndUpdate( + { _id: CONFIG_ID }, + { $set: { ...patch, updatedBy: actor, updatedAt: new Date() } }, + { upsert: true, new: true }, + ) + return resultToConfig(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const upsertMigration = async ( + args: UpsertMigrationArgs, + ): Promise => { + try { + const now = new Date() + const result = await CashWalletMigration.findOneAndUpdate( + { accountId: args.accountId, runId: args.runId }, + { + $setOnInsert: { + _id: randomUUID(), + ...args, + status: "not_started", + attempts: 0, + updatedAt: now, + }, + }, + { upsert: true, new: true }, + ) + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const findMigrationByAccountId = async ({ + accountId, + cutoverVersion, + runId, + }: { + accountId: AccountId + cutoverVersion: number + runId: string + }): Promise => { + try { + const result = await CashWalletMigration.findOne({ + accountId, + cutoverVersion, + runId, + }) + if (!result) return null + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const transitionMigration = async ({ + id, + from, + to, + cutoverVersion, + runId, + patch = {}, + }: TransitionMigrationArgs): Promise => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { _id: id, status: from, cutoverVersion, runId }, + { $set: { ...patch, status: to, updatedAt: new Date() } }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not transition cash wallet migration") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const acquireMigrationLock = async ({ + id, + workerId, + staleBefore, + cutoverVersion, + runId, + }: LockMigrationArgs): Promise => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { + _id: id, + cutoverVersion, + runId, + $or: [{ lockedAt: null }, { lockedAt: { $lt: staleBefore } }], + }, + { $set: { lockedAt: new Date(), lockedBy: workerId, updatedAt: new Date() } }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not acquire cash wallet migration lock") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const releaseMigrationLock = async ({ + id, + workerId, + cutoverVersion, + runId, + }: Omit): Promise< + CashWalletMigration | RepositoryError + > => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { _id: id, lockedBy: workerId, cutoverVersion, runId }, + { $set: { lockedAt: null, lockedBy: null, updatedAt: new Date() } }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not release cash wallet migration lock") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const markMigrationFailed = async ({ + id, + workerId, + cutoverVersion, + runId, + error, + status, + }: MarkMigrationFailedArgs): Promise => { + try { + const result = await CashWalletMigration.findOneAndUpdate( + { _id: id, lockedBy: workerId, cutoverVersion, runId }, + { + $set: { + status, + lastError: error.message, + lockedAt: null, + lockedBy: null, + updatedAt: new Date(), + }, + $inc: { attempts: 1 }, + }, + { new: true }, + ) + if (!result) + return new CouldNotUpdateError("Could not mark cash wallet migration failed") + return resultToMigration(result) + } catch (err) { + return parseRepositoryError(err) + } + } + + const listRunnableMigrations = async ({ + cutoverVersion, + runId, + limit, + }: { + cutoverVersion: number + runId: string + limit?: number + }): Promise => { + try { + const results = await CashWalletMigration.find({ + cutoverVersion, + runId, + status: { $nin: TERMINAL_STATUSES }, + }) + .sort({ updatedAt: 1 }) + .limit(limit ?? 0) + return results.map(resultToMigration) + } catch (err) { + return parseRepositoryError(err) + } + } + + const countByStatus = async ({ + cutoverVersion, + runId, + status, + }: { + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + }): Promise => { + try { + return CashWalletMigration.countDocuments({ cutoverVersion, runId, status }) + } catch (err) { + return parseRepositoryError(err) + } + } + + return { + getConfig, + updateConfig, + upsertMigration, + findMigrationByAccountId, + transitionMigration, + acquireMigrationLock, + releaseMigrationLock, + markMigrationFailed, + listRunnableMigrations, + countByStatus, + } +} diff --git a/src/services/mongoose/index.ts b/src/services/mongoose/index.ts index 8e5be4f68..e0cb83509 100644 --- a/src/services/mongoose/index.ts +++ b/src/services/mongoose/index.ts @@ -8,3 +8,4 @@ export * from "./wallet-invoices" export * from "./wallet-on-chain-addresses" export * from "./wallet-onchain-pending-receive" export * from "./merchants" +export * from "./cash-wallet-cutover" diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index ef7b0ebf9..a15023362 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -620,6 +620,68 @@ export const WalletOnChainPendingReceive = WalletOnChainPendingReceiveSchema, ) +const CashWalletCutoverConfigSchema = new Schema({ + _id: { type: String, default: "cash_wallet_cutover" }, + state: { type: String, enum: ["pre", "in_progress", "complete"], required: true }, + scheduledAt: Date, + startedAt: Date, + completedAt: Date, + pausedAt: Date, + pauseReason: String, + updatedBy: String, + cutoverVersion: { type: Number, required: true, default: 1 }, + runId: String, + updatedAt: { type: Date, default: Date.now }, +}) + +export const CashWalletCutoverConfig = mongoose.model( + "CashWalletCutoverConfig", + CashWalletCutoverConfigSchema, +) + +const CashWalletMigrationSchema = new Schema({ + _id: { type: String, required: true }, + accountId: { type: String, required: true, index: true }, + accountUuid: String, + legacyUsdWalletId: { type: String, required: true }, + destinationUsdtWalletId: { type: String, required: true }, + previousDefaultWalletId: String, + cutoverVersion: { type: Number, required: true, index: true }, + runId: { type: String, required: true, index: true }, + status: { type: String, required: true, index: true }, + sourceBalanceUsdCents: String, + destinationAmountUsdtMicros: String, + destinationStartingBalanceUsdtMicros: String, + feeAmountUsdCents: String, + feeAmountUsdtMicros: String, + balanceMoveInvoicePaymentRequest: String, + balanceMoveInvoicePaymentHash: String, + balanceMovePaymentTransactionId: String, + feeReimbursementInvoicePaymentRequest: String, + feeReimbursementInvoicePaymentHash: String, + feeReimbursementPaymentTransactionId: String, + estimatedFee: Boolean, + idempotencyKey: { type: String, required: true }, + attempts: { type: Number, default: 0 }, + lastError: String, + lockedAt: Date, + lockedBy: String, + startedAt: Date, + completedAt: Date, + updatedAt: { type: Date, default: Date.now, index: true }, +}) + +CashWalletMigrationSchema.index({ accountId: 1, runId: 1 }, { unique: true }) +CashWalletMigrationSchema.index({ idempotencyKey: 1 }, { unique: true }) +CashWalletMigrationSchema.index({ cutoverVersion: 1, status: 1, updatedAt: 1 }) +CashWalletMigrationSchema.index({ runId: 1, status: 1 }) +CashWalletMigrationSchema.index({ lockedAt: 1 }) + +export const CashWalletMigration = mongoose.model( + "CashWalletMigration", + CashWalletMigrationSchema, +) + const BridgeVirtualAccountSchema = new Schema({ // unique: true enforces one VA per account at the DB layer — idempotency guard accountId: { type: String, required: true, unique: true }, diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 39d5a3da9..332c918f5 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -103,6 +103,52 @@ interface AccountRecord { save: () => Promise } +interface CashWalletCutoverConfigRecord { + _id: string + state: CashWalletCutoverState + scheduledAt?: Date + startedAt?: Date + completedAt?: Date + pausedAt?: Date + pauseReason?: string + updatedBy?: string + cutoverVersion: number + runId?: string + updatedAt: Date +} + +interface CashWalletMigrationRecord { + _id: string + accountId: string + accountUuid?: string + legacyUsdWalletId: string + destinationUsdtWalletId: string + previousDefaultWalletId?: string + cutoverVersion: number + runId: string + status: CashWalletMigrationStatus + sourceBalanceUsdCents?: string + destinationAmountUsdtMicros?: string + destinationStartingBalanceUsdtMicros?: string + feeAmountUsdCents?: string + feeAmountUsdtMicros?: string + balanceMoveInvoicePaymentRequest?: string + balanceMoveInvoicePaymentHash?: string + balanceMovePaymentTransactionId?: string + feeReimbursementInvoicePaymentRequest?: string + feeReimbursementInvoicePaymentHash?: string + feeReimbursementPaymentTransactionId?: string + estimatedFee?: boolean + idempotencyKey: string + attempts: number + lastError?: string + lockedAt?: Date + lockedBy?: string + startedAt?: Date + completedAt?: Date + updatedAt: Date +} + interface LocationRecord { type: "Point" coordinates: CoordinateRecord diff --git a/test/flash/unit/app/cash-wallet-cutover/amount-conversion.spec.ts b/test/flash/unit/app/cash-wallet-cutover/amount-conversion.spec.ts new file mode 100644 index 000000000..db42bc5f0 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/amount-conversion.spec.ts @@ -0,0 +1,50 @@ +import { + destinationShortfallUsdtMicros, + feeUsdCentsToUsdtMicros, + usdCentsToUsdtMicros, + usdtMicrosToUsdCentsCeil, +} from "@app/cash-wallet-cutover/amount-conversion" + +describe("cash wallet cutover amount conversion", () => { + it("converts USD cents to USDT micros exactly", () => { + expect(usdCentsToUsdtMicros("0")).toBe("0") + expect(usdCentsToUsdtMicros("1")).toBe("10000") + expect(usdCentsToUsdtMicros("100")).toBe("1000000") + expect(usdCentsToUsdtMicros("123456789")).toBe("1234567890000") + }) + + it("converts fee USD cents to USDT micros exactly", () => { + expect(feeUsdCentsToUsdtMicros("7")).toBe("70000") + }) + + it("rounds USDT micros up to USD cents for fee audit fields", () => { + expect(usdtMicrosToUsdCentsCeil("0")).toBe("0") + expect(usdtMicrosToUsdCentsCeil("1")).toBe("1") + expect(usdtMicrosToUsdCentsCeil("10000")).toBe("1") + expect(usdtMicrosToUsdCentsCeil("10001")).toBe("2") + }) + + it("computes destination USDT shortfall from the observed balance delta", () => { + expect( + destinationShortfallUsdtMicros({ + targetUsdtMicros: "10000000", + startingUsdtMicros: "5000000", + currentUsdtMicros: "14930000", + }), + ).toBe("70000") + expect( + destinationShortfallUsdtMicros({ + targetUsdtMicros: "10000000", + startingUsdtMicros: "5000000", + currentUsdtMicros: "15000000", + }), + ).toBe("0") + }) + + it("rejects invalid or fractional cent inputs", () => { + expect(usdCentsToUsdtMicros("1.5")).toBeInstanceOf(Error) + expect(usdCentsToUsdtMicros("abc")).toBeInstanceOf(Error) + expect(usdCentsToUsdtMicros("-1")).toBeInstanceOf(Error) + expect(usdtMicrosToUsdCentsCeil("1.5")).toBeInstanceOf(Error) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/client-capability.spec.ts b/test/flash/unit/app/cash-wallet-cutover/client-capability.spec.ts new file mode 100644 index 000000000..cfe6211e8 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/client-capability.spec.ts @@ -0,0 +1,46 @@ +import { + CASH_WALLET_USDT_CLIENT_CAPABILITY, + parseCashWalletClientCapabilities, +} from "@app/cash-wallet-cutover/client-capability" + +describe("cash wallet client capability parser", () => { + it("defaults missing headers to legacy compatibility", () => { + expect(parseCashWalletClientCapabilities({})).toEqual({ + cashWalletPresentation: "legacy_compat", + hasUsdtCashWalletSupport: false, + }) + }) + + it("treats unknown capabilities as legacy compatibility", () => { + expect( + parseCashWalletClientCapabilities({ + "x-flash-client-capabilities": "contacts-v2", + }), + ).toEqual({ + cashWalletPresentation: "legacy_compat", + hasUsdtCashWalletSupport: false, + }) + }) + + it("detects the USDT Cash Wallet capability", () => { + expect( + parseCashWalletClientCapabilities({ + "x-flash-client-capabilities": `contacts-v2, ${CASH_WALLET_USDT_CLIENT_CAPABILITY}`, + }), + ).toEqual({ + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, + }) + }) + + it("accepts native client connection-param casing", () => { + expect( + parseCashWalletClientCapabilities({ + "X-Flash-Client-Capabilities": CASH_WALLET_USDT_CLIENT_CAPABILITY, + }), + ).toEqual({ + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, + }) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/cutover-gate.spec.ts b/test/flash/unit/app/cash-wallet-cutover/cutover-gate.spec.ts new file mode 100644 index 000000000..356ce2a5f --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/cutover-gate.spec.ts @@ -0,0 +1,154 @@ +import { + CashWalletCutoverInProgressError, + CashWalletMigrationFailedError, + evaluateCashWalletCutoverGuard, + evaluateCashWalletCutoverPresentation, +} from "@app/cash-wallet-cutover/guard" + +const config = (state: CashWalletCutoverState): CashWalletCutoverConfig => ({ + state, + cutoverVersion: 2, + runId: "run-2", + updatedAt: new Date("2026-05-19T00:00:00Z"), +}) + +const migration = (status: CashWalletMigrationStatus): CashWalletMigration => ({ + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 2, + runId: "run-2", + status, + idempotencyKey: "run-2:account-id", + attempts: 0, + updatedAt: new Date("2026-05-19T00:00:00Z"), +}) + +const legacyClient = { + cashWalletPresentation: "legacy_compat" as const, + hasUsdtCashWalletSupport: false, +} + +const usdtClient = { + cashWalletPresentation: "usdt" as const, + hasUsdtCashWalletSupport: true, +} + +describe("cash wallet cutover guard", () => { + it("allows legacy route before cutover starts", () => { + expect(evaluateCashWalletCutoverGuard({ cutover: config("pre") })).toEqual({ + route: "legacy_usd", + }) + }) + + it("allows legacy route during cutover before this account starts", () => { + expect(evaluateCashWalletCutoverGuard({ cutover: config("in_progress") })).toEqual({ + route: "legacy_usd", + }) + expect( + evaluateCashWalletCutoverGuard({ + cutover: config("in_progress"), + migration: migration("not_started"), + }), + ).toEqual({ route: "legacy_usd" }) + }) + + it("rejects writes while this account is actively migrating", () => { + for (const status of [ + "balance_read", + "balance_move_sending", + "fee_reimbursement_sending", + ] as const) { + expect( + evaluateCashWalletCutoverGuard({ + cutover: config("in_progress"), + migration: migration(status), + }), + ).toBeInstanceOf(CashWalletCutoverInProgressError) + } + }) + + it("routes completed accounts to USDT during cutover", () => { + expect( + evaluateCashWalletCutoverGuard({ + cutover: config("in_progress"), + migration: migration("complete"), + }), + ).toEqual({ route: "usdt" }) + }) + + it("rejects failed and manual-review migrations", () => { + for (const status of ["failed", "requires_operator_review"] as const) { + expect( + evaluateCashWalletCutoverGuard({ + cutover: config("in_progress"), + migration: migration(status), + }), + ).toBeInstanceOf(CashWalletMigrationFailedError) + } + }) + + it("routes all accounts to USDT after global completion", () => { + expect(evaluateCashWalletCutoverGuard({ cutover: config("complete") })).toEqual({ + route: "usdt", + }) + }) +}) + +describe("cash wallet cutover presentation", () => { + it("presents legacy USD before cutover starts", () => { + expect( + evaluateCashWalletCutoverPresentation({ + cutover: config("pre"), + client: usdtClient, + }), + ).toEqual({ + presentation: "legacy_usd", + }) + }) + + it("presents completed accounts as legacy-compatible for old clients", () => { + expect( + evaluateCashWalletCutoverPresentation({ + cutover: config("in_progress"), + migration: migration("complete"), + client: legacyClient, + }), + ).toEqual({ + presentation: "legacy_usd_compat", + }) + }) + + it("presents completed accounts as USDT for capable clients", () => { + expect( + evaluateCashWalletCutoverPresentation({ + cutover: config("in_progress"), + migration: migration("complete"), + client: usdtClient, + }), + ).toEqual({ + presentation: "usdt", + }) + }) + + it("uses client capability after global completion", () => { + expect( + evaluateCashWalletCutoverPresentation({ + cutover: config("complete"), + client: legacyClient, + }), + ).toEqual({ + presentation: "legacy_usd_compat", + }) + + expect( + evaluateCashWalletCutoverPresentation({ + cutover: config("complete"), + client: usdtClient, + }), + ).toEqual({ + presentation: "usdt", + }) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/discovery-collector.spec.ts b/test/flash/unit/app/cash-wallet-cutover/discovery-collector.spec.ts new file mode 100644 index 000000000..201b382ba --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/discovery-collector.spec.ts @@ -0,0 +1,86 @@ +import { RepositoryError } from "@domain/errors" + +import { discoverCashWalletCutoverAccounts } from "@app/cash-wallet-cutover/discovery" + +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const account = (id: AccountId, defaultWalletId: WalletId): Account => + ({ + id, + uuid: `${id}-uuid` as AccountUuid, + defaultWalletId, + }) as Account + +const wallet = ({ + id, + accountId, + currency, +}: { + id: WalletId + accountId: AccountId + currency: WalletCurrency +}): Wallet => + ({ + id, + accountId, + type: WalletType.Checking, + currency, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: "lnurl" as Lnurl, + }) as Wallet + +async function* unlockedAccounts(accounts: Account[]): AsyncGenerator { + for (const account of accounts) yield account +} + +describe("cash wallet cutover account discovery collector", () => { + it("classifies every unlocked account with its wallets", async () => { + const accountOne = account("account-1" as AccountId, "account-1-usd" as WalletId) + const accountTwo = account("account-2" as AccountId, "account-2-usdt" as WalletId) + const walletsRepo = { + listByAccountId: jest.fn(async (accountId: AccountId) => [ + wallet({ + id: `${accountId}-usd` as WalletId, + accountId, + currency: WalletCurrency.Usd, + }), + wallet({ + id: `${accountId}-usdt` as WalletId, + accountId, + currency: WalletCurrency.Usdt, + }), + ]), + } + + const result = await discoverCashWalletCutoverAccounts({ + accountsRepo: { + listUnlockedAccounts: () => unlockedAccounts([accountOne, accountTwo]), + }, + walletsRepo, + }) + + expect(result).toEqual([ + expect.objectContaining({ accountId: "account-1", status: "legacy_default" }), + expect.objectContaining({ accountId: "account-2", status: "already_usdt" }), + ]) + expect(walletsRepo.listByAccountId).toHaveBeenCalledWith("account-1") + expect(walletsRepo.listByAccountId).toHaveBeenCalledWith("account-2") + }) + + it("returns repository errors without continuing discovery", async () => { + const accountOne = account("account-1" as AccountId, "account-1-usd" as WalletId) + const error = new RepositoryError("wallet lookup failed") + const walletsRepo = { + listByAccountId: jest.fn(async () => error), + } + + const result = await discoverCashWalletCutoverAccounts({ + accountsRepo: { listUnlockedAccounts: () => unlockedAccounts([accountOne]) }, + walletsRepo, + }) + + expect(result).toBe(error) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/discovery.spec.ts b/test/flash/unit/app/cash-wallet-cutover/discovery.spec.ts new file mode 100644 index 000000000..1606d9cad --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/discovery.spec.ts @@ -0,0 +1,111 @@ +import { classifyCashWalletsForCutover } from "@app/cash-wallet-cutover/discovery" + +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const account = (defaultWalletId: WalletId): Account => ({ + id: "account-id" as AccountId, + uuid: "account-uuid" as AccountUuid, + createdAt: new Date("2026-05-20T00:00:00Z"), + defaultWalletId, + username: "username" as Username, + npub: "npub" as Npub, + level: 1 as AccountLevel, + status: "active" as AccountStatus, + statusHistory: [{ status: "active" as AccountStatus, timestamp: new Date() }], + title: "" as BusinessMapTitle, + coordinates: undefined as Coordinates, + contactEnabled: false, + contacts: [], + withdrawFee: 0 as Satoshis, + isEditor: false, + notificationSettings: { push: { enabled: true, disabledCategories: [] } }, + quizQuestions: [], + quiz: [], + kratosUserId: "user-id" as UserId, + displayCurrency: "USD" as DisplayCurrency, +}) + +const wallet = ({ + id, + currency, + type = WalletType.Checking, +}: { + id: WalletId + currency: WalletCurrency + type?: WalletType +}): Wallet => ({ + id, + accountId: "account-id" as AccountId, + type, + currency, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: "lnurl" as Lnurl, +}) + +describe("cash wallet cutover discovery", () => { + const legacyUsdWallet = wallet({ + id: "legacy-usd-wallet-id" as WalletId, + currency: WalletCurrency.Usd, + }) + const destinationUsdtWallet = wallet({ + id: "usdt-wallet-id" as WalletId, + currency: WalletCurrency.Usdt, + }) + + it("classifies accounts whose default still points to legacy USD", () => { + const result = classifyCashWalletsForCutover({ + account: account("legacy-usd-wallet-id" as WalletId), + wallets: [legacyUsdWallet, destinationUsdtWallet], + }) + + expect(result).toMatchObject({ + status: "legacy_default", + accountId: "account-id", + accountUuid: "account-uuid", + legacyUsdWalletId: "legacy-usd-wallet-id", + destinationUsdtWalletId: "usdt-wallet-id", + previousDefaultWalletId: "legacy-usd-wallet-id", + }) + }) + + it("classifies accounts already defaulting to ETH-USDT", () => { + const result = classifyCashWalletsForCutover({ + account: account("usdt-wallet-id" as WalletId), + wallets: [legacyUsdWallet, destinationUsdtWallet], + }) + + expect(result).toMatchObject({ + status: "already_usdt", + legacyUsdWalletId: "legacy-usd-wallet-id", + destinationUsdtWalletId: "usdt-wallet-id", + previousDefaultWalletId: "usdt-wallet-id", + }) + }) + + it("classifies legacy USD wallets that are no longer the default as residual", () => { + const result = classifyCashWalletsForCutover({ + account: account("btc-wallet-id" as WalletId), + wallets: [legacyUsdWallet, destinationUsdtWallet], + }) + + expect(result).toMatchObject({ status: "residual_legacy_usd" }) + }) + + it("surfaces accounts that cannot be planned because a required cash wallet is missing", () => { + expect( + classifyCashWalletsForCutover({ + account: account("legacy-usd-wallet-id" as WalletId), + wallets: [legacyUsdWallet], + }), + ).toMatchObject({ status: "missing_destination_usdt" }) + + expect( + classifyCashWalletsForCutover({ + account: account("usdt-wallet-id" as WalletId), + wallets: [destinationUsdtWallet], + }), + ).toMatchObject({ status: "missing_legacy_usd" }) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/executor.spec.ts b/test/flash/unit/app/cash-wallet-cutover/executor.spec.ts new file mode 100644 index 000000000..60d5fae41 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/executor.spec.ts @@ -0,0 +1,82 @@ +import { CouldNotUpdateError } from "@domain/errors" + +import { executeCashWalletMigrationStep } from "@app/cash-wallet-cutover/executor" + +const migration = (status: CashWalletMigrationStatus): CashWalletMigration => ({ + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "legacy-usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 7, + runId: "run-7", + status, + idempotencyKey: "cash-wallet-cutover:run-7:account-id", + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), +}) + +const handlers = () => ({ + not_started: jest.fn(async () => migration("started")), + started: jest.fn(async () => migration("provisioned")), + provisioned: jest.fn(async () => migration("balance_read")), + balance_read: jest.fn(async () => migration("invoice_created")), + invoice_created: jest.fn(async () => migration("balance_move_sending")), + balance_move_sending: jest.fn(async () => migration("balance_move_sent")), + balance_move_sent: jest.fn(async () => migration("balance_move_verified")), + balance_move_verified: jest.fn(async () => + migration("fee_reimbursement_invoice_created"), + ), + fee_reimbursement_invoice_created: jest.fn(async () => + migration("fee_reimbursement_sending"), + ), + fee_reimbursement_sending: jest.fn(async () => migration("fee_reimbursed")), + fee_reimbursed: jest.fn(async () => migration("pointer_flipped")), + pointer_flipped: jest.fn(async () => migration("legacy_zero_verified")), + legacy_zero_verified: jest.fn(async () => migration("complete")), +}) + +describe("cash wallet migration executor", () => { + it("dispatches a runnable migration to the handler for its current status", async () => { + const stepHandlers = handlers() + + const result = await executeCashWalletMigrationStep({ + migration: migration("invoice_created"), + handlers: stepHandlers, + }) + + expect(result).toMatchObject({ status: "balance_move_sending" }) + expect(stepHandlers.invoice_created).toHaveBeenCalledWith( + migration("invoice_created"), + ) + }) + + it("returns terminal migrations without invoking handlers", async () => { + const stepHandlers = handlers() + + const result = await executeCashWalletMigrationStep({ + migration: migration("requires_operator_review"), + handlers: stepHandlers, + }) + + expect(result).toMatchObject({ status: "requires_operator_review" }) + expect(Object.values(stepHandlers).some((handler) => handler.mock.calls.length)).toBe( + false, + ) + }) + + it("returns handler failures without trying a second checkpoint", async () => { + const error = new CouldNotUpdateError("checkpoint failed") + const stepHandlers = { + ...handlers(), + balance_move_sent: jest.fn(async () => error), + } + + const result = await executeCashWalletMigrationStep({ + migration: migration("balance_move_sent"), + handlers: stepHandlers, + }) + + expect(result).toBe(error) + expect(stepHandlers.balance_move_verified).not.toHaveBeenCalled() + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/handlers.spec.ts b/test/flash/unit/app/cash-wallet-cutover/handlers.spec.ts new file mode 100644 index 000000000..b807cd96f --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/handlers.spec.ts @@ -0,0 +1,231 @@ +import { createCashWalletMigrationStepHandlers } from "@app/cash-wallet-cutover/handlers" + +const migration = (status: CashWalletMigrationStatus): CashWalletMigration => ({ + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "legacy-usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 7, + runId: "run-7", + status, + idempotencyKey: "cash-wallet-cutover:run-7:account-id", + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), +}) + +describe("cash wallet migration step handlers", () => { + it("builds handlers for every runnable status", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async ({ to }) => migration(to)), + } + const services = { + now: jest.fn(() => new Date("2026-05-20T16:00:00Z")), + provisioningService: { + ensureDestinationWallet: jest.fn(async () => true), + }, + balanceReader: { + readSourceBalanceUsdCents: jest.fn(async () => "1234"), + readDestinationBalanceUsdtMicros: jest.fn(async () => "5000000"), + }, + invoiceService: { + createInvoice: jest.fn( + async () => + ({ + paymentRequest: "lnbc1" as EncodedPaymentRequest, + paymentHash: "hash" as PaymentHash, + }) as LnInvoice, + ), + createNoAmountInvoice: jest.fn( + async () => + ({ + paymentRequest: "lnbc1-no-amount" as EncodedPaymentRequest, + paymentHash: "noAmountHash" as PaymentHash, + }) as LnInvoice, + ), + }, + paymentService: { + payInvoice: jest.fn(async () => ({ + transactionId: "ibex-tx-id" as IbexTransactionId, + })), + }, + balanceVerifier: { + verifyBalanceMove: jest.fn(async () => true), + }, + feeService: { + readFeeAmountUsdtMicros: jest.fn(async () => "70000"), + }, + treasuryService: { + getTreasuryWalletId: jest.fn(async () => "treasury-wallet-id" as WalletId), + }, + pointerService: { + flipDefaultWallet: jest.fn(async () => ({ + previousDefaultWalletId: "legacy-usd-wallet-id" as WalletId, + })), + }, + legacyWalletVerifier: { + verifyLegacyWalletZero: jest.fn(async () => true), + }, + } + + const handlers = createCashWalletMigrationStepHandlers({ + migrationsRepo, + services, + }) + + expect(Object.keys(handlers).sort()).toEqual([ + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "balance_read", + "fee_reimbursed", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "invoice_created", + "legacy_zero_verified", + "not_started", + "pointer_flipped", + "provisioned", + "started", + ]) + + await handlers.not_started(migration("not_started")) + await handlers.started(migration("started")) + await handlers.provisioned(migration("provisioned")) + await handlers.balance_move_verified(migration("balance_move_verified")) + + expect(services.now).toHaveBeenCalled() + expect(services.provisioningService.ensureDestinationWallet).toHaveBeenCalled() + expect(services.balanceReader.readSourceBalanceUsdCents).toHaveBeenCalledWith( + migration("provisioned"), + ) + expect(services.balanceReader.readDestinationBalanceUsdtMicros).toHaveBeenCalledWith( + migration("provisioned"), + ) + expect(services.feeService.readFeeAmountUsdtMicros).toHaveBeenCalledWith( + migration("balance_move_verified"), + ) + }) + + it("skips balance move and fee reimbursement for zero-balance migrations", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async ({ to }) => migration(to)), + } + const services = { + now: jest.fn(() => new Date("2026-05-20T16:00:00Z")), + provisioningService: { + ensureDestinationWallet: jest.fn(async () => true), + }, + balanceReader: { + readSourceBalanceUsdCents: jest.fn(async () => "0"), + readDestinationBalanceUsdtMicros: jest.fn(async () => "0"), + }, + invoiceService: { + createInvoice: jest.fn(), + createNoAmountInvoice: jest.fn(), + }, + paymentService: { + payInvoice: jest.fn(), + }, + balanceVerifier: { + verifyBalanceMove: jest.fn(), + }, + feeService: { + readFeeAmountUsdtMicros: jest.fn(), + }, + treasuryService: { + getTreasuryWalletId: jest.fn(), + }, + pointerService: { + flipDefaultWallet: jest.fn(async () => ({ + previousDefaultWalletId: "legacy-usd-wallet-id" as WalletId, + })), + }, + legacyWalletVerifier: { + verifyLegacyWalletZero: jest.fn(async () => true), + }, + } + + const handlers = createCashWalletMigrationStepHandlers({ + migrationsRepo, + services, + }) + + const result = await handlers.balance_read({ + ...migration("balance_read"), + sourceBalanceUsdCents: "0", + destinationAmountUsdtMicros: "0", + }) + + expect(result).toMatchObject({ status: "pointer_flipped" }) + expect(services.pointerService.flipDefaultWallet).toHaveBeenCalledWith({ + accountId: "account-id", + destinationWalletId: "usdt-wallet-id", + }) + expect(services.invoiceService.createInvoice).not.toHaveBeenCalled() + expect(services.invoiceService.createNoAmountInvoice).not.toHaveBeenCalled() + expect(services.paymentService.payInvoice).not.toHaveBeenCalled() + expect(services.balanceVerifier.verifyBalanceMove).not.toHaveBeenCalled() + expect(services.feeService.readFeeAmountUsdtMicros).not.toHaveBeenCalled() + expect(services.treasuryService.getTreasuryWalletId).not.toHaveBeenCalled() + }) + + it("skips fee reimbursement invoice creation when the destination shortfall is zero", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async ({ to, patch }) => ({ + ...migration(to), + ...patch, + })), + } + const services = { + now: jest.fn(() => new Date("2026-05-20T16:00:00Z")), + provisioningService: { + ensureDestinationWallet: jest.fn(async () => true), + }, + balanceReader: { + readSourceBalanceUsdCents: jest.fn(async () => "1000"), + readDestinationBalanceUsdtMicros: jest.fn(async () => "0"), + }, + invoiceService: { + createInvoice: jest.fn(), + createNoAmountInvoice: jest.fn(), + }, + paymentService: { + payInvoice: jest.fn(), + }, + balanceVerifier: { + verifyBalanceMove: jest.fn(async () => true), + }, + feeService: { + readFeeAmountUsdtMicros: jest.fn(async () => "0"), + }, + treasuryService: { + getTreasuryWalletId: jest.fn(), + }, + pointerService: { + flipDefaultWallet: jest.fn(async () => ({ + previousDefaultWalletId: "legacy-usd-wallet-id" as WalletId, + })), + }, + legacyWalletVerifier: { + verifyLegacyWalletZero: jest.fn(async () => true), + }, + } + + const handlers = createCashWalletMigrationStepHandlers({ + migrationsRepo, + services, + }) + + const result = await handlers.balance_move_verified( + migration("balance_move_verified"), + ) + + expect(result).toMatchObject({ + status: "fee_reimbursed", + feeAmountUsdCents: "0", + feeAmountUsdtMicros: "0", + }) + expect(services.invoiceService.createInvoice).not.toHaveBeenCalled() + expect(services.treasuryService.getTreasuryWalletId).not.toHaveBeenCalled() + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts b/test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts new file mode 100644 index 000000000..ff578a24d --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/lifecycle.spec.ts @@ -0,0 +1,190 @@ +jest.mock("@services/mongoose", () => ({ + CashWalletCutoverRepository: jest.fn(), +})) + +import { + completePrimaryCashWalletCutover, + getPrimaryCashWalletCutoverStatus, + startPrimaryCashWalletCutover, +} from "@app/cash-wallet-cutover/lifecycle" +import { + CashWalletCutoverInProgressError, + CashWalletMigrationFailedError, + InvalidCashWalletCutoverStateTransitionError, +} from "@app/cash-wallet-cutover/errors" + +const config = (state: CashWalletCutoverState): CashWalletCutoverConfig => ({ + state, + cutoverVersion: 7, + runId: "run-7", + updatedAt: new Date("2026-05-20T00:00:00Z"), +}) + +const repo = ({ + currentConfig = config("pre"), + runnable = [], + counts = {}, +}: { + currentConfig?: CashWalletCutoverConfig + runnable?: CashWalletMigration[] + counts?: Partial> +} = {}) => ({ + getConfig: jest.fn(async () => currentConfig), + updateConfig: jest.fn(async (patch: Partial) => ({ + ...currentConfig, + ...patch, + })), + listRunnableMigrations: jest.fn(async () => runnable), + countByStatus: jest.fn( + async ({ status }: { status: CashWalletMigrationStatus }) => counts[status] ?? 0, + ), +}) + +describe("cash wallet cutover lifecycle", () => { + const now = new Date("2026-05-20T12:00:00Z") + + it("starts a prepared cutover run", async () => { + const migrationsRepo = repo() + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + actor: "operator", + now, + migrationsRepo, + }) + + expect(migrationsRepo.updateConfig).toHaveBeenCalledWith( + expect.objectContaining({ + state: "in_progress", + cutoverVersion: 7, + runId: "run-7", + startedAt: now, + }), + "operator", + ) + expect(result).toMatchObject({ state: "in_progress", runId: "run-7" }) + }) + + it("is idempotent for the active cutover run", async () => { + const migrationsRepo = repo({ currentConfig: config("in_progress") }) + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + actor: "operator", + now, + migrationsRepo, + }) + + expect(migrationsRepo.updateConfig).not.toHaveBeenCalled() + expect(result).toEqual(config("in_progress")) + }) + + it("rejects starting a different run while one is active", async () => { + const migrationsRepo = repo({ currentConfig: config("in_progress") }) + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion: 8, + runId: "run-8", + actor: "operator", + now, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(CashWalletCutoverInProgressError) + }) + + it("rejects restarting a completed cutover", async () => { + const migrationsRepo = repo({ currentConfig: config("complete") }) + + const result = await startPrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + actor: "operator", + now, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(InvalidCashWalletCutoverStateTransitionError) + }) + + it("refuses completion while runnable migrations remain", async () => { + const migrationsRepo = repo({ + currentConfig: config("in_progress"), + runnable: [{ id: "migration-id", status: "started" } as CashWalletMigration], + }) + + const result = await completePrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + actor: "operator", + now, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(CashWalletCutoverInProgressError) + expect(migrationsRepo.updateConfig).not.toHaveBeenCalled() + }) + + it("refuses completion when failed migrations exist", async () => { + const migrationsRepo = repo({ + currentConfig: config("in_progress"), + counts: { failed: 1 }, + }) + + const result = await completePrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + actor: "operator", + now, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(CashWalletMigrationFailedError) + }) + + it("marks cutover complete after all migrations are terminal-success", async () => { + const migrationsRepo = repo({ currentConfig: config("in_progress") }) + + const result = await completePrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + actor: "operator", + now, + migrationsRepo, + }) + + expect(migrationsRepo.updateConfig).toHaveBeenCalledWith( + expect.objectContaining({ + state: "complete", + cutoverVersion: 7, + runId: "run-7", + completedAt: now, + }), + "operator", + ) + expect(result).toMatchObject({ state: "complete" }) + }) + + it("returns non-zero migration counts for status checks", async () => { + const migrationsRepo = repo({ + currentConfig: config("in_progress"), + counts: { complete: 10, failed: 1 }, + }) + + const result = await getPrimaryCashWalletCutoverStatus({ + cutoverVersion: 7, + runId: "run-7", + migrationsRepo, + }) + + expect(result).toEqual({ + config: config("in_progress"), + countsByStatus: { + complete: 10, + failed: 1, + }, + }) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/migration-records.spec.ts b/test/flash/unit/app/cash-wallet-cutover/migration-records.spec.ts new file mode 100644 index 000000000..331f0dbb4 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/migration-records.spec.ts @@ -0,0 +1,70 @@ +import { RepositoryError } from "@domain/errors" + +import { upsertPrimaryCashWalletMigrationRecords } from "@app/cash-wallet-cutover/migration-records" + +const plan = (accountId: AccountId): PrimaryCashWalletMigrationPlan => ({ + accountId, + accountUuid: `${accountId}-uuid` as AccountUuid, + legacyUsdWalletId: `${accountId}-usd` as WalletId, + destinationUsdtWalletId: `${accountId}-usdt` as WalletId, + previousDefaultWalletId: `${accountId}-default` as WalletId, + cutoverVersion: 5, + runId: "run-5", + idempotencyKey: `cash-wallet-cutover:run-5:${accountId}`, +}) + +describe("cash wallet migration record upsert", () => { + it("upserts one not-started migration record for each primary plan", async () => { + const migrationsRepo = { + upsertMigration: jest.fn(async (args) => ({ + id: `${args.accountId}-migration`, + ...args, + status: "not_started" as CashWalletMigrationStatus, + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), + })), + } + + const result = await upsertPrimaryCashWalletMigrationRecords({ + migrationsRepo, + plans: [plan("account-1" as AccountId), plan("account-2" as AccountId)], + }) + + expect(result).toEqual([ + expect.objectContaining({ id: "account-1-migration", accountId: "account-1" }), + expect.objectContaining({ id: "account-2-migration", accountId: "account-2" }), + ]) + expect(migrationsRepo.upsertMigration).toHaveBeenCalledTimes(2) + expect(migrationsRepo.upsertMigration).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + accountId: "account-1", + accountUuid: "account-1-uuid", + legacyUsdWalletId: "account-1-usd", + destinationUsdtWalletId: "account-1-usdt", + previousDefaultWalletId: "account-1-default", + cutoverVersion: 5, + runId: "run-5", + idempotencyKey: "cash-wallet-cutover:run-5:account-1", + }), + ) + }) + + it("returns repository errors and stops creating more records", async () => { + const error = new RepositoryError("could not upsert migration") + const migrationsRepo = { + upsertMigration: jest + .fn() + .mockResolvedValueOnce(error) + .mockResolvedValueOnce({} as CashWalletMigration), + } + + const result = await upsertPrimaryCashWalletMigrationRecords({ + migrationsRepo, + plans: [plan("account-1" as AccountId), plan("account-2" as AccountId)], + }) + + expect(result).toBe(error) + expect(migrationsRepo.upsertMigration).toHaveBeenCalledTimes(1) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/migration-state-machine.spec.ts b/test/flash/unit/app/cash-wallet-cutover/migration-state-machine.spec.ts new file mode 100644 index 000000000..316fa2443 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/migration-state-machine.spec.ts @@ -0,0 +1,55 @@ +import { + assertCanTransition, + nextResumeStatus, +} from "@app/cash-wallet-cutover/state-machine" + +describe("cash wallet cutover migration state machine", () => { + it("allows the happy-path checkpoint order", () => { + const statuses = [ + "not_started", + "started", + "provisioned", + "balance_read", + "invoice_created", + "balance_move_sending", + "balance_move_sent", + "balance_move_verified", + "fee_reimbursement_invoice_created", + "fee_reimbursement_sending", + "fee_reimbursed", + "pointer_flipped", + "legacy_zero_verified", + "complete", + ] as const + + for (let i = 0; i < statuses.length - 1; i++) { + expect(assertCanTransition(statuses[i], statuses[i + 1])).toBe(true) + } + }) + + it("rejects pointer flip before fee reimbursement", () => { + expect( + assertCanTransition("balance_move_verified", "pointer_flipped"), + ).toBeInstanceOf(Error) + }) + + it("allows skipping fee reimbursement when there is no shortfall", () => { + expect(assertCanTransition("balance_move_verified", "fee_reimbursed")).toBe(true) + }) + + it("resumes from stored checkpoint without repeating completed side effects", () => { + expect(nextResumeStatus("invoice_created")).toBe("invoice_created") + expect(nextResumeStatus("balance_move_sent")).toBe("balance_move_sent") + expect(nextResumeStatus("fee_reimbursement_invoice_created")).toBe( + "fee_reimbursement_invoice_created", + ) + }) + + it("does not progress terminal/manual-review states without override", () => { + expect(assertCanTransition("complete", "started")).toBeInstanceOf(Error) + expect(assertCanTransition("failed", "started")).toBeInstanceOf(Error) + expect(assertCanTransition("requires_operator_review", "started")).toBeInstanceOf( + Error, + ) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/orchestrator.spec.ts b/test/flash/unit/app/cash-wallet-cutover/orchestrator.spec.ts new file mode 100644 index 000000000..32811fb58 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/orchestrator.spec.ts @@ -0,0 +1,84 @@ +jest.mock("@app/accounts", () => ({ + addWalletIfNonexistent: jest.fn(), + updateDefaultWalletId: jest.fn(), +})) +jest.mock("@app/wallets", () => ({ + addInvoiceForRecipientForUsdWallet: jest.fn(), + addInvoiceNoAmountForRecipient: jest.fn(), + getBalanceForWallet: jest.fn(), +})) +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(() => ({ findById: jest.fn() })), + CashWalletCutoverRepository: jest.fn(), +})) +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + payInvoice: jest.fn(), + getTransactionDetails: jest.fn(), + }, +})) + +import { runPrimaryCashWalletCutoverBatch } from "@app/cash-wallet-cutover/orchestrator" + +const migration = (status: CashWalletMigrationStatus): CashWalletMigration => ({ + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "legacy-usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 7, + runId: "run-7", + status, + idempotencyKey: "cash-wallet-cutover:run-7:account-id", + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), +}) + +describe("primary cash wallet cutover orchestrator", () => { + it("runs a locked batch with default step handlers", async () => { + const started = migration("not_started") + const locked = migration("not_started") + const migrationsRepo = { + transitionMigration: jest.fn(async () => migration("started")), + listRunnableMigrations: jest.fn(async () => [started]), + acquireMigrationLock: jest.fn(async () => locked), + markMigrationFailed: jest.fn(), + releaseMigrationLock: jest.fn(async () => locked), + } + const runtimeServices = { + now: jest.fn(() => new Date("2026-05-20T16:00:00Z")), + provisioningService: { ensureDestinationWallet: jest.fn() }, + balanceReader: { + readSourceBalanceUsdCents: jest.fn(), + readDestinationBalanceUsdtMicros: jest.fn(), + }, + invoiceService: { createInvoice: jest.fn(), createNoAmountInvoice: jest.fn() }, + paymentService: { payInvoice: jest.fn() }, + balanceVerifier: { verifyBalanceMove: jest.fn() }, + feeService: { readFeeAmountUsdtMicros: jest.fn() }, + treasuryService: { getTreasuryWalletId: jest.fn() }, + pointerService: { flipDefaultWallet: jest.fn() }, + legacyWalletVerifier: { verifyLegacyWalletZero: jest.fn() }, + } + + const result = await runPrimaryCashWalletCutoverBatch({ + cutoverVersion: 7, + runId: "run-7", + workerId: "worker-1", + limit: 5, + lockStaleBefore: new Date("2026-05-20T15:00:00Z"), + migrationsRepo, + runtimeServices, + }) + + expect(result).toEqual({ attempted: 1, advanced: 1, failed: 0, skipped: 0 }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "not_started", + to: "started", + cutoverVersion: 7, + runId: "run-7", + patch: { startedAt: new Date("2026-05-20T16:00:00Z") }, + }) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/planner.spec.ts b/test/flash/unit/app/cash-wallet-cutover/planner.spec.ts new file mode 100644 index 000000000..61f492a1b --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/planner.spec.ts @@ -0,0 +1,51 @@ +import { buildPrimaryCashWalletMigrationPlan } from "@app/cash-wallet-cutover/planner" + +const discovery = ( + status: CashWalletCutoverDiscoveryStatus, + accountId: AccountId, +): CashWalletCutoverDiscovery => ({ + status, + accountId, + accountUuid: `${accountId}-uuid` as AccountUuid, + legacyUsdWalletId: `${accountId}-usd` as WalletId, + destinationUsdtWalletId: `${accountId}-usdt` as WalletId, + previousDefaultWalletId: `${accountId}-default` as WalletId, +}) + +describe("primary cash wallet migration planner", () => { + it("creates deterministic migration plans for legacy-default accounts only", () => { + const result = buildPrimaryCashWalletMigrationPlan({ + cutoverVersion: 4, + runId: "run-4", + discoveries: [ + discovery("legacy_default", "account-1" as AccountId), + discovery("already_usdt", "account-2" as AccountId), + discovery("residual_legacy_usd", "account-3" as AccountId), + discovery("legacy_default", "account-4" as AccountId), + ], + }) + + expect(result).toEqual([ + { + accountId: "account-1", + accountUuid: "account-1-uuid", + legacyUsdWalletId: "account-1-usd", + destinationUsdtWalletId: "account-1-usdt", + previousDefaultWalletId: "account-1-default", + cutoverVersion: 4, + runId: "run-4", + idempotencyKey: "cash-wallet-cutover:run-4:account-1", + }, + { + accountId: "account-4", + accountUuid: "account-4-uuid", + legacyUsdWalletId: "account-4-usd", + destinationUsdtWalletId: "account-4-usdt", + previousDefaultWalletId: "account-4-default", + cutoverVersion: 4, + runId: "run-4", + idempotencyKey: "cash-wallet-cutover:run-4:account-4", + }, + ]) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/preflight.spec.ts b/test/flash/unit/app/cash-wallet-cutover/preflight.spec.ts new file mode 100644 index 000000000..66a1a5aad --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/preflight.spec.ts @@ -0,0 +1,65 @@ +import { buildCashWalletCutoverPreflightReport } from "@app/cash-wallet-cutover/preflight" + +const discovery = ( + status: CashWalletCutoverDiscoveryStatus, + accountId = `${status}-account` as AccountId, +): CashWalletCutoverDiscovery => ({ + status, + accountId, + accountUuid: `${accountId}-uuid` as AccountUuid, + legacyUsdWalletId: + status === "missing_legacy_usd" ? undefined : (`${accountId}-usd` as WalletId), + destinationUsdtWalletId: + status === "missing_destination_usdt" ? undefined : (`${accountId}-usdt` as WalletId), + previousDefaultWalletId: `${accountId}-default` as WalletId, +}) + +describe("cash wallet cutover preflight report", () => { + it("counts migration candidates and non-migrating classifications", () => { + const report = buildCashWalletCutoverPreflightReport({ + cutoverVersion: 3, + runId: "run-3", + discoveries: [ + discovery("legacy_default", "legacy-1" as AccountId), + discovery("legacy_default", "legacy-2" as AccountId), + discovery("already_usdt"), + discovery("residual_legacy_usd"), + discovery("missing_legacy_usd"), + discovery("missing_destination_usdt"), + ], + }) + + expect(report).toMatchObject({ + cutoverVersion: 3, + runId: "run-3", + totalAccounts: 6, + migrationCandidates: 2, + alreadyUsdt: 1, + residualLegacyUsd: 1, + blockers: 2, + canStart: false, + }) + expect(report.blockerAccounts).toEqual([ + { accountId: "missing_legacy_usd-account", reason: "missing_legacy_usd" }, + { + accountId: "missing_destination_usdt-account", + reason: "missing_destination_usdt", + }, + ]) + }) + + it("allows start when every account is either migratable, already migrated, or residual", () => { + const report = buildCashWalletCutoverPreflightReport({ + cutoverVersion: 3, + runId: "run-3", + discoveries: [ + discovery("legacy_default"), + discovery("already_usdt"), + discovery("residual_legacy_usd"), + ], + }) + + expect(report.canStart).toBe(true) + expect(report.blockerAccounts).toEqual([]) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/prepare.spec.ts b/test/flash/unit/app/cash-wallet-cutover/prepare.spec.ts new file mode 100644 index 000000000..137c373dc --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/prepare.spec.ts @@ -0,0 +1,123 @@ +import { RepositoryError } from "@domain/errors" + +import { preparePrimaryCashWalletCutover } from "@app/cash-wallet-cutover/prepare" + +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const account = (id: AccountId, defaultWalletId: WalletId): Account => + ({ + id, + uuid: `${id}-uuid` as AccountUuid, + defaultWalletId, + }) as Account + +const wallet = (accountId: AccountId, id: WalletId, currency: WalletCurrency): Wallet => + ({ + id, + accountId, + type: WalletType.Checking, + currency, + onChainAddressIdentifiers: [], + onChainAddresses: () => [], + lnurlp: "lnurl" as Lnurl, + }) as Wallet + +async function* unlockedAccounts(accounts: Account[]): AsyncGenerator { + for (const account of accounts) yield account +} + +describe("prepare primary cash wallet cutover", () => { + it("discovers accounts, builds preflight, and upserts primary migration records", async () => { + const accountOne = account("account-1" as AccountId, "account-1-usd" as WalletId) + const accountTwo = account("account-2" as AccountId, "account-2-usdt" as WalletId) + const walletsRepo = { + listByAccountId: jest.fn(async (accountId: AccountId) => [ + wallet(accountId, `${accountId}-usd` as WalletId, WalletCurrency.Usd), + wallet(accountId, `${accountId}-usdt` as WalletId, WalletCurrency.Usdt), + ]), + } + const migrationsRepo = { + upsertMigration: jest.fn(async (plan: PrimaryCashWalletMigrationPlan) => ({ + id: `${plan.accountId}-migration`, + ...plan, + status: "not_started" as CashWalletMigrationStatus, + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), + })), + } + + const result = await preparePrimaryCashWalletCutover({ + cutoverVersion: 6, + runId: "run-6", + accountsRepo: { + listUnlockedAccounts: () => unlockedAccounts([accountOne, accountTwo]), + }, + walletsRepo, + migrationsRepo, + }) + + expect(result).toMatchObject({ + report: { + totalAccounts: 2, + migrationCandidates: 1, + alreadyUsdt: 1, + blockers: 0, + canStart: true, + }, + plannedMigrations: [ + expect.objectContaining({ + accountId: "account-1", + idempotencyKey: "cash-wallet-cutover:run-6:account-1", + }), + ], + migrations: [expect.objectContaining({ id: "account-1-migration" })], + }) + expect(migrationsRepo.upsertMigration).toHaveBeenCalledTimes(1) + }) + + it("does not create migration records when preflight has blockers", async () => { + const blockedAccount = account("account-1" as AccountId, "account-1-usd" as WalletId) + const migrationsRepo = { + upsertMigration: jest.fn(), + } + + const result = await preparePrimaryCashWalletCutover({ + cutoverVersion: 6, + runId: "run-6", + accountsRepo: { listUnlockedAccounts: () => unlockedAccounts([blockedAccount]) }, + walletsRepo: { + listByAccountId: jest.fn(async (accountId: AccountId) => [ + wallet(accountId, `${accountId}-usd` as WalletId, WalletCurrency.Usd), + ]), + }, + migrationsRepo, + }) + + expect(result).toMatchObject({ + report: { + blockers: 1, + canStart: false, + }, + plannedMigrations: [], + migrations: [], + }) + expect(migrationsRepo.upsertMigration).not.toHaveBeenCalled() + }) + + it("returns repository errors from discovery", async () => { + const error = new RepositoryError("wallet lookup failed") + const result = await preparePrimaryCashWalletCutover({ + cutoverVersion: 6, + runId: "run-6", + accountsRepo: { + listUnlockedAccounts: () => + unlockedAccounts([account("account-1" as AccountId, "wallet-id" as WalletId)]), + }, + walletsRepo: { listByAccountId: jest.fn(async () => error) }, + migrationsRepo: { upsertMigration: jest.fn() }, + }) + + expect(result).toBe(error) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/presentation-for-account.spec.ts b/test/flash/unit/app/cash-wallet-cutover/presentation-for-account.spec.ts new file mode 100644 index 000000000..7e9557733 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/presentation-for-account.spec.ts @@ -0,0 +1,135 @@ +jest.mock("@services/mongoose", () => ({ + CashWalletCutoverRepository: jest.fn(), + WalletsRepository: jest.fn(), +})) + +import { + resolveCashWalletMutationWalletIdForAccount, + resolveCashWalletPresentationForAccount, +} from "@app/cash-wallet-cutover/presentation-for-account" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const account = { id: "account-id", defaultWalletId: "legacy-usd-wallet-id" } as Account + +const wallet = ({ id, currency }: { id: string; currency: WalletCurrency }): Wallet => + ({ + id, + accountId: account.id, + type: WalletType.Checking, + currency, + }) as Wallet + +const legacyUsdWallet = wallet({ + id: "legacy-usd-wallet-id", + currency: WalletCurrency.Usd, +}) +const usdtWallet = wallet({ + id: "usdt-wallet-id", + currency: WalletCurrency.Usdt, +}) + +const config = (state: CashWalletCutoverState): CashWalletCutoverConfig => ({ + state, + cutoverVersion: 2, + runId: "run-2", + updatedAt: new Date("2026-05-19T00:00:00Z"), +}) + +const migration = (status: CashWalletMigrationStatus): CashWalletMigration => ({ + id: "migration-id", + accountId: account.id, + legacyUsdWalletId: legacyUsdWallet.id, + destinationUsdtWalletId: usdtWallet.id, + cutoverVersion: 2, + runId: "run-2", + status, + idempotencyKey: "run-2:account-id", + attempts: 0, + updatedAt: new Date("2026-05-19T00:00:00Z"), +}) + +describe("cash wallet presentation for account", () => { + it("uses existing migration lookup and presents old clients as legacy-compatible after migration", async () => { + const migrationsRepo = { + getConfig: jest.fn(async () => config("in_progress")), + findMigrationByAccountId: jest.fn(async () => migration("complete")), + } + const walletsRepo = { + listByAccountId: jest.fn(async () => [legacyUsdWallet, usdtWallet]), + } + + const result = await resolveCashWalletPresentationForAccount({ + account, + client: { + cashWalletPresentation: "legacy_compat", + hasUsdtCashWalletSupport: false, + }, + migrationsRepo, + walletsRepo, + }) + + expect(result).toEqual({ + wallets: [legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + }) + expect(migrationsRepo.findMigrationByAccountId).toHaveBeenCalledWith({ + accountId: account.id, + cutoverVersion: 2, + runId: "run-2", + }) + }) + + it("does not require migration lookup after global completion", async () => { + const migrationsRepo = { + getConfig: jest.fn(async () => config("complete")), + findMigrationByAccountId: jest.fn(), + } + const walletsRepo = { + listByAccountId: jest.fn(async () => [legacyUsdWallet, usdtWallet]), + } + + const result = await resolveCashWalletPresentationForAccount({ + account, + client: { + cashWalletPresentation: "usdt", + hasUsdtCashWalletSupport: true, + }, + migrationsRepo, + walletsRepo, + }) + + expect(result).toEqual({ + wallets: [usdtWallet], + defaultWalletId: usdtWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + }) + expect(migrationsRepo.findMigrationByAccountId).not.toHaveBeenCalled() + }) + + it("routes old-client legacy USD mutation wallet ids to the active settlement wallet", async () => { + const migrationsRepo = { + getConfig: jest.fn(async () => config("in_progress")), + findMigrationByAccountId: jest.fn(async () => migration("complete")), + } + const walletsRepo = { + listByAccountId: jest.fn(async () => [legacyUsdWallet, usdtWallet]), + } + + const result = await resolveCashWalletMutationWalletIdForAccount({ + account, + walletId: legacyUsdWallet.id, + client: { + cashWalletPresentation: "legacy_compat", + hasUsdtCashWalletSupport: false, + }, + migrationsRepo, + walletsRepo, + }) + + expect(result).toBe(usdtWallet.id) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts b/test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts new file mode 100644 index 000000000..8eb42fdc1 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/presentation.spec.ts @@ -0,0 +1,120 @@ +import { + CashWalletMissingLegacyUsdWalletError, + CashWalletMissingUsdtWalletError, +} from "@app/cash-wallet-cutover/errors" +import { + cashWalletTransactionWalletIdsForPresentation, + resolveCashWalletPresentation, +} from "@app/cash-wallet-cutover/presentation" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const wallet = ({ id, currency }: { id: string; currency: WalletCurrency }): Wallet => + ({ + id, + accountId: "account-id", + type: WalletType.Checking, + currency, + }) as Wallet + +const legacyUsdWallet = wallet({ + id: "legacy-usd-wallet-id", + currency: WalletCurrency.Usd, +}) +const usdtWallet = wallet({ + id: "usdt-wallet-id", + currency: WalletCurrency.Usdt, +}) +const btcWallet = wallet({ + id: "btc-wallet-id", + currency: WalletCurrency.Btc, +}) + +describe("cash wallet presentation resolver", () => { + it("returns the legacy USD wallet as the active settlement wallet before cutover", () => { + expect( + resolveCashWalletPresentation({ + decision: { presentation: "legacy_usd" }, + wallets: [btcWallet, legacyUsdWallet, usdtWallet], + }), + ).toEqual({ + wallets: [btcWallet, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: legacyUsdWallet, + }) + }) + + it("presents legacy USD while routing settlement to USDT for old clients after migration", () => { + expect( + resolveCashWalletPresentation({ + decision: { presentation: "legacy_usd_compat" }, + wallets: [btcWallet, legacyUsdWallet, usdtWallet], + }), + ).toEqual({ + wallets: [btcWallet, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + }) + }) + + it("presents the USDT wallet directly for capable clients", () => { + expect( + resolveCashWalletPresentation({ + decision: { presentation: "usdt" }, + wallets: [btcWallet, legacyUsdWallet, usdtWallet], + }), + ).toEqual({ + wallets: [btcWallet, usdtWallet], + defaultWalletId: usdtWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + }) + }) + + it("returns cutover-state errors for missing presentation wallets", () => { + expect( + resolveCashWalletPresentation({ + decision: { presentation: "legacy_usd" }, + wallets: [usdtWallet], + }), + ).toBeInstanceOf(CashWalletMissingLegacyUsdWalletError) + + expect( + resolveCashWalletPresentation({ + decision: { presentation: "legacy_usd_compat" }, + wallets: [legacyUsdWallet], + }), + ).toBeInstanceOf(CashWalletMissingUsdtWalletError) + }) +}) + +describe("cash wallet transaction wallet ids for presentation", () => { + it("uses the active settlement wallet when defaulting legacy-compatible history", () => { + expect( + cashWalletTransactionWalletIdsForPresentation({ + presentation: { + wallets: [btcWallet, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + }, + }), + ).toEqual([btcWallet.id, usdtWallet.id]) + }) + + it("remaps explicit legacy USD wallet ids to active settlement wallet ids", () => { + expect( + cashWalletTransactionWalletIdsForPresentation({ + walletIds: [legacyUsdWallet.id], + presentation: { + wallets: [btcWallet, legacyUsdWallet], + defaultWalletId: legacyUsdWallet.id, + legacyUsdWallet, + activeSettlementWallet: usdtWallet, + }, + }), + ).toEqual([usdtWallet.id]) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/preview.spec.ts b/test/flash/unit/app/cash-wallet-cutover/preview.spec.ts new file mode 100644 index 000000000..90bcc5045 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/preview.spec.ts @@ -0,0 +1,81 @@ +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(), + WalletsRepository: jest.fn(), +})) + +import { previewPrimaryCashWalletCutover } from "@app/cash-wallet-cutover/preview" +import { WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +const account = ({ + id, + defaultWalletId, +}: { + id: string + defaultWalletId: string +}): Account => + ({ + id, + uuid: `${id}-uuid`, + defaultWalletId, + }) as Account + +const wallet = ({ id, currency }: { id: string; currency: WalletCurrency }): Wallet => + ({ + id, + type: WalletType.Checking, + currency, + }) as Wallet + +describe("preview primary cash wallet cutover", () => { + it("builds the preflight report and plan without repository writes", async () => { + const accounts = [ + account({ id: "account-1", defaultWalletId: "usd-1" }), + account({ id: "account-2", defaultWalletId: "usdt-2" }), + ] + + const accountsRepo = { + listUnlockedAccounts: function* () { + yield* accounts + }, + } + const walletsRepo = { + listByAccountId: jest.fn(async (accountId: AccountId) => { + if (accountId === "account-1") { + return [ + wallet({ id: "usd-1", currency: WalletCurrency.Usd }), + wallet({ id: "usdt-1", currency: WalletCurrency.Usdt }), + ] + } + + return [ + wallet({ id: "usd-2", currency: WalletCurrency.Usd }), + wallet({ id: "usdt-2", currency: WalletCurrency.Usdt }), + ] + }), + } + + const result = await previewPrimaryCashWalletCutover({ + cutoverVersion: 7, + runId: "run-7", + accountsRepo, + walletsRepo, + }) + + expect(result).toEqual({ + report: expect.objectContaining({ + totalAccounts: 2, + migrationCandidates: 1, + alreadyUsdt: 1, + canStart: true, + }), + plannedMigrations: [ + expect.objectContaining({ + accountId: "account-1", + legacyUsdWalletId: "usd-1", + destinationUsdtWalletId: "usdt-1", + }), + ], + }) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/runner.spec.ts b/test/flash/unit/app/cash-wallet-cutover/runner.spec.ts new file mode 100644 index 000000000..6987c3873 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/runner.spec.ts @@ -0,0 +1,126 @@ +import { CouldNotUpdateError } from "@domain/errors" + +import { runCashWalletMigrationBatch } from "@app/cash-wallet-cutover/runner" + +const migration = ( + status: CashWalletMigrationStatus, + id = `${status}-migration`, +): CashWalletMigration => ({ + id, + accountId: `${id}-account` as AccountId, + legacyUsdWalletId: `${id}-legacy-usd-wallet` as WalletId, + destinationUsdtWalletId: `${id}-usdt-wallet` as WalletId, + cutoverVersion: 7, + runId: "run-7", + status, + idempotencyKey: `cash-wallet-cutover:run-7:${id}-account`, + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), +}) + +describe("cash wallet migration batch runner", () => { + it("locks each runnable migration, executes one step, and releases the lock", async () => { + const runnable = [migration("not_started", "migration-1")] + const locked = { ...runnable[0], lockedBy: "worker-1" } + const completedStep = migration("started", "migration-1") + const migrationsRepo = { + listRunnableMigrations: jest.fn(async () => runnable), + acquireMigrationLock: jest.fn(async () => locked), + markMigrationFailed: jest.fn(), + releaseMigrationLock: jest.fn(async () => locked), + } + const executor = jest.fn(async () => completedStep) + + const result = await runCashWalletMigrationBatch({ + cutoverVersion: 7, + runId: "run-7", + workerId: "worker-1", + limit: 10, + lockStaleBefore: new Date("2026-05-20T15:00:00Z"), + migrationsRepo, + executor, + }) + + expect(result).toEqual({ attempted: 1, advanced: 1, failed: 0, skipped: 0 }) + expect(migrationsRepo.listRunnableMigrations).toHaveBeenCalledWith({ + cutoverVersion: 7, + runId: "run-7", + limit: 10, + }) + expect(migrationsRepo.acquireMigrationLock).toHaveBeenCalledWith({ + id: "migration-1", + workerId: "worker-1", + staleBefore: new Date("2026-05-20T15:00:00Z"), + cutoverVersion: 7, + runId: "run-7", + }) + expect(executor).toHaveBeenCalledWith(locked) + expect(migrationsRepo.releaseMigrationLock).toHaveBeenCalledWith({ + id: "migration-1", + workerId: "worker-1", + cutoverVersion: 7, + runId: "run-7", + }) + }) + + it("skips migrations that cannot be locked", async () => { + const lockError = new CouldNotUpdateError("lock unavailable") + const migrationsRepo = { + listRunnableMigrations: jest.fn(async () => [migration("started", "migration-1")]), + acquireMigrationLock: jest.fn(async () => lockError), + markMigrationFailed: jest.fn(), + releaseMigrationLock: jest.fn(), + } + const executor = jest.fn() + + const result = await runCashWalletMigrationBatch({ + cutoverVersion: 7, + runId: "run-7", + workerId: "worker-1", + limit: 10, + lockStaleBefore: new Date("2026-05-20T15:00:00Z"), + migrationsRepo, + executor, + }) + + expect(result).toEqual({ attempted: 1, advanced: 0, failed: 0, skipped: 1 }) + expect(executor).not.toHaveBeenCalled() + expect(migrationsRepo.releaseMigrationLock).not.toHaveBeenCalled() + }) + + it("releases the lock when execution fails", async () => { + const locked = migration("balance_move_sent", "migration-1") + const executionError = new CouldNotUpdateError("execution failed") + const migrationsRepo = { + listRunnableMigrations: jest.fn(async () => [locked]), + acquireMigrationLock: jest.fn(async () => locked), + markMigrationFailed: jest.fn(async () => ({ + ...locked, + status: "requires_operator_review" as const, + })), + releaseMigrationLock: jest.fn(async () => locked), + } + const executor = jest.fn(async () => executionError) + + const result = await runCashWalletMigrationBatch({ + cutoverVersion: 7, + runId: "run-7", + workerId: "worker-1", + limit: 10, + lockStaleBefore: new Date("2026-05-20T15:00:00Z"), + migrationsRepo, + executor, + }) + + expect(result).toEqual({ attempted: 1, advanced: 0, failed: 1, skipped: 0 }) + expect(migrationsRepo.markMigrationFailed).toHaveBeenCalledWith({ + id: "migration-1", + workerId: "worker-1", + cutoverVersion: 7, + runId: "run-7", + error: executionError, + status: "requires_operator_review", + }) + expect(migrationsRepo.releaseMigrationLock).not.toHaveBeenCalled() + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/runtime-services.spec.ts b/test/flash/unit/app/cash-wallet-cutover/runtime-services.spec.ts new file mode 100644 index 000000000..45e793e60 --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/runtime-services.spec.ts @@ -0,0 +1,222 @@ +import { CouldNotUpdateError } from "@domain/errors" +import { USDAmount, USDTAmount, WalletCurrency } from "@domain/shared" +import { WalletType } from "@domain/wallets" + +jest.mock("@app/accounts", () => ({ + addWalletIfNonexistent: jest.fn(), + updateDefaultWalletId: jest.fn(), +})) +jest.mock("@app/wallets", () => ({ + addInvoiceForRecipientForUsdWallet: jest.fn(), + addInvoiceNoAmountForRecipient: jest.fn(), + getBalanceForWallet: jest.fn(), +})) +jest.mock("@services/mongoose", () => ({ + AccountsRepository: jest.fn(() => ({ findById: jest.fn() })), +})) +jest.mock("@services/ibex/client", () => ({ + __esModule: true, + default: { + addInvoice: jest.fn(), + payInvoice: jest.fn(), + getTransactionDetails: jest.fn(), + }, +})) + +import { createCashWalletMigrationRuntimeServices } from "@app/cash-wallet-cutover/runtime-services" +import Ibex from "@services/ibex/client" + +const ibexAddInvoiceResponse = { + invoice: { + bolt11: + "lnbc140n1p3k6yzupp53p305l6de6s9xw2j0qaa59pl7lahys4f2uavwncll9z2vq0syvvsdqqcqzpgxqzuysp5mdgsaa734eg7srwx92rsn3hyc4xzt5tphfpadl5c6fanhppwaz4s9qyyssqm6yhnnhl8jltwjtclzk4g7nxr99ycsp4sqd6vksevqh06h8l3gm5fdhtl59t6g3fsalv26sj5zvwhxwlghc9wcfgkrjrtuh4873ejnspc5xksy", + }, +} + +const migration = (patch: Partial = {}): CashWalletMigration => ({ + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "legacy-usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 7, + runId: "run-7", + status: "balance_move_verified", + idempotencyKey: "cash-wallet-cutover:run-7:account-id", + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), + ...patch, +}) + +describe("cash wallet migration runtime services", () => { + it("reads source USD balances as cents", async () => { + const deps = { + getBalanceForWallet: jest.fn(async () => USDAmount.cents("1234")), + } + + const services = createCashWalletMigrationRuntimeServices(deps) + + const result = await services.balanceReader.readSourceBalanceUsdCents(migration()) + + expect(result).toBe("1234") + expect(deps.getBalanceForWallet).toHaveBeenCalledWith({ + walletId: "legacy-usd-wallet-id", + currency: WalletCurrency.Usd, + }) + }) + + it("reads destination USDT balances as micros", async () => { + const deps = { + getBalanceForWallet: jest.fn(async () => USDTAmount.smallestUnits("5000000")), + } + + const services = createCashWalletMigrationRuntimeServices(deps) + + const result = + await services.balanceReader.readDestinationBalanceUsdtMicros(migration()) + + expect(result).toBe("5000000") + expect(deps.getBalanceForWallet).toHaveBeenCalledWith({ + walletId: "usdt-wallet-id", + currency: WalletCurrency.Usdt, + }) + }) + + it("ensures the expected destination USDT wallet exists", async () => { + const deps = { + addWalletIfNonexistent: jest.fn(async () => ({ + id: "usdt-wallet-id" as WalletId, + })), + } + + const services = createCashWalletMigrationRuntimeServices(deps) + + const result = await services.provisioningService.ensureDestinationWallet({ + accountId: "account-id" as AccountId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + }) + + expect(result).toBe(true) + expect(deps.addWalletIfNonexistent).toHaveBeenCalledWith({ + accountId: "account-id", + type: WalletType.Checking, + currency: WalletCurrency.Usdt, + }) + }) + + it("creates no-amount destination invoices through IBEX", async () => { + jest.mocked(Ibex.addInvoice).mockResolvedValue(ibexAddInvoiceResponse as never) + + const services = createCashWalletMigrationRuntimeServices() + + const result = await services.invoiceService.createNoAmountInvoice({ + recipientWalletId: "usdt-wallet-id" as WalletId, + memo: "cash-wallet-cutover:run-7:migration-id:balance-move", + }) + + expect(result).toMatchObject({ + paymentRequest: ibexAddInvoiceResponse.invoice.bolt11, + }) + expect(Ibex.addInvoice).toHaveBeenCalledWith({ + accountId: "usdt-wallet-id", + memo: "cash-wallet-cutover:run-7:migration-id:balance-move", + expiration: 900, + }) + }) + + it("extracts the IBEX transaction id after paying an invoice with a sender-side USD cap", async () => { + const deps = { + payInvoice: jest.fn(async () => ({ + transaction: { id: "ibex-tx-id" }, + })), + } + + const services = createCashWalletMigrationRuntimeServices(deps) + + const result = await services.paymentService.payInvoice({ + senderWalletId: "legacy-usd-wallet-id" as WalletId, + paymentRequest: "lnbc1payment", + senderAmountUsdCents: "1000", + }) + + expect(result).toEqual({ transactionId: "ibex-tx-id" }) + const paymentArgs = deps.payInvoice.mock.calls[0][0]! + expect(paymentArgs.accountId).toBe("legacy-usd-wallet-id") + expect(paymentArgs.invoice).toBe("lnbc1payment") + expect(paymentArgs.send).toBeInstanceOf(USDAmount) + expect(paymentArgs.send.asCents()).toBe("1000") + }) + + it("returns an error when IBEX payment response has no transaction id", async () => { + const services = createCashWalletMigrationRuntimeServices({ + payInvoice: jest.fn(async () => ({})), + }) + + const result = await services.paymentService.payInvoice({ + senderWalletId: "legacy-usd-wallet-id" as WalletId, + paymentRequest: "lnbc1payment", + }) + + expect(result).toBeInstanceOf(Error) + }) + + it("computes the fee reimbursement as the exact destination USDT shortfall", async () => { + const deps = { + getBalanceForWallet: jest.fn(async () => USDTAmount.smallestUnits("14930000")), + } + + const services = createCashWalletMigrationRuntimeServices(deps) + + const result = await services.feeService.readFeeAmountUsdtMicros( + migration({ + balanceMovePaymentTransactionId: "ibex-tx-id", + destinationAmountUsdtMicros: "10000000", + destinationStartingBalanceUsdtMicros: "5000000", + }), + ) + + expect(result).toBe("70000") + expect(deps.getBalanceForWallet).toHaveBeenCalledWith({ + walletId: "usdt-wallet-id", + currency: WalletCurrency.Usdt, + }) + }) + + it("flips the default wallet and returns the previous default wallet id", async () => { + const deps = { + accountsRepo: { + findById: jest.fn(async () => ({ + defaultWalletId: "legacy-usd-wallet-id" as WalletId, + })), + }, + updateDefaultWalletId: jest.fn(async () => ({ defaultWalletId: "usdt-wallet-id" })), + } + + const services = createCashWalletMigrationRuntimeServices(deps) + + const result = await services.pointerService.flipDefaultWallet({ + accountId: "account-id" as AccountId, + destinationWalletId: "usdt-wallet-id" as WalletId, + }) + + expect(result).toEqual({ + previousDefaultWalletId: "legacy-usd-wallet-id", + }) + expect(deps.updateDefaultWalletId).toHaveBeenCalledWith({ + accountId: "account-id", + walletId: "usdt-wallet-id", + }) + }) + + it("propagates legacy zero verification errors", async () => { + const error = new CouldNotUpdateError("balance lookup failed") + const services = createCashWalletMigrationRuntimeServices({ + getBalanceForWallet: jest.fn(async () => error), + }) + + const result = await services.legacyWalletVerifier.verifyLegacyWalletZero({ + legacyUsdWalletId: "legacy-usd-wallet-id" as WalletId, + }) + + expect(result).toBe(error) + }) +}) diff --git a/test/flash/unit/app/cash-wallet-cutover/worker.spec.ts b/test/flash/unit/app/cash-wallet-cutover/worker.spec.ts new file mode 100644 index 000000000..0d27c14ae --- /dev/null +++ b/test/flash/unit/app/cash-wallet-cutover/worker.spec.ts @@ -0,0 +1,863 @@ +import { CouldNotUpdateError } from "@domain/errors" + +import { + createCashWalletMigrationBalanceMoveInvoice, + createCashWalletMigrationFeeReimbursementInvoice, + flipCashWalletMigrationDefaultPointer, + completeCashWalletMigration, + markCashWalletMigrationFeeReimbursed, + markCashWalletMigrationBalanceMoveSent, + provisionCashWalletMigrationDestination, + recordCashWalletMigrationBalance, + sendCashWalletMigrationBalanceMovePayment, + sendCashWalletMigrationFeeReimbursementPayment, + skipCashWalletMigrationFeeReimbursement, + startCashWalletMigration, + verifyCashWalletMigrationBalanceMove, + verifyCashWalletMigrationLegacyZero, +} from "@app/cash-wallet-cutover/worker" + +const migration = (status: CashWalletMigrationStatus): CashWalletMigration => ({ + id: "migration-id", + accountId: "account-id" as AccountId, + legacyUsdWalletId: "legacy-usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 7, + runId: "run-7", + status, + idempotencyKey: "cash-wallet-cutover:run-7:account-id", + attempts: 0, + updatedAt: new Date("2026-05-20T00:00:00Z"), +}) + +describe("cash wallet migration worker checkpoints", () => { + it("starts a not-started migration with an atomic repository transition", async () => { + const startedAt = new Date("2026-05-20T13:00:00Z") + const migrationsRepo = { + transitionMigration: jest.fn(async () => migration("started")), + } + + const result = await startCashWalletMigration({ + migration: migration("not_started"), + migrationsRepo, + startedAt, + }) + + expect(result).toMatchObject({ status: "started" }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "not_started", + to: "started", + cutoverVersion: 7, + runId: "run-7", + patch: { startedAt }, + }) + }) + + it("rejects invalid start transitions before touching the repository", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + + const result = await startCashWalletMigration({ + migration: migration("balance_read"), + migrationsRepo, + startedAt: new Date("2026-05-20T13:00:00Z"), + }) + + expect(result).toBeInstanceOf(Error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("returns repository transition failures", async () => { + const error = new CouldNotUpdateError("transition failed") + const migrationsRepo = { + transitionMigration: jest.fn(async () => error), + } + + const result = await startCashWalletMigration({ + migration: migration("not_started"), + migrationsRepo, + startedAt: new Date("2026-05-20T13:00:00Z"), + }) + + expect(result).toBe(error) + }) + + it("provisions the destination wallet before reading balances", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => migration("provisioned")), + } + const provisioningService = { + ensureDestinationWallet: jest.fn(async () => true as const), + } + + const result = await provisionCashWalletMigrationDestination({ + migration: migration("started"), + provisioningService, + migrationsRepo, + }) + + expect(result).toMatchObject({ status: "provisioned" }) + expect(provisioningService.ensureDestinationWallet).toHaveBeenCalledWith({ + accountId: "account-id", + destinationUsdtWalletId: "usdt-wallet-id", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "started", + to: "provisioned", + cutoverVersion: 7, + runId: "run-7", + }) + }) + + it("returns destination wallet provisioning failures without advancing", async () => { + const error = new CouldNotUpdateError("destination wallet missing") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const provisioningService = { + ensureDestinationWallet: jest.fn(async () => error), + } + + const result = await provisionCashWalletMigrationDestination({ + migration: migration("started"), + provisioningService, + migrationsRepo, + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("records source balance and destination amount before creating invoices", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("balance_read"), + sourceBalanceUsdCents: "1234", + destinationAmountUsdtMicros: "12340000", + destinationStartingBalanceUsdtMicros: "5000000", + })), + } + + const result = await recordCashWalletMigrationBalance({ + migration: migration("provisioned"), + migrationsRepo, + sourceBalanceUsdCents: "1234", + destinationStartingBalanceUsdtMicros: "5000000", + }) + + expect(result).toMatchObject({ + status: "balance_read", + sourceBalanceUsdCents: "1234", + destinationAmountUsdtMicros: "12340000", + destinationStartingBalanceUsdtMicros: "5000000", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "provisioned", + to: "balance_read", + cutoverVersion: 7, + runId: "run-7", + patch: { + sourceBalanceUsdCents: "1234", + destinationAmountUsdtMicros: "12340000", + destinationStartingBalanceUsdtMicros: "5000000", + }, + }) + }) + + it("rejects invalid balance amounts before touching the repository", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + + const result = await recordCashWalletMigrationBalance({ + migration: migration("provisioned"), + migrationsRepo, + sourceBalanceUsdCents: "12.34", + destinationStartingBalanceUsdtMicros: "0", + }) + + expect(result).toBeInstanceOf(Error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("creates a no-amount balance move invoice on the destination wallet", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("invoice_created"), + balanceMoveInvoicePaymentRequest: "lnbc1balance-move", + balanceMoveInvoicePaymentHash: "paymentHash", + })), + } + const invoice = { + paymentRequest: "lnbc1balance-move" as EncodedPaymentRequest, + paymentHash: "paymentHash" as PaymentHash, + } as LnInvoice + const invoiceService = { + createNoAmountInvoice: jest.fn(async () => invoice), + } + + const result = await createCashWalletMigrationBalanceMoveInvoice({ + migration: { + ...migration("balance_read"), + destinationAmountUsdtMicros: "12340000", + }, + invoiceService, + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "invoice_created", + balanceMoveInvoicePaymentRequest: "lnbc1balance-move", + balanceMoveInvoicePaymentHash: "paymentHash", + }) + expect(invoiceService.createNoAmountInvoice).toHaveBeenCalledWith({ + recipientWalletId: "usdt-wallet-id", + memo: "cash-wallet-cutover:run-7:migration-id:balance-move", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "balance_read", + to: "invoice_created", + cutoverVersion: 7, + runId: "run-7", + patch: { + balanceMoveInvoicePaymentRequest: "lnbc1balance-move", + balanceMoveInvoicePaymentHash: "paymentHash", + }, + }) + }) + + it("rejects balance move invoice creation when the destination amount is missing", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const invoiceService = { + createNoAmountInvoice: jest.fn(), + } + + const result = await createCashWalletMigrationBalanceMoveInvoice({ + migration: migration("balance_read"), + invoiceService, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(invoiceService.createNoAmountInvoice).not.toHaveBeenCalled() + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("returns balance move invoice creation failures without advancing the checkpoint", async () => { + const error = new CouldNotUpdateError("invoice creation failed") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const invoiceService = { + createNoAmountInvoice: jest.fn(async () => error), + } + + const result = await createCashWalletMigrationBalanceMoveInvoice({ + migration: { + ...migration("balance_read"), + destinationAmountUsdtMicros: "12340000", + }, + invoiceService, + migrationsRepo, + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("sends the balance move payment from the legacy wallet capped to the recorded source balance", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("balance_move_sending"), + balanceMovePaymentTransactionId: "ibex-tx-id", + })), + } + const paymentService = { + payInvoice: jest.fn(async () => ({ + transactionId: "ibex-tx-id" as IbexTransactionId, + })), + } + + const result = await sendCashWalletMigrationBalanceMovePayment({ + migration: { + ...migration("invoice_created"), + balanceMoveInvoicePaymentRequest: "lnbc1balance-move", + sourceBalanceUsdCents: "1000", + }, + paymentService, + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "balance_move_sending", + balanceMovePaymentTransactionId: "ibex-tx-id", + }) + expect(paymentService.payInvoice).toHaveBeenCalledWith({ + senderWalletId: "legacy-usd-wallet-id", + paymentRequest: "lnbc1balance-move", + senderAmountUsdCents: "1000", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "invoice_created", + to: "balance_move_sending", + cutoverVersion: 7, + runId: "run-7", + patch: { + balanceMovePaymentTransactionId: "ibex-tx-id", + }, + }) + }) + + it("rejects balance move payment sending when the invoice payment request is missing", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const paymentService = { + payInvoice: jest.fn(), + } + + const result = await sendCashWalletMigrationBalanceMovePayment({ + migration: migration("invoice_created"), + paymentService, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(paymentService.payInvoice).not.toHaveBeenCalled() + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("rejects balance move payment sending when the source balance is missing", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const paymentService = { + payInvoice: jest.fn(), + } + + const result = await sendCashWalletMigrationBalanceMovePayment({ + migration: { + ...migration("invoice_created"), + balanceMoveInvoicePaymentRequest: "lnbc1balance-move", + }, + paymentService, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(paymentService.payInvoice).not.toHaveBeenCalled() + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("marks the balance move payment as sent after a transaction id is recorded", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("balance_move_sent"), + balanceMovePaymentTransactionId: "ibex-tx-id", + })), + } + + const result = await markCashWalletMigrationBalanceMoveSent({ + migration: { + ...migration("balance_move_sending"), + balanceMovePaymentTransactionId: "ibex-tx-id", + }, + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "balance_move_sent", + balanceMovePaymentTransactionId: "ibex-tx-id", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "balance_move_sending", + to: "balance_move_sent", + cutoverVersion: 7, + runId: "run-7", + }) + }) + + it("rejects marking the balance move payment as sent before a transaction id exists", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + + const result = await markCashWalletMigrationBalanceMoveSent({ + migration: migration("balance_move_sending"), + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("verifies the balance move before fee reimbursement", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => migration("balance_move_verified")), + } + const balanceVerifier = { + verifyBalanceMove: jest.fn(async () => true as const), + } + + const result = await verifyCashWalletMigrationBalanceMove({ + migration: { + ...migration("balance_move_sent"), + balanceMovePaymentTransactionId: "ibex-tx-id", + }, + balanceVerifier, + migrationsRepo, + }) + + expect(result).toMatchObject({ status: "balance_move_verified" }) + expect(balanceVerifier.verifyBalanceMove).toHaveBeenCalledWith({ + legacyUsdWalletId: "legacy-usd-wallet-id", + destinationUsdtWalletId: "usdt-wallet-id", + sourceBalanceUsdCents: undefined, + destinationAmountUsdtMicros: undefined, + transactionId: "ibex-tx-id", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "balance_move_sent", + to: "balance_move_verified", + cutoverVersion: 7, + runId: "run-7", + }) + }) + + it("returns balance move verification failures without advancing the checkpoint", async () => { + const error = new CouldNotUpdateError("balance move not settled") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const balanceVerifier = { + verifyBalanceMove: jest.fn(async () => error), + } + + const result = await verifyCashWalletMigrationBalanceMove({ + migration: { + ...migration("balance_move_sent"), + balanceMovePaymentTransactionId: "ibex-tx-id", + }, + balanceVerifier, + migrationsRepo, + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("rejects balance move verification before a transaction id exists", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const balanceVerifier = { + verifyBalanceMove: jest.fn(), + } + + const result = await verifyCashWalletMigrationBalanceMove({ + migration: migration("balance_move_sent"), + balanceVerifier, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(balanceVerifier.verifyBalanceMove).not.toHaveBeenCalled() + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("creates a fee reimbursement invoice on the destination wallet for the exact USDT shortfall", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("fee_reimbursement_invoice_created"), + feeAmountUsdCents: "8", + feeAmountUsdtMicros: "70001", + feeReimbursementInvoicePaymentRequest: "lnbc1fee-reimbursement", + feeReimbursementInvoicePaymentHash: "feePaymentHash", + })), + } + const invoice = { + paymentRequest: "lnbc1fee-reimbursement" as EncodedPaymentRequest, + paymentHash: "feePaymentHash" as PaymentHash, + } as LnInvoice + const invoiceService = { + createInvoice: jest.fn(async () => invoice), + } + + const result = await createCashWalletMigrationFeeReimbursementInvoice({ + migration: migration("balance_move_verified"), + invoiceService, + migrationsRepo, + feeAmountUsdtMicros: "70001", + }) + + expect(result).toMatchObject({ + status: "fee_reimbursement_invoice_created", + feeAmountUsdCents: "8", + feeAmountUsdtMicros: "70001", + feeReimbursementInvoicePaymentRequest: "lnbc1fee-reimbursement", + feeReimbursementInvoicePaymentHash: "feePaymentHash", + }) + expect(invoiceService.createInvoice).toHaveBeenCalledWith({ + recipientWalletId: "usdt-wallet-id", + amount: "70001", + memo: "cash-wallet-cutover:run-7:migration-id:fee-reimbursement", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "balance_move_verified", + to: "fee_reimbursement_invoice_created", + cutoverVersion: 7, + runId: "run-7", + patch: { + feeAmountUsdCents: "8", + feeAmountUsdtMicros: "70001", + feeReimbursementInvoicePaymentRequest: "lnbc1fee-reimbursement", + feeReimbursementInvoicePaymentHash: "feePaymentHash", + }, + }) + }) + + it("rejects invalid fee reimbursement amounts before creating an invoice", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const invoiceService = { + createInvoice: jest.fn(), + } + + const result = await createCashWalletMigrationFeeReimbursementInvoice({ + migration: migration("balance_move_verified"), + invoiceService, + migrationsRepo, + feeAmountUsdtMicros: "0.07", + }) + + expect(result).toBeInstanceOf(Error) + expect(invoiceService.createInvoice).not.toHaveBeenCalled() + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("returns fee reimbursement invoice creation failures without advancing", async () => { + const error = new CouldNotUpdateError("fee invoice failed") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const invoiceService = { + createInvoice: jest.fn(async () => error), + } + + const result = await createCashWalletMigrationFeeReimbursementInvoice({ + migration: migration("balance_move_verified"), + invoiceService, + migrationsRepo, + feeAmountUsdtMicros: "70000", + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("skips fee reimbursement when there is no destination shortfall", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("fee_reimbursed"), + feeAmountUsdCents: "0", + feeAmountUsdtMicros: "0", + })), + } + + const result = await skipCashWalletMigrationFeeReimbursement({ + migration: migration("balance_move_verified"), + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "fee_reimbursed", + feeAmountUsdCents: "0", + feeAmountUsdtMicros: "0", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "balance_move_verified", + to: "fee_reimbursed", + cutoverVersion: 7, + runId: "run-7", + patch: { + feeAmountUsdCents: "0", + feeAmountUsdtMicros: "0", + }, + }) + }) + + it("sends the fee reimbursement payment from the treasury wallet", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("fee_reimbursement_sending"), + feeReimbursementPaymentTransactionId: "fee-ibex-tx-id", + })), + } + const paymentService = { + payInvoice: jest.fn(async () => ({ + transactionId: "fee-ibex-tx-id" as IbexTransactionId, + })), + } + + const result = await sendCashWalletMigrationFeeReimbursementPayment({ + migration: { + ...migration("fee_reimbursement_invoice_created"), + feeReimbursementInvoicePaymentRequest: "lnbc1fee-reimbursement", + }, + treasuryWalletId: "treasury-wallet-id" as WalletId, + paymentService, + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "fee_reimbursement_sending", + feeReimbursementPaymentTransactionId: "fee-ibex-tx-id", + }) + expect(paymentService.payInvoice).toHaveBeenCalledWith({ + senderWalletId: "treasury-wallet-id", + paymentRequest: "lnbc1fee-reimbursement", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "fee_reimbursement_invoice_created", + to: "fee_reimbursement_sending", + cutoverVersion: 7, + runId: "run-7", + patch: { + feeReimbursementPaymentTransactionId: "fee-ibex-tx-id", + }, + }) + }) + + it("rejects fee reimbursement sending when the invoice payment request is missing", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const paymentService = { + payInvoice: jest.fn(), + } + + const result = await sendCashWalletMigrationFeeReimbursementPayment({ + migration: migration("fee_reimbursement_invoice_created"), + treasuryWalletId: "treasury-wallet-id" as WalletId, + paymentService, + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(paymentService.payInvoice).not.toHaveBeenCalled() + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("returns fee reimbursement payment failures without advancing", async () => { + const error = new CouldNotUpdateError("fee payment failed") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const paymentService = { + payInvoice: jest.fn(async () => error), + } + + const result = await sendCashWalletMigrationFeeReimbursementPayment({ + migration: { + ...migration("fee_reimbursement_invoice_created"), + feeReimbursementInvoicePaymentRequest: "lnbc1fee-reimbursement", + }, + treasuryWalletId: "treasury-wallet-id" as WalletId, + paymentService, + migrationsRepo, + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("marks the fee reimbursement as complete after a transaction id is recorded", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("fee_reimbursed"), + feeReimbursementPaymentTransactionId: "fee-ibex-tx-id", + })), + } + + const result = await markCashWalletMigrationFeeReimbursed({ + migration: { + ...migration("fee_reimbursement_sending"), + feeReimbursementPaymentTransactionId: "fee-ibex-tx-id", + }, + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "fee_reimbursed", + feeReimbursementPaymentTransactionId: "fee-ibex-tx-id", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "fee_reimbursement_sending", + to: "fee_reimbursed", + cutoverVersion: 7, + runId: "run-7", + }) + }) + + it("rejects marking fee reimbursement complete before a transaction id exists", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(), + } + + const result = await markCashWalletMigrationFeeReimbursed({ + migration: migration("fee_reimbursement_sending"), + migrationsRepo, + }) + + expect(result).toBeInstanceOf(Error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("flips the account default pointer to the destination USDT wallet", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("pointer_flipped"), + previousDefaultWalletId: "legacy-usd-wallet-id" as WalletId, + })), + } + const pointerService = { + flipDefaultWallet: jest.fn(async () => ({ + previousDefaultWalletId: "legacy-usd-wallet-id" as WalletId, + })), + } + + const result = await flipCashWalletMigrationDefaultPointer({ + migration: migration("fee_reimbursed"), + pointerService, + migrationsRepo, + }) + + expect(result).toMatchObject({ + status: "pointer_flipped", + previousDefaultWalletId: "legacy-usd-wallet-id", + }) + expect(pointerService.flipDefaultWallet).toHaveBeenCalledWith({ + accountId: "account-id", + destinationWalletId: "usdt-wallet-id", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "fee_reimbursed", + to: "pointer_flipped", + cutoverVersion: 7, + runId: "run-7", + patch: { + previousDefaultWalletId: "legacy-usd-wallet-id", + }, + }) + }) + + it("returns pointer flip failures without advancing", async () => { + const error = new CouldNotUpdateError("default wallet update failed") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const pointerService = { + flipDefaultWallet: jest.fn(async () => error), + } + + const result = await flipCashWalletMigrationDefaultPointer({ + migration: migration("fee_reimbursed"), + pointerService, + migrationsRepo, + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("verifies the legacy USD wallet is zero after the pointer flip", async () => { + const migrationsRepo = { + transitionMigration: jest.fn(async () => migration("legacy_zero_verified")), + } + const legacyWalletVerifier = { + verifyLegacyWalletZero: jest.fn(async () => true as const), + } + + const result = await verifyCashWalletMigrationLegacyZero({ + migration: migration("pointer_flipped"), + legacyWalletVerifier, + migrationsRepo, + }) + + expect(result).toMatchObject({ status: "legacy_zero_verified" }) + expect(legacyWalletVerifier.verifyLegacyWalletZero).toHaveBeenCalledWith({ + legacyUsdWalletId: "legacy-usd-wallet-id", + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "pointer_flipped", + to: "legacy_zero_verified", + cutoverVersion: 7, + runId: "run-7", + }) + }) + + it("returns legacy zero verification failures without advancing", async () => { + const error = new CouldNotUpdateError("legacy wallet still has a balance") + const migrationsRepo = { + transitionMigration: jest.fn(), + } + const legacyWalletVerifier = { + verifyLegacyWalletZero: jest.fn(async () => error), + } + + const result = await verifyCashWalletMigrationLegacyZero({ + migration: migration("pointer_flipped"), + legacyWalletVerifier, + migrationsRepo, + }) + + expect(result).toBe(error) + expect(migrationsRepo.transitionMigration).not.toHaveBeenCalled() + }) + + it("completes the migration after legacy zero verification", async () => { + const completedAt = new Date("2026-05-20T15:30:00Z") + const migrationsRepo = { + transitionMigration: jest.fn(async () => ({ + ...migration("complete"), + completedAt, + })), + } + + const result = await completeCashWalletMigration({ + migration: migration("legacy_zero_verified"), + migrationsRepo, + completedAt, + }) + + expect(result).toMatchObject({ + status: "complete", + completedAt, + }) + expect(migrationsRepo.transitionMigration).toHaveBeenCalledWith({ + id: "migration-id", + from: "legacy_zero_verified", + to: "complete", + cutoverVersion: 7, + runId: "run-7", + patch: { completedAt }, + }) + }) +}) diff --git a/test/flash/unit/graphql/cash-wallet-cutover.spec.ts b/test/flash/unit/graphql/cash-wallet-cutover.spec.ts new file mode 100644 index 000000000..f8e102a1e --- /dev/null +++ b/test/flash/unit/graphql/cash-wallet-cutover.spec.ts @@ -0,0 +1,81 @@ +jest.mock("@services/mongoose/cash-wallet-cutover", () => ({ + CashWalletCutoverRepository: jest.fn(), +})) + +import CashWalletCutoverQuery from "@graphql/shared/root/query/cash-wallet-cutover" +import CashWalletCutoverUpdateMutation from "@graphql/admin/root/mutation/cash-wallet-cutover-update" +import { CashWalletCutoverRepository } from "@services/mongoose/cash-wallet-cutover" + +describe("cash wallet cutover GraphQL surface", () => { + const updatedAt = new Date("2026-05-22T12:00:00Z") + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("returns the public cutover flag state", async () => { + const getConfig = jest.fn(async () => ({ + state: "in_progress" as const, + cutoverVersion: 7, + runId: "run-7", + scheduledAt: new Date("2026-05-22T13:00:00Z"), + updatedAt, + })) + jest.mocked(CashWalletCutoverRepository).mockReturnValue({ getConfig } as never) + + const result = await CashWalletCutoverQuery.resolve?.( + null, + {}, + {} as GraphQLPublicContext, + {} as never, + ) + + expect(result).toMatchObject({ + state: "in_progress", + cutoverVersion: 7, + runId: "run-7", + scheduledAt: new Date("2026-05-22T13:00:00Z"), + }) + }) + + it("lets admins mutate the cutover flag", async () => { + const updateConfig = jest.fn(async () => ({ + state: "in_progress" as const, + cutoverVersion: 8, + runId: "run-8", + updatedBy: "admin-user-id", + updatedAt, + })) + jest.mocked(CashWalletCutoverRepository).mockReturnValue({ updateConfig } as never) + + const result = await CashWalletCutoverUpdateMutation.resolve?.( + null, + { + input: { + state: "in_progress", + cutoverVersion: 8, + runId: "run-8", + }, + }, + { user: { id: "admin-user-id" } } as GraphQLAdminContext, + {} as never, + ) + + expect(updateConfig).toHaveBeenCalledWith( + { + state: "in_progress", + cutoverVersion: 8, + runId: "run-8", + }, + "admin-user-id", + ) + expect(result).toMatchObject({ + errors: [], + cashWalletCutover: { + state: "in_progress", + cutoverVersion: 8, + runId: "run-8", + }, + }) + }) +}) diff --git a/test/flash/unit/graphql/shared/types/object/usd-wallet.spec.ts b/test/flash/unit/graphql/shared/types/object/usd-wallet.spec.ts new file mode 100644 index 000000000..272bd40d6 --- /dev/null +++ b/test/flash/unit/graphql/shared/types/object/usd-wallet.spec.ts @@ -0,0 +1,17 @@ +import { usdtMicrosToUsdCents } from "@graphql/shared/types/object/usd-wallet" + +describe("UsdWallet legacy compatibility balance", () => { + it("converts USDT micros to USD cents from integer smallest units", () => { + expect(usdtMicrosToUsdCents("10000000")).toBe(1000) + }) + + it("accepts formatted USDT smallest units from precision calls", () => { + expect(usdtMicrosToUsdCents("10000000.00000000")).toBe(1000) + }) + + it("rejects non-zero fractional micros", () => { + expect(() => usdtMicrosToUsdCents("10000000.5")).toThrow( + "Cannot convert fractional USDT micros", + ) + }) +}) diff --git a/test/flash/unit/services/mongoose/cash-wallet-cutover.spec.ts b/test/flash/unit/services/mongoose/cash-wallet-cutover.spec.ts new file mode 100644 index 000000000..fcb76e7de --- /dev/null +++ b/test/flash/unit/services/mongoose/cash-wallet-cutover.spec.ts @@ -0,0 +1,242 @@ +import { CouldNotUpdateError } from "@domain/errors" +import { CashWalletCutoverRepository } from "@services/mongoose/cash-wallet-cutover" +import { CashWalletCutoverConfig, CashWalletMigration } from "@services/mongoose/schema" + +jest.mock("@services/mongoose/schema", () => ({ + CashWalletCutoverConfig: { + findById: jest.fn(), + findOneAndUpdate: jest.fn(), + }, + CashWalletMigration: { + findOne: jest.fn(), + findOneAndUpdate: jest.fn(), + find: jest.fn(), + countDocuments: jest.fn(), + }, +})) + +describe("CashWalletCutoverRepository", () => { + const repo = CashWalletCutoverRepository() + const updatedAt = new Date("2026-05-19T00:00:00Z") + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("returns default pre state when no config exists", async () => { + jest.mocked(CashWalletCutoverConfig.findById).mockResolvedValue(null as never) + + const result = await repo.getConfig() + + expect(result).toEqual({ + state: "pre", + cutoverVersion: 1, + updatedAt: new Date(0), + }) + }) + + it("upserts singleton config", async () => { + jest.mocked(CashWalletCutoverConfig.findOneAndUpdate).mockResolvedValue({ + _id: "cash_wallet_cutover", + state: "in_progress", + cutoverVersion: 2, + runId: "run-2", + updatedBy: "operator", + updatedAt, + } as never) + + const result = await repo.updateConfig( + { state: "in_progress", cutoverVersion: 2, runId: "run-2" }, + "operator", + ) + + expect(CashWalletCutoverConfig.findOneAndUpdate).toHaveBeenCalledWith( + { _id: "cash_wallet_cutover" }, + expect.objectContaining({ + $set: expect.objectContaining({ + state: "in_progress", + cutoverVersion: 2, + runId: "run-2", + updatedBy: "operator", + }), + }), + { upsert: true, new: true }, + ) + expect(result).toMatchObject({ + state: "in_progress", + cutoverVersion: 2, + runId: "run-2", + }) + }) + + it("creates one migration record per account id and run", async () => { + jest.mocked(CashWalletMigration.findOneAndUpdate).mockResolvedValue({ + _id: "migration-id", + accountId: "account-id", + legacyUsdWalletId: "usd-wallet-id", + destinationUsdtWalletId: "usdt-wallet-id", + cutoverVersion: 2, + runId: "run-2", + status: "not_started", + idempotencyKey: "run-2:account-id", + attempts: 0, + updatedAt, + } as never) + + const result = await repo.upsertMigration({ + accountId: "account-id" as AccountId, + legacyUsdWalletId: "usd-wallet-id" as WalletId, + destinationUsdtWalletId: "usdt-wallet-id" as WalletId, + cutoverVersion: 2, + runId: "run-2", + idempotencyKey: "run-2:account-id", + }) + + expect(CashWalletMigration.findOneAndUpdate).toHaveBeenCalledWith( + { accountId: "account-id", runId: "run-2" }, + expect.objectContaining({ + $setOnInsert: expect.objectContaining({ + accountId: "account-id", + runId: "run-2", + status: "not_started", + }), + }), + { upsert: true, new: true }, + ) + expect(result).toMatchObject({ id: "migration-id", accountId: "account-id" }) + }) + + it("transitions migration status atomically", async () => { + jest.mocked(CashWalletMigration.findOneAndUpdate).mockResolvedValue({ + _id: "migration-id", + accountId: "account-id", + legacyUsdWalletId: "usd-wallet-id", + destinationUsdtWalletId: "usdt-wallet-id", + cutoverVersion: 2, + runId: "run-2", + status: "started", + idempotencyKey: "run-2:account-id", + attempts: 0, + updatedAt, + } as never) + + const result = await repo.transitionMigration({ + id: "migration-id", + from: "not_started", + to: "started", + cutoverVersion: 2, + runId: "run-2", + patch: { startedAt: updatedAt }, + }) + + expect(CashWalletMigration.findOneAndUpdate).toHaveBeenCalledWith( + { _id: "migration-id", status: "not_started", cutoverVersion: 2, runId: "run-2" }, + expect.objectContaining({ $set: expect.objectContaining({ status: "started" }) }), + { new: true }, + ) + expect(result).toMatchObject({ status: "started" }) + }) + + it("acquires and rejects active locks atomically", async () => { + const staleBefore = new Date("2026-05-19T00:00:00Z") + jest.mocked(CashWalletMigration.findOneAndUpdate).mockResolvedValue(null as never) + + const result = await repo.acquireMigrationLock({ + id: "migration-id", + workerId: "worker-1", + staleBefore, + cutoverVersion: 2, + runId: "run-2", + }) + + expect(CashWalletMigration.findOneAndUpdate).toHaveBeenCalledWith( + { + _id: "migration-id", + cutoverVersion: 2, + runId: "run-2", + $or: [{ lockedAt: null }, { lockedAt: { $lt: staleBefore } }], + }, + expect.objectContaining({ + $set: expect.objectContaining({ lockedBy: "worker-1" }), + }), + { new: true }, + ) + expect(result).toBeInstanceOf(Error) + }) + + it("finds resumable non-terminal migrations for the current run", async () => { + const limit = jest.fn().mockResolvedValue([]) + const sort = jest.fn(() => ({ limit })) + jest.mocked(CashWalletMigration.find).mockReturnValue({ sort } as never) + + const result = await repo.listRunnableMigrations({ + cutoverVersion: 2, + runId: "run-2", + limit: 10, + }) + + expect(CashWalletMigration.find).toHaveBeenCalledWith( + expect.objectContaining({ + cutoverVersion: 2, + runId: "run-2", + status: { + $nin: expect.arrayContaining([ + "complete", + "failed", + "requires_operator_review", + ]), + }, + }), + ) + expect(sort).toHaveBeenCalledWith({ updatedAt: 1 }) + expect(limit).toHaveBeenCalledWith(10) + expect(result).toEqual([]) + }) + + it("marks migration failures durably and clears the worker lock", async () => { + jest.mocked(CashWalletMigration.findOneAndUpdate).mockResolvedValue({ + _id: "migration-id", + accountId: "account-id", + legacyUsdWalletId: "usd-wallet-id", + destinationUsdtWalletId: "usdt-wallet-id", + cutoverVersion: 2, + runId: "run-2", + status: "requires_operator_review", + idempotencyKey: "run-2:account-id", + attempts: 2, + lastError: "execution failed", + lockedAt: null, + lockedBy: null, + updatedAt, + } as never) + + const error = new CouldNotUpdateError("execution failed") + const result = await repo.markMigrationFailed({ + id: "migration-id", + workerId: "worker-1", + cutoverVersion: 2, + runId: "run-2", + status: "requires_operator_review", + error, + }) + + expect(CashWalletMigration.findOneAndUpdate).toHaveBeenCalledWith( + { _id: "migration-id", lockedBy: "worker-1", cutoverVersion: 2, runId: "run-2" }, + expect.objectContaining({ + $set: expect.objectContaining({ + status: "requires_operator_review", + lastError: "execution failed", + lockedAt: null, + lockedBy: null, + }), + $inc: { attempts: 1 }, + }), + { new: true }, + ) + expect(result).toMatchObject({ + status: "requires_operator_review", + attempts: 2, + lastError: "execution failed", + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index ec1990392..8212e936f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,39 +14,39 @@ resolved "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz" integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== -"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0": - version "3.13.9" - resolved "https://registry.npmjs.org/@apollo/client/-/client-3.13.9.tgz" - integrity sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ== +"@apollo/client@3.8.3": + version "3.8.3" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.8.3.tgz" + integrity sha512-mK86JM6hCpMEBGDgdO9U8ZYS8r9lPjXE1tVGpJMdSFUsIcXpmEfHUAbbFpPtYmxn8Qa7XsYy0dwDaDhpf4UUPw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" - "@wry/caches" "^1.0.0" + "@wry/context" "^0.7.3" "@wry/equality" "^0.5.6" - "@wry/trie" "^0.5.0" + "@wry/trie" "^0.4.3" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.18.0" + optimism "^0.17.5" prop-types "^15.7.2" - rehackt "^0.1.0" + response-iterator "^0.2.6" symbol-observable "^4.0.0" ts-invariant "^0.10.3" tslib "^2.3.0" zen-observable-ts "^1.2.5" -"@apollo/client@3.8.3": - version "3.8.3" - resolved "https://registry.npmjs.org/@apollo/client/-/client-3.8.3.tgz" - integrity sha512-mK86JM6hCpMEBGDgdO9U8ZYS8r9lPjXE1tVGpJMdSFUsIcXpmEfHUAbbFpPtYmxn8Qa7XsYy0dwDaDhpf4UUPw== +"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0": + version "3.13.9" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.13.9.tgz" + integrity sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" - "@wry/context" "^0.7.3" + "@wry/caches" "^1.0.0" "@wry/equality" "^0.5.6" - "@wry/trie" "^0.4.3" + "@wry/trie" "^0.5.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.17.5" + optimism "^0.18.0" prop-types "^15.7.2" - response-iterator "^0.2.6" + rehackt "^0.1.0" symbol-observable "^4.0.0" ts-invariant "^0.10.3" tslib "^2.3.0" @@ -213,7 +213,7 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-crypto/sha256-js@^5.2.0", "@aws-crypto/sha256-js@5.2.0": +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": version "5.2.0" resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz" integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== @@ -229,7 +229,7 @@ dependencies: tslib "^2.6.2" -"@aws-crypto/util@^5.2.0", "@aws-crypto/util@5.2.0": +"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": version "5.2.0" resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz" integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== @@ -706,7 +706,7 @@ "@smithy/types" "^4.11.0" tslib "^2.6.2" -"@aws-sdk/types@^3.222.0", "@aws-sdk/types@3.968.0": +"@aws-sdk/types@3.968.0", "@aws-sdk/types@^3.222.0": version "3.968.0" resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.968.0.tgz" integrity sha512-Wuumj/1cuiuXTMdHmvH88zbEl+5Pw++fOFQuMCF4yP0R+9k1lwX8rVst+oy99xaxtdluJZXrsccoZoA67ST1Ow== @@ -798,7 +798,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz" integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== -"@babel/core@^7.0.0", "@babel/core@^7.0.0 || ^8.0.0-0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.22.20", "@babel/core@^7.23.9", "@babel/core@^7.27.4", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.8.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.20", "@babel/core@^7.23.9", "@babel/core@^7.27.4": version "7.28.5" resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz" integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== @@ -1787,7 +1787,7 @@ "@firebase/util" "1.9.3" tslib "^2.1.0" -"@firebase/database-types@^0.10.4", "@firebase/database-types@0.10.4": +"@firebase/database-types@0.10.4", "@firebase/database-types@^0.10.4": version "0.10.4" resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz" integrity sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ== @@ -1857,16 +1857,16 @@ resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz" integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== -"@google-cloud/promisify@^3.0.0": - version "3.0.1" - resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz" - integrity sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA== - "@google-cloud/promisify@<4.1.0": version "4.0.0" resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz" integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== +"@google-cloud/promisify@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz" + integrity sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA== + "@google-cloud/storage@^6.9.5": version "6.12.0" resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.12.0.tgz" @@ -1953,6 +1953,14 @@ tslib "^2.4.0" unixify "1.0.0" +"@graphql-tools/merge@8.3.1": + version "8.3.1" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz" + integrity sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg== + dependencies: + "@graphql-tools/utils" "8.9.0" + tslib "^2.4.0" + "@graphql-tools/merge@^8.1.2", "@graphql-tools/merge@^8.4.1": version "8.4.2" resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz" @@ -1969,14 +1977,6 @@ "@graphql-tools/utils" "^10.9.1" tslib "^2.4.0" -"@graphql-tools/merge@8.3.1": - version "8.3.1" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz" - integrity sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg== - dependencies: - "@graphql-tools/utils" "8.9.0" - tslib "^2.4.0" - "@graphql-tools/mock@^8.1.2": version "8.7.20" resolved "https://registry.npmjs.org/@graphql-tools/mock/-/mock-8.7.20.tgz" @@ -1994,16 +1994,7 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/schema@^10.0.25": - version "10.0.25" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.25.tgz" - integrity sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw== - dependencies: - "@graphql-tools/merge" "^9.1.1" - "@graphql-tools/utils" "^10.9.1" - tslib "^2.4.0" - -"@graphql-tools/schema@^8.0.0", "@graphql-tools/schema@^8.5.1", "@graphql-tools/schema@8.5.1": +"@graphql-tools/schema@8.5.1", "@graphql-tools/schema@^8.0.0", "@graphql-tools/schema@^8.5.1": version "8.5.1" resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz" integrity sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg== @@ -2013,17 +2004,16 @@ tslib "^2.4.0" value-or-promise "1.0.11" -"@graphql-tools/schema@^9.0.1": - version "9.0.19" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz" - integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== +"@graphql-tools/schema@^10.0.25": + version "10.0.25" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.25.tgz" + integrity sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw== dependencies: - "@graphql-tools/merge" "^8.4.1" - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/merge" "^9.1.1" + "@graphql-tools/utils" "^10.9.1" tslib "^2.4.0" - value-or-promise "^1.0.12" -"@graphql-tools/schema@^9.0.18": +"@graphql-tools/schema@^9.0.1", "@graphql-tools/schema@^9.0.18": version "9.0.19" resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz" integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== @@ -2033,6 +2023,13 @@ tslib "^2.4.0" value-or-promise "^1.0.12" +"@graphql-tools/utils@8.9.0": + version "8.9.0" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz" + integrity sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg== + dependencies: + tslib "^2.4.0" + "@graphql-tools/utils@^10.9.1": version "10.9.1" resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.9.1.tgz" @@ -2052,13 +2049,6 @@ "@graphql-typed-document-node/core" "^3.1.1" tslib "^2.4.0" -"@graphql-tools/utils@8.9.0": - version "8.9.0" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz" - integrity sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg== - dependencies: - tslib "^2.4.0" - "@graphql-tools/webpack-loader-runtime@7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/@graphql-tools/webpack-loader-runtime/-/webpack-loader-runtime-7.0.0.tgz" @@ -2078,6 +2068,14 @@ resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== +"@grpc/grpc-js@1.9.4": + version "1.9.4" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.4.tgz" + integrity sha512-oEnzYiDuEsBydZBtP84BkpduLsE1nSAO4KrhTLHRzNrIQE647fhchmosTQsJdCo8X9zBBt+l5+fNk+m/yCFJ/Q== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + "@grpc/grpc-js@^1.9.3": version "1.14.0" resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz" @@ -2094,25 +2092,17 @@ "@grpc/proto-loader" "^0.7.0" "@types/node" ">=12.12.47" -"@grpc/grpc-js@1.9.4": - version "1.9.4" - resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.4.tgz" - integrity sha512-oEnzYiDuEsBydZBtP84BkpduLsE1nSAO4KrhTLHRzNrIQE647fhchmosTQsJdCo8X9zBBt+l5+fNk+m/yCFJ/Q== - dependencies: - "@grpc/proto-loader" "^0.7.8" - "@types/node" ">=12.12.47" - -"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.9": - version "0.7.15" - resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz" - integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== +"@grpc/proto-loader@0.7.10": + version "0.7.10" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz" + integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== dependencies: lodash.camelcase "^4.3.0" long "^5.0.0" - protobufjs "^7.2.5" + protobufjs "^7.2.4" yargs "^17.7.2" -"@grpc/proto-loader@^0.7.8": +"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.8", "@grpc/proto-loader@^0.7.9": version "0.7.15" resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz" integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== @@ -2132,16 +2122,6 @@ protobufjs "^7.5.3" yargs "^17.7.2" -"@grpc/proto-loader@0.7.10": - version "0.7.10" - resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz" - integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== - dependencies: - lodash.camelcase "^4.3.0" - long "^5.0.0" - protobufjs "^7.2.4" - yargs "^17.7.2" - "@hono/node-server@^1.19.9": version "1.19.9" resolved "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz" @@ -2354,13 +2334,6 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - "@jest/schemas@30.0.5": version "30.0.5" resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz" @@ -2368,6 +2341,13 @@ dependencies: "@sinclair/typebox" "^0.34.0" +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz" @@ -2439,19 +2419,7 @@ slash "^3.0.0" write-file-atomic "^5.0.1" -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jest/types@^30.0.0", "@jest/types@30.2.0": +"@jest/types@30.2.0", "@jest/types@^30.0.0": version "30.2.0" resolved "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz" integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== @@ -2464,6 +2432,18 @@ "@types/yargs" "^17.0.33" chalk "^4.1.2" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@josephg/resolvable@^1.0.0": version "1.0.1" resolved "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz" @@ -2495,14 +2475,6 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.31" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" - integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" @@ -2511,6 +2483,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@js-sdsl/ordered-map@^4.4.2": version "4.4.2" resolved "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz" @@ -2620,13 +2600,6 @@ resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz" integrity sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w== -"@noble/curves@~1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" - integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== - dependencies: - "@noble/hashes" "1.3.1" - "@noble/curves@1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz" @@ -2634,20 +2607,12 @@ dependencies: "@noble/hashes" "1.3.2" -"@noble/hashes@^1.2.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@~1.3.0": - version "1.3.3" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== - -"@noble/hashes@~1.3.1": - version "1.3.3" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== +"@noble/curves@~1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" "@noble/hashes@1.3.1": version "1.3.1" @@ -2659,6 +2624,16 @@ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@noble/hashes@^1.2.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": + version "1.3.3" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -2667,7 +2642,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -2685,14 +2660,14 @@ resolved "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz" integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== -"@opentelemetry/api-logs@>=0.39.1", "@opentelemetry/api-logs@0.43.0": +"@opentelemetry/api-logs@0.43.0": version "0.43.0" resolved "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.43.0.tgz" integrity sha512-0CXMOYPXgAdLM2OzVkiUfAL6QQwWVhnMfUXCqLsITY42FZ9TxAhZIHkoc4mfVxvPuXsBnRYGR8UQZX86p87z4A== dependencies: "@opentelemetry/api" "^1.0.0" -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.6.0", "@opentelemetry/api@>=1.0.0 <1.10.0", "@opentelemetry/api@>=1.0.0 <1.7.0", "@opentelemetry/api@>=1.3.0 <1.10.0", "@opentelemetry/api@>=1.3.0 <1.7.0", "@opentelemetry/api@>=1.4.0 <1.7.0": +"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.6.0": version "1.6.0" resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.6.0.tgz" integrity sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g== @@ -2702,13 +2677,6 @@ resolved "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz" integrity sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA== -"@opentelemetry/core@^1.17.0", "@opentelemetry/core@1.30.1": - version "1.30.1" - resolved "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz" - integrity sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ== - dependencies: - "@opentelemetry/semantic-conventions" "1.28.0" - "@opentelemetry/core@1.17.0": version "1.17.0" resolved "https://registry.npmjs.org/@opentelemetry/core/-/core-1.17.0.tgz" @@ -2716,6 +2684,13 @@ dependencies: "@opentelemetry/semantic-conventions" "1.17.0" +"@opentelemetry/core@1.30.1", "@opentelemetry/core@^1.17.0": + version "1.30.1" + resolved "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz" + integrity sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.28.0" + "@opentelemetry/exporter-trace-otlp-http@^0.43.0": version "0.43.0" resolved "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.43.0.tgz" @@ -2779,7 +2754,7 @@ "@opentelemetry/instrumentation" "^0.46.0" "@opentelemetry/semantic-conventions" "^1.0.0" -"@opentelemetry/instrumentation@^0.43.0", "@opentelemetry/instrumentation@0.43.0": +"@opentelemetry/instrumentation@0.43.0", "@opentelemetry/instrumentation@^0.43.0": version "0.43.0" resolved "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.43.0.tgz" integrity sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ== @@ -2861,14 +2836,6 @@ resolved "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz" integrity sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g== -"@opentelemetry/resources@^1.17.0", "@opentelemetry/resources@1.30.1": - version "1.30.1" - resolved "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz" - integrity sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA== - dependencies: - "@opentelemetry/core" "1.30.1" - "@opentelemetry/semantic-conventions" "1.28.0" - "@opentelemetry/resources@1.17.0": version "1.17.0" resolved "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.17.0.tgz" @@ -2877,6 +2844,14 @@ "@opentelemetry/core" "1.17.0" "@opentelemetry/semantic-conventions" "1.17.0" +"@opentelemetry/resources@1.30.1", "@opentelemetry/resources@^1.17.0": + version "1.30.1" + resolved "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz" + integrity sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA== + dependencies: + "@opentelemetry/core" "1.30.1" + "@opentelemetry/semantic-conventions" "1.28.0" + "@opentelemetry/sdk-logs@0.43.0": version "0.43.0" resolved "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.43.0.tgz" @@ -2885,14 +2860,6 @@ "@opentelemetry/core" "1.17.0" "@opentelemetry/resources" "1.17.0" -"@opentelemetry/sdk-metrics@^1.9.1": - version "1.30.1" - resolved "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz" - integrity sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog== - dependencies: - "@opentelemetry/core" "1.30.1" - "@opentelemetry/resources" "1.30.1" - "@opentelemetry/sdk-metrics@1.17.0": version "1.17.0" resolved "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.17.0.tgz" @@ -2902,14 +2869,13 @@ "@opentelemetry/resources" "1.17.0" lodash.merge "^4.6.2" -"@opentelemetry/sdk-trace-base@^1.17.0", "@opentelemetry/sdk-trace-base@1.30.1": +"@opentelemetry/sdk-metrics@^1.9.1": version "1.30.1" - resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz" - integrity sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg== + resolved "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz" + integrity sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog== dependencies: "@opentelemetry/core" "1.30.1" "@opentelemetry/resources" "1.30.1" - "@opentelemetry/semantic-conventions" "1.28.0" "@opentelemetry/sdk-trace-base@1.17.0": version "1.17.0" @@ -2920,6 +2886,15 @@ "@opentelemetry/resources" "1.17.0" "@opentelemetry/semantic-conventions" "1.17.0" +"@opentelemetry/sdk-trace-base@1.30.1", "@opentelemetry/sdk-trace-base@^1.17.0": + version "1.30.1" + resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz" + integrity sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg== + dependencies: + "@opentelemetry/core" "1.30.1" + "@opentelemetry/resources" "1.30.1" + "@opentelemetry/semantic-conventions" "1.28.0" + "@opentelemetry/sdk-trace-node@^1.17.0": version "1.30.1" resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz" @@ -2932,11 +2907,6 @@ "@opentelemetry/sdk-trace-base" "1.30.1" semver "^7.5.2" -"@opentelemetry/semantic-conventions@^1.0.0", "@opentelemetry/semantic-conventions@^1.17.0": - version "1.37.0" - resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz" - integrity sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA== - "@opentelemetry/semantic-conventions@1.17.0": version "1.17.0" resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.17.0.tgz" @@ -2947,8 +2917,15 @@ resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz" integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA== +"@opentelemetry/semantic-conventions@^1.0.0", "@opentelemetry/semantic-conventions@^1.17.0": + version "1.37.0" + resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz" + integrity sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA== + "@ory/client@^1.2.6": - version "v1.22.7" + version "1.22.37" + resolved "https://registry.yarnpkg.com/@ory/client/-/client-1.22.37.tgz#9b63d5c04730ccad1536429a5085286f55eb50c2" + integrity sha512-UEYHSveRRD3TCT0JB54ZKQoRMEHn7HTsNsMOjz2t3bzCs5aNjgpjYIBK4z5zE2PwNrcInZyQXfRiwK+LIUaOug== dependencies: axios "^1.6.1" @@ -2990,11 +2967,71 @@ "@otplib/plugin-crypto" "^12.0.1" "@otplib/plugin-thirty-two" "^12.0.1" +"@parcel/watcher-android-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" + integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA== + "@parcel/watcher-darwin-arm64@2.5.1": version "2.5.1" resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz" integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw== +"@parcel/watcher-darwin-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8" + integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg== + +"@parcel/watcher-freebsd-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b" + integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ== + +"@parcel/watcher-linux-arm-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1" + integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA== + +"@parcel/watcher-linux-arm-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e" + integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q== + +"@parcel/watcher-linux-arm64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30" + integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w== + +"@parcel/watcher-linux-arm64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2" + integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg== + +"@parcel/watcher-linux-x64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" + integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== + +"@parcel/watcher-linux-x64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee" + integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg== + +"@parcel/watcher-win32-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243" + integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw== + +"@parcel/watcher-win32-ia32@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6" + integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ== + +"@parcel/watcher-win32-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947" + integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA== + "@parcel/watcher@^2.4.1": version "2.5.1" resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz" @@ -3166,6 +3203,11 @@ resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== +"@scure/base@1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + "@scure/base@^1.1.1": version "1.2.6" resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz" @@ -3176,11 +3218,6 @@ resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz" integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== -"@scure/base@1.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== - "@scure/bip32@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz" @@ -3755,7 +3792,52 @@ resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.85.tgz" integrity sha512-jTikp+i4nO4Ofe6qGm4I3sFeebD1OvueBCHITux5tQKD6umN1c2z4CRGv6K49NIz/qEpUcdr6Qny6K+3yibVFQ== -"@swc/core@*", "@swc/core@^1.3.68", "@swc/core@>=1.2.50", "@swc/core@1.3.85": +"@swc/core-darwin-x64@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.85.tgz#ce623656ee427876423f07d898fbd7fa767d39bd" + integrity sha512-3uHYkjVU+2F+YbVYtq5rH0uCJIztFTALaS3mQEfQUZKXZ5/8jD5titTCRqFKtSlQg0CzaFZgsYsuqwYBmgN0mA== + +"@swc/core-linux-arm-gnueabihf@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.85.tgz#9eb958e3b2c4af0d208dc2d1e71270797605c71b" + integrity sha512-ouHzAHsFaEOkRuoTAOI/8n2m8BQAAnb4vr/xbMhhDOmix0lp5eNsW5Iac/EcJ2uG6B3n7P2K8oycj9SWkj+pfw== + +"@swc/core-linux-arm64-gnu@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.85.tgz#2932636d5414709c56b9a670ca804dc317201a41" + integrity sha512-/Z1CZOWiO+NqJEh1J20PIxQFHMH43upQJ1l7FJ5Z7+MyuYF8WkeJ7OSovau729pBR+38vvvccEJrMZIztfv7hQ== + +"@swc/core-linux-arm64-musl@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.85.tgz#3b312c57ac0b6861d87a7c0f7ff24e232eb792d5" + integrity sha512-gfh7CfKavi076dbMBTzfdawSGcYfZ4+1Q+8aRkSesqepKHcIWIJti8Cf3zB4a6CHNhJe+VN0Gb7DEfumydAm1w== + +"@swc/core-linux-x64-gnu@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.85.tgz#d9fd7ff6ba4e693274604926b82027cb4c2a3306" + integrity sha512-lWVqjHKzofb9q1qrBM4dLqO7CIisp08/xMS5Hz9DWex1gTc5F2b6yJO6Ceqwa256GMweJcdP6A5EvEFQAiZ5dg== + +"@swc/core-linux-x64-musl@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.85.tgz#0a80efac999fb125131385a8937227819549939f" + integrity sha512-EPJmlfqC05TUetnlErxNRyIp7Nc3B2w9abET6oQ/EgldeAeQnZ3M6svMViET/c2QSomgrU3rdP+Qcozkt62/4A== + +"@swc/core-win32-arm64-msvc@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.85.tgz#9808ee0d35eeb315efa7b73edcca80001451c9f3" + integrity sha512-ibckJDZw8kNosciMexwk0z75ZyUhwtiFMV9rSBpup0opa7NNCUCoERCJ1e9LRyMdhsVUoLpZg/KZiHCdTw96hQ== + +"@swc/core-win32-ia32-msvc@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.85.tgz#9b85ba050f333199e88e54ba0dd7b72302af64be" + integrity sha512-hY4MpHGUVQHL1T2kgRXOigDho4DTIpVPYzJ4uyy8VQRbS7GzN5XtvdGP/fA4zp8+2BQjcig+6J7Y92SY15ouNQ== + +"@swc/core-win32-x64-msvc@1.3.85": + version "1.3.85" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.85.tgz#5c7f3f366804b512cf26e1731d8714f0409f1204" + integrity sha512-ktxWOMFJ0iqKn6WUHtXqi4CS7xkyHmrRtjllGRuGqxmLmDX/HSOfuQ55Tm1KXKk5oHLacJkUbOSF2kBrpZ8dpg== + +"@swc/core@1.3.85": version "1.3.85" resolved "https://registry.npmjs.org/@swc/core/-/core-1.3.85.tgz" integrity sha512-qnoxp+2O0GtvRdYnXgR1v8J7iymGGYpx6f6yCK9KxipOZOjrlKILFANYlghQxZyPUfXwK++TFxfSlX4r9wK+kg== @@ -3778,7 +3860,7 @@ resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz" integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@swc/jest@^0.2.26", "@swc/jest@^0.2.29": +"@swc/jest@^0.2.29": version "0.2.39" resolved "https://registry.npmjs.org/@swc/jest/-/jest-0.2.39.tgz" integrity sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA== @@ -3955,7 +4037,7 @@ dependencies: dotenv "*" -"@types/eslint@^8.44.2", "@types/eslint@>=8.0.0": +"@types/eslint@^8.44.2": version "8.56.12" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz" integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g== @@ -3968,17 +4050,16 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@types/express-serve-static-core@^4.17.18": - version "4.19.7" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz" - integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== +"@types/express-serve-static-core@4.17.31": + version "4.17.31" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz" + integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" - "@types/send" "*" -"@types/express-serve-static-core@^4.17.33": +"@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.33": version "4.19.7" resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz" integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== @@ -3988,25 +4069,6 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express-serve-static-core@4.17.31": - version "4.17.31" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz" - integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/express@*", "@types/express@^4.17.15", "@types/express@^4.17.20": - version "4.17.24" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.24.tgz" - integrity sha512-Mbrt4SRlXSTWryOnHAh2d4UQ/E7n9lZyGSi6KgX+4hkuL9soYbLOVXVhnk/ODp12YsGc95f4pOvqywJ6kngUwg== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - "@types/express@4.17.14": version "4.17.14" resolved "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz" @@ -4027,6 +4089,16 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/express@^4.17.15", "@types/express@^4.17.20": + version "4.17.24" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.24.tgz" + integrity sha512-Mbrt4SRlXSTWryOnHAh2d4UQ/E7n9lZyGSi6KgX+4hkuL9soYbLOVXVhnk/ODp12YsGc95f4pOvqywJ6kngUwg== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/form-data@0.0.33": version "0.0.33" resolved "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz" @@ -4203,7 +4275,7 @@ resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== -"@types/markdown-it@*", "@types/markdown-it@^14.1.1": +"@types/markdown-it@^14.1.1": version "14.1.2" resolved "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz" integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== @@ -4248,20 +4320,10 @@ dependencies: undici-types "~6.21.0" -"@types/node@^10.0.3": - version "10.17.60" - resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" - integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== - -"@types/node@^10.1.0": - version "10.17.60" - resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" - integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== - -"@types/node@^8.0.0": - version "8.10.66" - resolved "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz" - integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== +"@types/node@20.7.1": + version "20.7.1" + resolved "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz" + integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "24.9.1" @@ -4270,17 +4332,15 @@ dependencies: undici-types "~7.16.0" -"@types/node@>=13.7.0": - version "24.9.1" - resolved "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz" - integrity sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg== - dependencies: - undici-types "~7.16.0" +"@types/node@^10.0.3", "@types/node@^10.1.0": + version "10.17.60" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@20.7.1": - version "20.7.1" - resolved "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz" - integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== +"@types/node@^8.0.0": + version "8.10.66" + resolved "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz" + integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== "@types/nodemon@^1.19.2": version "1.19.6" @@ -4304,7 +4364,7 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react@*", "@types/react@^18.2.21": +"@types/react@^18.2.21": version "18.3.26" resolved "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz" integrity sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA== @@ -4319,16 +4379,6 @@ dependencies: "@types/node" "*" -"@types/request@^2.48.8": - version "2.48.13" - resolved "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz" - integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== - dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.5" - "@types/request@2.48.9": version "2.48.9" resolved "https://registry.npmjs.org/@types/request/-/request-2.48.9.tgz" @@ -4339,6 +4389,16 @@ "@types/tough-cookie" "*" form-data "^2.5.0" +"@types/request@^2.48.8": + version "2.48.13" + resolved "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz" + integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.5" + "@types/rimraf@^3.0.2": version "3.0.2" resolved "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz" @@ -4457,7 +4517,7 @@ resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz" integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== -"@typescript-eslint/eslint-plugin@^5.0.0 || ^6.0.0 || ^7.0.0", "@typescript-eslint/eslint-plugin@^6.7.0": +"@typescript-eslint/eslint-plugin@^6.7.0": version "6.21.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz" integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== @@ -4474,7 +4534,7 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.7.2": +"@typescript-eslint/parser@^6.7.2": version "6.21.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz" integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== @@ -4526,33 +4586,7 @@ resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/typescript-estree@^4.33.0": - version "4.33.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@^5.55.0": - version "5.62.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@5.62.0": +"@typescript-eslint/typescript-estree@5.62.0", "@typescript-eslint/typescript-estree@^5.55.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz" integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== @@ -4579,19 +4613,18 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@^5.10.0": - version "5.62.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== +"@typescript-eslint/typescript-estree@^4.33.0": + version "4.33.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" "@typescript-eslint/utils@6.21.0": version "6.21.0" @@ -4606,6 +4639,20 @@ "@typescript-eslint/typescript-estree" "6.21.0" semver "^7.5.4" +"@typescript-eslint/utils@^5.10.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@4.33.0": version "4.33.0" resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz" @@ -4685,16 +4732,16 @@ commander "^8.2.0" globby "^12.0.2" -abbrev@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz" - integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== - abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" @@ -4735,7 +4782,7 @@ acorn-walk@^8.1.1: dependencies: acorn "^8.11.0" -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: version "8.15.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== @@ -4745,11 +4792,6 @@ adm-zip@0.5.10: resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz" integrity sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ== -agent-base@^7.1.2: - version "7.1.4" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -4757,6 +4799,11 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + ajv-draft-04@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz" @@ -4779,7 +4826,7 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.12.0, ajv@^8.17.1, ajv@^8.5.0, "ajv@4.11.8 - 8": +ajv@^8.0.0, ajv@^8.12.0, ajv@^8.17.1: version "8.17.1" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -5154,6 +5201,11 @@ async-retry@^1.2.1, async-retry@^1.3.3: dependencies: retry "0.13.1" +async@3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + async@^2.6.0: version "2.6.4" resolved "https://registry.npmjs.org/async/-/async-2.6.4.tgz" @@ -5166,11 +5218,6 @@ async@^3.2.0, async@^3.2.3, async@~3.2.0: resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== -async@3.2.4: - version "3.2.4" - resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - asyncjs-util@1.2.12: version "1.2.12" resolved "https://registry.npmjs.org/asyncjs-util/-/asyncjs-util-1.2.12.tgz" @@ -5217,7 +5264,7 @@ axios@^0.26.1: dependencies: follow-redirects "^1.14.8" -axios@^1.12.0, axios@^1.3.4, axios@^1.5.0, axios@^1.6.0, axios@^1.6.1, "axios@>= 0.17.0": +axios@^1.12.0, axios@^1.3.4, axios@^1.5.0, axios@^1.6.0, axios@^1.6.1: version "1.12.2" resolved "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz" integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw== @@ -5368,16 +5415,16 @@ batch@0.6.1: resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== +bech32@2.0.0, bech32@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + bech32@^1.1.2: version "1.1.4" resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -bech32@^2.0.0, bech32@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz" - integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== - bignumber.js@^9.0.0: version "9.3.1" resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz" @@ -5433,23 +5480,11 @@ bitcoin-cli-ts@^24.0.3: json-rpc-2.0 "^1.6.0" undici "^5.22.1" -bitcoin-ops@^1.3.0, bitcoin-ops@1.4.1: +bitcoin-ops@1.4.1, bitcoin-ops@^1.3.0: version "1.4.1" resolved "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz" integrity sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow== -bitcoinjs-lib@^6.0.0, bitcoinjs-lib@^6.1.4: - version "6.1.7" - resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz" - integrity sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg== - dependencies: - "@noble/hashes" "^1.2.0" - bech32 "^2.0.0" - bip174 "^2.1.1" - bs58check "^3.0.1" - typeforce "^1.11.3" - varuint-bitcoin "^1.1.2" - bitcoinjs-lib@6.1.3: version "6.1.3" resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz" @@ -5474,6 +5509,18 @@ bitcoinjs-lib@6.1.5: typeforce "^1.11.3" varuint-bitcoin "^1.1.2" +bitcoinjs-lib@^6.0.0, bitcoinjs-lib@^6.1.4: + version "6.1.7" + resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz" + integrity sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg== + dependencies: + "@noble/hashes" "^1.2.0" + bech32 "^2.0.0" + bip174 "^2.1.1" + bs58check "^3.0.1" + typeforce "^1.11.3" + varuint-bitcoin "^1.1.2" + bl@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" @@ -5488,53 +5535,15 @@ bluebird@^3.7.2: resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.11.8: - version "4.12.2" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" - integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== - -bn.js@^4.11.9: - version "4.12.2" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" - integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== - bn.js@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@^1.19.0, body-parser@^1.20.1, body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -body-parser@^2.2.1: - version "2.2.2" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz" - integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== - dependencies: - bytes "^3.1.2" - content-type "^1.0.5" - debug "^4.4.3" - http-errors "^2.0.0" - iconv-lite "^0.7.0" - on-finished "^2.4.1" - qs "^6.14.1" - raw-body "^3.0.1" - type-is "^2.0.1" +bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.2" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" + integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== body-parser@1.20.1: version "1.20.1" @@ -5572,6 +5581,39 @@ body-parser@1.20.2: type-is "~1.6.18" unpipe "1.0.0" +body-parser@1.20.3, body-parser@^1.19.0, body-parser@^1.20.1: + version "1.20.3" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" + body@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/body/-/body-5.1.0.tgz" @@ -5645,7 +5687,7 @@ brorand@^1.1.0: resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.24.0, browserslist@^4.26.3, "browserslist@>= 4.21.0": +browserslist@^4.24.0, browserslist@^4.26.3: version "4.27.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz" integrity sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw== @@ -5670,14 +5712,6 @@ bs58@^5.0.0: dependencies: base-x "^4.0.0" -bs58check@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz" - integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== - dependencies: - "@noble/hashes" "^1.2.0" - bs58 "^5.0.0" - bs58check@<3.0.0: version "2.1.2" resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz" @@ -5687,6 +5721,14 @@ bs58check@<3.0.0: create-hash "^1.1.0" safe-buffer "^5.1.2" +bs58check@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz" + integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^5.0.0" + bser@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" @@ -5694,9 +5736,9 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -bson@^5.5.0: +bson@^5.4.0, bson@^5.5.0: version "5.5.1" - resolved "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz" + resolved "https://registry.yarnpkg.com/bson/-/bson-5.5.1.tgz#f5849d405711a7f23acdda9a442375df858e6833" integrity sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g== bson@^6.10.4: @@ -5730,16 +5772,16 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -bytes@^3.1.2, bytes@~3.1.2, bytes@3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - bytes@1: version "1.0.0" resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" integrity sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ== +bytes@3.1.2, bytes@^3.1.2, bytes@~3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" @@ -5951,16 +5993,16 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - clone@2.x: version "2.1.2" resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + cluster-key-slot@^1.1.0: version "1.1.2" resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz" @@ -6003,16 +6045,16 @@ color-support@^1.1.2: resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colorette@^2.0.7: - version "2.0.20" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - colorette@2.0.19: version "2.0.19" resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + colors@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz" @@ -6045,12 +6087,7 @@ commander@^8.2.0: resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -commander@^9.1.0: - version "9.5.0" - resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - -commander@^9.5.0: +commander@^9.1.0, commander@^9.5.0: version "9.5.0" resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== @@ -6136,11 +6173,6 @@ console.table@^0.10.0: dependencies: easy-table "1.1.0" -content-disposition@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz" - integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== - content-disposition@0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" @@ -6148,6 +6180,11 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" @@ -6171,32 +6208,27 @@ cookie-parser@^1.4.6: cookie "0.7.2" cookie-signature "1.0.6" -cookie-signature@^1.2.1: - version "1.2.2" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz" - integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== - cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@^0.5.0, cookie@0.5.0: +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + +cookie@0.5.0, cookie@^0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@^0.7.1: - version "0.7.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz" - integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== - cookie@0.7.1: version "0.7.1" resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -cookie@0.7.2: +cookie@0.7.2, cookie@^0.7.1: version "0.7.2" resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== @@ -6213,7 +6245,7 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@^2.8.5, cors@2.8.5: +cors@2.8.5, cors@^2.8.5: version "2.8.5" resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -6297,7 +6329,7 @@ csv-writer@^1.6.0: resolved "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz" integrity sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g== -d@^1.0.1, d@^1.0.2, d@1: +d@1, d@^1.0.1, d@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/d/-/d-1.0.2.tgz" integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== @@ -6332,16 +6364,16 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -dataloader@^2.2.2: - version "2.2.3" - resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz" - integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== - dataloader@2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz" integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== +dataloader@^2.2.2: + version "2.2.3" + resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz" + integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== + datauri@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/datauri/-/datauri-4.1.0.tgz" @@ -6372,34 +6404,20 @@ dayjs@^1.11.9: resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz" integrity sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA== -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - ms "^2.1.1" + ms "2.0.0" -debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3, debug@4, debug@4.x: +debug@4, debug@4.x, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" @@ -6407,6 +6425,13 @@ debug@4.3.4: dependencies: ms "2.1.2" +debug@^3.1.0, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + dedent@^1.0.0, dedent@^1.5.1: version "1.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz" @@ -6472,7 +6497,7 @@ denque@^2.1.0: resolved "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@^2.0.0, depd@~2.0.0, depd@2.0.0: +depd@2.0.0, depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -6792,7 +6817,7 @@ easy-table@1.1.0: optionalDependencies: wcwidth ">=1.0.1" -ecdsa-sig-formatter@^1.0.11, ecdsa-sig-formatter@1.0.11: +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== @@ -6856,7 +6881,7 @@ emoji-regex@^9.2.2: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -encodeurl@^2.0.0: +encodeurl@^2.0.0, encodeurl@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== @@ -6866,11 +6891,6 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - encoding-sniffer@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz" @@ -7126,7 +7146,7 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^9.0.0, "eslint-config-prettier@>= 7.0.0 <10.0.0 || >=10.1.0": +eslint-config-prettier@^9.0.0: version "9.1.2" resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz" integrity sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ== @@ -7213,7 +7233,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.49.0, eslint@>=7.0.0, eslint@>=8.0.0: +eslint@^8.49.0: version "8.57.1" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz" integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== @@ -7281,16 +7301,16 @@ espree@^9.0.0, espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esprima@1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz" integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2: version "1.6.0" resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" @@ -7305,12 +7325,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^4.2.0: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -7348,16 +7363,16 @@ eventemitter2@~0.4.13: resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" integrity sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ== -events@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - events@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/events/-/events-1.1.1.tgz" integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== +events@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: version "3.0.6" resolved "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz" @@ -7427,31 +7442,68 @@ express-unless@^2.1.3: resolved "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz" integrity sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ== -express@^4.17.1, express@^4.18.2, "express@>= 4.11": - version "4.21.2" - resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== +express@4.18.2: + version "4.18.2" + resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.3" + body-parser "1.20.1" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.7.1" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~2.0.0" + encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.3.1" + finalhandler "1.2.0" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.3" + merge-descriptors "1.0.1" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.12" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +express@^4.17.1, express@^4.18.2: + version "4.21.2" + resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.7.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.12" proxy-addr "~2.0.7" qs "6.13.0" range-parser "~1.2.1" @@ -7498,43 +7550,6 @@ express@^5.0.1: type-is "^2.0.1" vary "^1.1.2" -express@4.18.2: - version "4.18.2" - resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - ext@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz" @@ -7613,13 +7628,6 @@ fast-uri@^3.0.1: resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz" integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== -fast-xml-parser@^4.2.2, fast-xml-parser@^4.4.1: - version "4.5.3" - resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz" - integrity sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig== - dependencies: - strnum "^1.1.1" - fast-xml-parser@5.2.5: version "5.2.5" resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz" @@ -7627,6 +7635,13 @@ fast-xml-parser@5.2.5: dependencies: strnum "^2.1.0" +fast-xml-parser@^4.2.2, fast-xml-parser@^4.4.1: + version "4.5.3" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz" + integrity sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig== + dependencies: + strnum "^1.1.1" + fastq@^1.6.0: version "1.19.1" resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" @@ -7634,13 +7649,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -faye-websocket@~0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz" - integrity sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ== - dependencies: - websocket-driver ">=0.5.1" - faye-websocket@0.11.4: version "0.11.4" resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" @@ -7648,6 +7656,13 @@ faye-websocket@0.11.4: dependencies: websocket-driver ">=0.5.1" +faye-websocket@~0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz" + integrity sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ== + dependencies: + websocket-driver ">=0.5.1" + fb-watchman@^2.0.0, fb-watchman@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" @@ -7711,18 +7726,6 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz" - integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== - dependencies: - debug "^4.4.0" - encodeurl "^2.0.0" - escape-html "^1.0.3" - on-finished "^2.4.1" - parseurl "^1.3.3" - statuses "^2.0.1" - finalhandler@1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" @@ -7762,6 +7765,18 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" @@ -7898,31 +7913,7 @@ form-data-encoder@^1.7.2: resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.9.0.tgz" integrity sha512-rahaRMkN8P8d/tgK/BLPX+WBVM27NbvdXBxqQujBtkDAIFspaRqN7Od7lfdGQA6KAD+f82fYCLBq1ipvcu8qLw== -form-data@^2.2.0: - version "2.5.5" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" - integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.35" - safe-buffer "^5.2.1" - -form-data@^2.5.0: - version "2.5.5" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" - integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.35" - safe-buffer "^5.2.1" - -form-data@^2.5.5: +form-data@^2.2.0, form-data@^2.5.0, form-data@^2.5.5: version "2.5.5" resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== @@ -7958,16 +7949,16 @@ forwarded@0.2.0: resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz" - integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== - fresh@0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fs-extra@^10.0.1: version "10.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" @@ -8064,7 +8055,7 @@ gaze@^1.1.0: dependencies: globule "^1.0.0" -gcp-metadata@^5.2.0, gcp-metadata@^5.3.0: +gcp-metadata@^5.3.0: version "5.3.0" resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz" integrity sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w== @@ -8247,19 +8238,7 @@ glob@~5.0.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~7.1.1: - version "7.1.7" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@~7.1.6: +glob@~7.1.1, glob@~7.1.6: version "7.1.7" resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -8306,7 +8285,7 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@^11.0.3, globby@^11.1.0, globby@11.1.0: +globby@11.1.0, globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -8406,16 +8385,16 @@ google-p12-pem@^4.0.0: dependencies: node-forge "^1.3.1" -google-protobuf@^3.21.2: - version "3.21.4" - resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz" - integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== - google-protobuf@3.15.8: version "3.15.8" resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.8.tgz" integrity sha512-2jtfdqTaSxk0cuBJBtTTWsot4WtR9RVr2rXg7x7OoqiuOKopPrwXpM1G4dXIkLcUNRh3RKzz76C8IOkksZSeOw== +google-protobuf@^3.21.2: + version "3.21.4" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz" + integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== + gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" @@ -8431,7 +8410,7 @@ graphemer@^1.4.0: resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -"graphql-middleware@^2.0.0 || ^3.0.0 || ^4.0.0 || ^6.0.0", graphql-middleware@^6.1.33: +graphql-middleware@^6.1.33: version "6.1.35" resolved "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.1.35.tgz" integrity sha512-azawK7ApUYtcuPGRGBR9vDZu795pRuaFhO5fgomdJppdfKRt7jwncuh0b7+D3i574/4B+16CNWgVpnGVlg3ZCg== @@ -8475,7 +8454,7 @@ graphql-shield@^7.6.4: tslib "^2.4.0" yup "^0.32.0" -"graphql-subscriptions@^1.0.0 || ^2.0.0 || ^3.0.0", graphql-subscriptions@^2.0.0: +graphql-subscriptions@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz" integrity sha512-s6k2b8mmt9gF9pEfkxsaO1lTxaySfKoEJzEfmwguBbQ//Oq23hIXCfR1hm4kdh5hnR20RdwB+s3BCb+0duHSZA== @@ -8499,16 +8478,23 @@ graphql-tools@^9.0.0: optionalDependencies: "@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~3.11.0 || ~3.12.0 || ~3.13.0" -graphql-ws@^5.13.1, graphql-ws@^5.5.5, "graphql-ws@^5.5.5 || ^6.0.3": +graphql-ws@^5.13.1: version "5.16.2" resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.2.tgz" integrity sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ== -"graphql@^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql@^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql@^14.2.1 || ^15.0.0 || ^16.0.0", "graphql@^14.6.0 || ^15.0.0 || ^16.0.0", "graphql@^15.0.0 || ^16.0.0", "graphql@^15.3.0 || ^16.0.0", "graphql@^15.7.2 || ^16.0.0", graphql@^16.2.0, graphql@^16.3.0, graphql@^16.8.0, "graphql@>=0.11 <=16", "graphql@14.x || 15.x || 16.x": +graphql@^16.3.0, graphql@^16.8.0: version "16.11.0" resolved "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz" integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== +grpc-tools@^1.12.4: + version "1.13.0" + resolved "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.0.tgz" + integrity sha512-7CbkJ1yWPfX0nHjbYG58BQThNhbICXBZynzCUxCb3LzX5X9B3hQbRY2STiRgIEiLILlK9fgl0z0QVGwPCdXf5g== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.5" + grpc_tools_node_protoc_ts@^5.3.3: version "5.3.3" resolved "https://registry.npmjs.org/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-5.3.3.tgz" @@ -8517,13 +8503,6 @@ grpc_tools_node_protoc_ts@^5.3.3: google-protobuf "3.15.8" handlebars "4.7.7" -grpc-tools@^1.12.4: - version "1.13.0" - resolved "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.0.tgz" - integrity sha512-7CbkJ1yWPfX0nHjbYG58BQThNhbICXBZynzCUxCb3LzX5X9B3hQbRY2STiRgIEiLILlK9fgl0z0QVGwPCdXf5g== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.5" - grunt-cli@~1.4.3: version "1.4.3" resolved "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz" @@ -8644,7 +8623,7 @@ grunt-sass@^3.0.2: resolved "https://registry.npmjs.org/grunt-sass/-/grunt-sass-3.1.0.tgz" integrity sha512-90s27H7FoCDcA8C8+R0GwC+ntYD3lG6S/jqcavWm3bn9RiJTmSfOvfbFa1PXx4NbBWuiGQMLfQTj/JvvqT5w6A== -grunt@>=0.4.5, grunt@>=1, grunt@>=1.4.1, grunt@~1.5.3: +grunt@~1.5.3: version "1.5.3" resolved "https://registry.npmjs.org/grunt/-/grunt-1.5.3.tgz" integrity sha512-mKwmo4X2d8/4c/BmcOETHek675uOqw0RuA/zy12jaspWqvTp4+ZeQF1W+OTpcbncnaBsfbQJ6l0l4j+Sn/GmaQ== @@ -8699,25 +8678,25 @@ gzip-size@^5.1.1: duplexer "^0.1.1" pify "^4.0.1" -handlebars@^4.7.7: - version "4.7.8" - resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz" - integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" - neo-async "^2.6.2" + neo-async "^2.6.0" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: uglify-js "^3.1.4" -handlebars@4.7.7: - version "4.7.7" - resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== +handlebars@^4.7.7: + version "4.7.8" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== dependencies: minimist "^1.2.5" - neo-async "^2.6.0" + neo-async "^2.6.2" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: @@ -8834,11 +8813,6 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hono@^4: - version "4.11.5" - resolved "https://registry.npmjs.org/hono/-/hono-4.11.5.tgz" - integrity sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g== - hooker@~0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" @@ -8884,6 +8858,22 @@ http-basic@^8.1.1: http-response-object "^3.0.1" parse-cache-control "^1.0.1" +http-cache-semantics@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz" @@ -8905,17 +8895,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - http-parser-js@>=0.5.1: version "0.5.10" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz" @@ -8988,7 +8967,14 @@ ibex-client@^2.2.0: api "^6.1.2" node-cache "^5.1.2" -iconv-lite@^0.6.3: +iconv-lite@0.4.24, iconv-lite@~0.4.13: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -9002,20 +8988,6 @@ iconv-lite@^0.7.0, iconv-lite@~0.7.0: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -iconv-lite@~0.4.13, iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" @@ -9051,7 +9023,7 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@1.4.2: +import-in-the-middle@1.4.2, import-in-the-middle@1.7.1: version "1.4.2" resolved "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz" integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== @@ -9061,16 +9033,6 @@ import-in-the-middle@1.4.2: cjs-module-lexer "^1.2.2" module-details-from-path "^1.0.3" -import-in-the-middle@1.7.1: - version "1.7.1" - resolved "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz" - integrity sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg== - dependencies: - acorn "^8.8.2" - acorn-import-assertions "^1.9.0" - cjs-module-lexer "^1.2.2" - module-details-from-path "^1.0.3" - import-local@^3.0.2: version "3.2.0" resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" @@ -9097,7 +9059,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -9136,7 +9098,7 @@ interpret@~1.1.0: resolved "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz" integrity sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA== -invoices@^3.0.0, invoices@3.0.0: +invoices@3.0.0, invoices@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/invoices/-/invoices-3.0.0.tgz" integrity sha512-/WDTkfU2RMelQpQ54BwZssqGXYNWbPnWkZ/9QV57vAvD3RLdCDbhDuucOGti8CK3sgk8nmhRV6V0WfMrxojMmA== @@ -9155,7 +9117,7 @@ ioredis-cache@^2.0.0: dependencies: ioredis "4 - 5" -ioredis@^5.3.2, "ioredis@4 - 5": +"ioredis@4 - 5", ioredis@^5.3.2: version "5.8.2" resolved "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz" integrity sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q== @@ -9175,16 +9137,16 @@ ip-address@^10.0.1: resolved "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz" integrity sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA== -ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - ipaddr.js@1.9.1: 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== +ipaddr.js@^2.1.0: + version "2.2.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" @@ -9603,11 +9565,6 @@ jackspeak@^4.1.1: dependencies: "@isaacs/cliui" "^8.0.2" -jest_workaround@^0.79.19: - version "0.79.19" - resolved "https://registry.npmjs.org/jest_workaround/-/jest_workaround-0.79.19.tgz" - integrity sha512-g/MtKSwyb4Ohnd5GHeJaduTgznkyst81x+eUBGOSGK7f8doWuRMPpt6XM/13sM2jLB2QNzT/7Djj7o2PhsozIA== - jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz" @@ -9733,6 +9690,24 @@ jest-get-type@^29.6.3: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== +jest-haste-map@30.2.0: + version "30.2.0" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz" + integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== + dependencies: + "@jest/types" "30.2.0" + "@types/node" "*" + anymatch "^3.1.3" + fb-watchman "^2.0.2" + graceful-fs "^4.2.11" + jest-regex-util "30.0.1" + jest-util "30.2.0" + jest-worker "30.2.0" + micromatch "^4.0.8" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.3" + jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz" @@ -9752,24 +9727,6 @@ jest-haste-map@^29.7.0: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@30.2.0: - version "30.2.0" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz" - integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== - dependencies: - "@jest/types" "30.2.0" - "@types/node" "*" - anymatch "^3.1.3" - fb-watchman "^2.0.2" - graceful-fs "^4.2.11" - jest-regex-util "30.0.1" - jest-util "30.2.0" - jest-worker "30.2.0" - micromatch "^4.0.8" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.3" - jest-junit@^16.0.0: version "16.0.0" resolved "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz" @@ -9827,16 +9784,16 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - jest-regex-util@30.0.1: version "30.0.1" resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz" integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz" @@ -9845,7 +9802,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@*, jest-resolve@^29.7.0: +jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -9941,18 +9898,6 @@ jest-snapshot@^29.7.0: pretty-format "^29.7.0" semver "^7.5.3" -jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@30.2.0: version "30.2.0" resolved "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz" @@ -9965,6 +9910,18 @@ jest-util@30.2.0: graceful-fs "^4.2.11" picomatch "^4.0.2" +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz" @@ -9991,16 +9948,6 @@ jest-watcher@^29.7.0: jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@30.2.0: version "30.2.0" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz" @@ -10012,7 +9959,17 @@ jest-worker@30.2.0: merge-stream "^2.0.0" supports-color "^8.1.1" -jest@*, jest@^29.7.0: +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -10022,6 +9979,11 @@ jest@*, jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" +jest_workaround@^0.79.19: + version "0.79.19" + resolved "https://registry.npmjs.org/jest_workaround/-/jest_workaround-0.79.19.tgz" + integrity sha512-g/MtKSwyb4Ohnd5GHeJaduTgznkyst81x+eUBGOSGK7f8doWuRMPpt6XM/13sM2jLB2QNzT/7Djj7o2PhsozIA== + jose@^4.15.4: version "4.15.9" resolved "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz" @@ -10062,7 +10024,7 @@ js-sha1@^0.6.0: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@^3.13.1, js-yaml@~3.14.0: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -10077,14 +10039,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@~3.14.0: - version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js2xmlparser@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz" @@ -10202,16 +10156,16 @@ json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@^3.2.0: - version "3.3.1" - resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz" - integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== - jsonc-parser@3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== +jsonc-parser@^3.2.0: + version "3.3.1" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + jsonfile@^6.0.1: version "6.2.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz" @@ -10386,7 +10340,7 @@ liftup@~3.0.1: rechoir "^0.7.0" resolve "^1.19.0" -lightning@^9.13.3, lightning@9.14.0: +lightning@9.14.0, lightning@^9.13.3: version "9.14.0" resolved "https://registry.npmjs.org/lightning/-/lightning-9.14.0.tgz" integrity sha512-08TEh9McKE6uMlCLulNPPyMzZcMBPzZDo2BMnRL+ImnkKZp8QCjioK4jdsl/41npDTsIgeSYdfk8vUrS0zSZng== @@ -10622,6 +10576,18 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@6.0.0, lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +"lru-cache@7.10.1 - 7.13.1": + version "7.13.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz" + integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== + lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" @@ -10639,18 +10605,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^6.0.0, lru-cache@6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -"lru-cache@7.10.1 - 7.13.1": - version "7.13.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz" - integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ== - lru-memoizer@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz" @@ -10751,7 +10705,7 @@ markdown-it-anchor@^8.6.7: resolved "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz" integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA== -markdown-it@*, markdown-it@^14.1.0: +markdown-it@^14.1.0: version "14.1.0" resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz" integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== @@ -10802,16 +10756,16 @@ mdurl@^2.0.0: resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz" integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== -media-typer@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz" - integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + medici@^6.2.0: version "6.3.3" resolved "https://registry.npmjs.org/medici/-/medici-6.3.3.tgz" @@ -10838,11 +10792,6 @@ memory-pager@^1.0.2: resolved "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz" integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== -merge-descriptors@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz" - integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" @@ -10853,6 +10802,11 @@ merge-descriptors@1.0.3: resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" @@ -10895,24 +10849,19 @@ migrate-mongo@^10.0.0: date-fns "^2.28.0" fn-args "^5.0.0" fs-extra "^10.0.1" - lodash "^4.17.21" - p-each-series "^2.2.0" - -mime-db@^1.54.0: - version "1.54.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - -"mime-db@>= 1.43.0 < 2": - version "1.54.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + lodash "^4.17.21" + p-each-series "^2.2.0" mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" @@ -10927,16 +10876,16 @@ mime-types@^3.0.0, mime-types@^3.0.2: dependencies: mime-db "^1.54.0" -mime@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" - integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== - mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + mimer@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/mimer/-/mimer-2.0.2.tgz" @@ -10957,28 +10906,35 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^10.0.3: - version "10.0.3" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz" - integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== - dependencies: - "@isaacs/brace-expansion" "^5.0.0" - -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2, "minimatch@2 || 3": +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== +minimatch@9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz" + integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + dependencies: + brace-expansion "^2.0.1" + +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" -minimatch@^5.1.0: +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + +minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -10992,34 +10948,13 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimatch@~3.0.2: - version "3.0.8" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" - integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== - dependencies: - brace-expansion "^1.1.7" - -minimatch@~3.0.4: +minimatch@~3.0.2, minimatch@~3.0.4: version "3.0.8" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== dependencies: brace-expansion "^1.1.7" -minimatch@9.0.1: - version "9.0.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz" - integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== - dependencies: - brace-expansion "^2.0.1" - -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" @@ -11032,16 +10967,16 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3, minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - minipass@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3, minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" @@ -11103,7 +11038,18 @@ mongodb-connection-string-url@^3.0.2: "@types/whatwg-url" "^11.0.2" whatwg-url "^14.1.0 || ^13.0.0" -"mongodb@^4.4.1 || ^5.0.0", mongodb@5.9.2: +mongodb@5.8.1: + version "5.8.1" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.8.1.tgz#dc201adfbd6c6d73401cdcf12ebdb75f14771faf" + integrity sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg== + dependencies: + bson "^5.4.0" + mongodb-connection-string-url "^2.6.0" + socks "^2.7.1" + optionalDependencies: + "@mongodb-js/saslprep" "^1.1.0" + +mongodb@5.9.2: version "5.9.2" resolved "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz" integrity sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ== @@ -11123,7 +11069,20 @@ mongodb@^6.1.0: bson "^6.10.4" mongodb-connection-string-url "^3.0.2" -mongoose@^7.8.4, "mongoose@5 - 8": +"mongoose@5 - 8", mongoose@~7.5.1: + version "7.5.4" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.5.4.tgz#c83d0a3de8e1a13c19ce6cc5810bf93f0fb4c2a2" + integrity sha512-u97BOfBOoCxysnH5X0WeF/O39DO1di75dYU75xaSs9mL3Si0qmP0qLWvWpBRdVkiiRVw+eaqJyKwaq6RvKPVZw== + dependencies: + bson "^5.4.0" + kareem "2.5.1" + mongodb "5.8.1" + mpath "0.9.0" + mquery "5.0.0" + ms "2.1.3" + sift "16.0.1" + +mongoose@^7.8.4: version "7.8.7" resolved "https://registry.npmjs.org/mongoose/-/mongoose-7.8.7.tgz" integrity sha512-5Bo4CrUxrPITrhMKsqUTOkXXo2CoRC5tXxVQhnddCzqDMwRXfyStrxj1oY865g8gaekSBhxAeNkYyUSJvGm9Hw== @@ -11141,27 +11100,27 @@ moo@^0.5.1: resolved "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz" integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== -morgan@^1.10.0: - version "1.10.1" - resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz" - integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== +morgan@1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== dependencies: basic-auth "~2.0.1" debug "2.6.9" depd "~2.0.0" on-finished "~2.3.0" - on-headers "~1.1.0" + on-headers "~1.0.2" -morgan@1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== +morgan@^1.10.0: + version "1.10.1" + resolved "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz" + integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== dependencies: basic-auth "~2.0.1" debug "2.6.9" depd "~2.0.0" on-finished "~2.3.0" - on-headers "~1.0.2" + on-headers "~1.1.0" mpath@0.9.0: version "0.9.0" @@ -11175,11 +11134,6 @@ mquery@5.0.0: dependencies: debug "4.x" -ms@^2.1.1, ms@^2.1.3, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" @@ -11190,6 +11144,11 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + mustache@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz" @@ -11210,16 +11169,16 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz" - integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== - negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + neo-async@^2.6.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" @@ -11334,14 +11293,7 @@ node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: dependencies: "@babel/parser" "^7.0.0" -node-source-walk@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/node-source-walk/-/node-source-walk-5.0.2.tgz" - integrity sha512-Y4jr/8SRS5hzEdZ7SGuvZGwfORvNsSsNRwDXx5WisiqzsVfeftDvRgfeqWNgZvWSJbgubTRVRYBzK6UO+ErqjA== - dependencies: - "@babel/parser" "^7.21.4" - -node-source-walk@^5.0.1: +node-source-walk@^5.0.0, node-source-walk@^5.0.1: version "5.0.2" resolved "https://registry.npmjs.org/node-source-walk/-/node-source-walk-5.0.2.tgz" integrity sha512-Y4jr/8SRS5hzEdZ7SGuvZGwfORvNsSsNRwDXx5WisiqzsVfeftDvRgfeqWNgZvWSJbgubTRVRYBzK6UO+ErqjA== @@ -11510,7 +11462,7 @@ oas-validator@^5.0.8: should "^13.2.1" yaml "^1.10.0" -oas@^20.0.0, oas@^20.11.0, oas@^20.5.0: +oas@^20.11.0, oas@^20.5.0: version "20.11.0" resolved "https://registry.npmjs.org/oas/-/oas-20.11.0.tgz" integrity sha512-Eio2qil8z86PD8KJTdyGPN7hbcF3dV0gc5B6mkSQgL3MLMVFE/XyVAmq/9BRh51zkIXOPmZpSIF3nStADbnkHA== @@ -11616,7 +11568,7 @@ on-exit-leak-free@^2.1.0: resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz" integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== -on-finished@^2.4.1, on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -11654,7 +11606,7 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -openapi-types@^12.1.0, openapi-types@>=7: +openapi-types@^12.1.0: version "12.1.3" resolved "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== @@ -11929,16 +11881,6 @@ path-scurry@^2.0.0: lru-cache "^11.0.0" minipass "^7.1.2" -path-to-regexp@^6.2.0: - version "6.3.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz" - integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== - -path-to-regexp@^8.0.0: - version "8.3.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz" - integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== - path-to-regexp@0.1.12: version "0.1.12" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" @@ -11949,6 +11891,16 @@ path-to-regexp@0.1.7: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== + +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -11959,16 +11911,16 @@ pg-cloudflare@^1.2.7: resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz" integrity sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg== -pg-connection-string@^2.9.1: - version "2.9.1" - resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz" - integrity sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w== - pg-connection-string@2.6.1: version "2.6.1" resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz" integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg== +pg-connection-string@^2.9.1: + version "2.9.1" + resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz" + integrity sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w== + pg-int8@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz" @@ -11995,7 +11947,7 @@ pg-types@2.2.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@^8.11.3, pg@>=8.0: +pg@^8.11.3: version "8.16.3" resolved "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz" integrity sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw== @@ -12078,7 +12030,7 @@ pino-std-serializers@^6.0.0, pino-std-serializers@^6.2.2: resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz" integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== -pino@^8.17.1, pino@8.21.0: +pino@8.21.0, pino@^8.17.1: version "8.21.0" resolved "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz" integrity sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q== @@ -12148,7 +12100,7 @@ postcss-values-parser@^6.0.2: is-url-superb "^4.0.0" quote-unquote "^1.0.0" -postcss@^8.1.7, postcss@^8.2.9, postcss@^8.4.19, postcss@^8.4.23: +postcss@^8.1.7, postcss@^8.4.19, postcss@^8.4.23: version "8.5.6" resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz" integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== @@ -12233,7 +12185,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.0.3, prettier@>=3.0.0: +prettier@^3.0.3: version "3.6.2" resolved "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== @@ -12338,28 +12290,10 @@ protobufjs-cli@1.1.1: tmp "^0.2.1" uglify-js "^3.7.7" -protobufjs@^7.0.0, protobufjs@^7.2.4, protobufjs@^7.2.5, protobufjs@^7.5.3: - version "7.5.4" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz" - integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - -protobufjs@7.2.4: - version "7.2.4" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz" - integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== +protobufjs@7.2.4, protobufjs@7.2.5, protobufjs@^7.0.0, protobufjs@^7.2.4, protobufjs@^7.2.5, protobufjs@^7.5.3: + version "7.2.5" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" + integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -12446,13 +12380,6 @@ pushdata-bitcoin@1.0.1: dependencies: bitcoin-ops "^1.3.0" -qs@^6.10.5, qs@^6.12.3, qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@^6.9.4: - version "6.14.1" - resolved "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz" - integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== - dependencies: - side-channel "^1.1.0" - qs@6.11.0: version "6.11.0" resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" @@ -12467,6 +12394,13 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" +qs@^6.10.5, qs@^6.12.3, qs@^6.14.0, qs@^6.14.1, qs@^6.4.0, qs@^6.9.4: + version "6.14.1" + resolved "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz" + integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== + dependencies: + side-channel "^1.1.0" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -12511,24 +12445,6 @@ rate-limiter-flexible@^3.0.0: resolved "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-3.0.6.tgz" integrity sha512-tlvbee6lyse/XTWmsuBDS4MT8N65FyM151bPmQlFyfhv9+RIHs7d3rSTXoz0j35H910dM01mH0yTIeWYo8+aAw== -raw-body@^3.0.0, raw-body@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz" - integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== - dependencies: - bytes "~3.1.2" - http-errors "~2.0.1" - iconv-lite "~0.7.0" - unpipe "~1.0.0" - -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz" - integrity sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg== - dependencies: - bytes "1" - string_decoder "0.10" - raw-body@2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" @@ -12549,6 +12465,24 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + +raw-body@~1.1.0: + version "1.1.7" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz" + integrity sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg== + dependencies: + bytes "1" + string_decoder "0.10" + rc@^1.2.7: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" @@ -12569,7 +12503,7 @@ react-is@^18.0.0: resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react@*, "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", react@^18.2.0: +react@^18.2.0: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -12589,25 +12523,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.2.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1: - version "3.6.2" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^3.4.0: - version "3.6.2" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -12686,7 +12602,9 @@ redis@3.1.2: redis-parser "^3.0.0" redlock@^5.0.0-beta.2: - version "v5.0.0-beta.2" + version "5.0.0-beta.2" + resolved "https://registry.yarnpkg.com/redlock/-/redlock-5.0.0-beta.2.tgz#a629c07e07d001c0fdd9f2efa614144c4416fe44" + integrity sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw== dependencies: node-abort-controller "^3.0.1" @@ -12905,14 +12823,7 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== -rimraf@^2.6.1: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -rimraf@^2.6.2: +rimraf@^2.6.1, rimraf@^2.6.2: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12963,20 +12874,15 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-identifier@^0.4.1: version "0.4.2" @@ -13052,17 +12958,7 @@ secure-json-parse@^2.4.0: resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -semver@^6.0.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.1: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -13072,23 +12968,6 @@ semver@^7.1.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semve resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== -send@^1.1.0, send@^1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/send/-/send-1.2.1.tgz" - integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== - dependencies: - debug "^4.4.3" - encodeurl "^2.0.0" - escape-html "^1.0.3" - etag "^1.8.1" - fresh "^2.0.0" - http-errors "^2.0.1" - mime-types "^3.0.2" - ms "^2.1.3" - on-finished "^2.4.1" - range-parser "^1.2.1" - statuses "^2.0.2" - send@0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" @@ -13127,6 +13006,23 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.npmjs.org/send/-/send-1.2.1.tgz" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" @@ -13140,7 +13036,17 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@^1.14.1, serve-static@1.16.2: +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +serve-static@1.16.2, serve-static@^1.14.1: version "1.16.2" resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz" integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== @@ -13160,16 +13066,6 @@ serve-static@^2.2.0: parseurl "^1.3.3" send "^1.2.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" @@ -13216,16 +13112,16 @@ setimmediate@^1.0.4, setimmediate@^1.0.5: resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -setprototypeof@~1.2.0, setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== +setprototypeof@1.2.0, setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + sha.js@^2.4.0, sha.js@^2.4.11: version "2.4.12" resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz" @@ -13398,19 +13294,11 @@ sonic-boom@^3.0.0, sonic-boom@^3.7.0: dependencies: atomic-sleep "^1.0.0" -source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0": +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-support@^0.5.12: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -13419,6 +13307,14 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@^0.5.12: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@^0.5.3: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" @@ -13518,25 +13414,20 @@ static-eval@2.0.2: dependencies: escodegen "^1.8.1" -statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" - integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== - -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -statuses@~1.5.0: +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== stop-iteration-iterator@^1.1.0: version "1.1.0" @@ -13573,25 +13464,6 @@ stream-to-array@^2.3.0: dependencies: any-promise "^1.1.0" -string_decoder@^1.1.1, string_decoder@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -string_decoder@0.10: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -13669,6 +13541,25 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@0.10: + version "0.10.31" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + +string_decoder@^1.1.1, string_decoder@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + stringify-object@^3.2.1: version "3.3.0" resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" @@ -13721,7 +13612,7 @@ strip-final-newline@^2.0.0: resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^2.0.0: +strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== @@ -13731,11 +13622,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - strnum@^1.1.1: version "1.1.2" resolved "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz" @@ -13778,14 +13664,7 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.1.1: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -13982,13 +13861,6 @@ tiny-lr@^1.1.1: object-assign "^4.1.0" qs "^6.4.0" -tiny-secp256k1@^2.2.3: - version "2.2.4" - resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.4.tgz" - integrity sha512-FoDTcToPqZE454Q04hH9o2EhxWsm7pOSpicyHkgTwKhdKWdsTUuqfP5MLq3g+VjAtl2vSx6JpXGdwA2qpYkI0Q== - dependencies: - uint8array-tools "0.0.7" - tiny-secp256k1@2.2.2: version "2.2.2" resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.2.tgz" @@ -14003,10 +13875,12 @@ tiny-secp256k1@2.2.3: dependencies: uint8array-tools "0.0.7" -tmp@^0.2.1: - version "0.2.5" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz" - integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== +tiny-secp256k1@^2.2.3: + version "2.2.4" + resolved "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.4.tgz" + integrity sha512-FoDTcToPqZE454Q04hH9o2EhxWsm7pOSpicyHkgTwKhdKWdsTUuqfP5MLq3g+VjAtl2vSx6JpXGdwA2qpYkI0Q== + dependencies: + uint8array-tools "0.0.7" tmp@0.2.1: version "0.2.1" @@ -14015,6 +13889,11 @@ tmp@0.2.1: dependencies: rimraf "^3.0.0" +tmp@^0.2.1: + version "0.2.5" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" @@ -14036,7 +13915,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@~1.0.1, toidentifier@1.0.1: +toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -14121,7 +14000,7 @@ ts-node-dev@^2.0.0: ts-node "^10.4.0" tsconfig "^7.0.0" -ts-node@^10.4.0, ts-node@^10.9.1, ts-node@>=9.0.0: +ts-node@^10.4.0, ts-node@^10.9.1: version "10.9.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -14140,17 +14019,7 @@ ts-node@^10.4.0, ts-node@^10.9.1, ts-node@>=9.0.0: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.10.1: - version "3.15.0" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tsconfig-paths@^3.15.0: +tsconfig-paths@^3.10.1, tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== @@ -14230,7 +14099,7 @@ twilio@^4.17.0: url-parse "^1.5.9" xmlbuilder "^13.0.2" -type-check@^0.4.0: +type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== @@ -14244,18 +14113,16 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - type-detect@4.0.8: 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== +type-fest@4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.3.2.tgz" + integrity sha512-VpwuOgnTsQUUWi0id8Hl4/xiQ+OoaeJGe8dnFjzubJYe/lOc2/d1Qx/d3FqWR0FlpOG/cvukAXfB12A49Y4iiA== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" @@ -14266,11 +14133,6 @@ type-fest@^0.21.3: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.3.2.tgz" - integrity sha512-VpwuOgnTsQUUWi0id8Hl4/xiQ+OoaeJGe8dnFjzubJYe/lOc2/d1Qx/d3FqWR0FlpOG/cvukAXfB12A49Y4iiA== - type-is@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz" @@ -14348,30 +14210,20 @@ typeforce@^1.11.3, typeforce@^1.11.5, typeforce@^1.18.0: resolved "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@*, "typescript@^3.9.5 || ^4.9.5 || ^5", typescript@^5.2.2, typescript@>=2.7, "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.2.0, typescript@>=4.7.2, typescript@>=5.0.0: - version "5.9.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== - -typescript@^3.9.10: +typescript@^3.9.10, typescript@^3.9.7: version "3.9.10" resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typescript@^3.9.7: - version "3.9.10" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== - -typescript@^4.0.0: +typescript@^4.0.0, typescript@^4.9.5: version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.2.2: + version "5.9.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" @@ -14421,16 +14273,16 @@ underscore.string@~3.3.5: sprintf-js "^1.1.1" util-deprecate "^1.0.2" -underscore@~1.13.2: - version "1.13.7" - resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz" - integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== - underscore@1.12.1: version "1.12.1" resolved "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz" integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== +underscore@~1.13.2: + version "1.13.7" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + undici-types@~6.21.0: version "6.21.0" resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" @@ -14493,7 +14345,7 @@ unixify@1.0.0: dependencies: normalize-path "^2.1.1" -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -14564,22 +14416,12 @@ uuid@^10.0.0: resolved "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz" integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== -uuid@^8.0.0: - version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -uuid@^8.3.2: +uuid@^8.0.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: - version "9.0.1" - resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== - -uuid@^9.0.1: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -14640,17 +14482,17 @@ validate.io-number@^1.0.3: resolved "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz" integrity sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg== -value-or-promise@^1.0.12: - version "1.0.12" - resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz" - integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== - value-or-promise@1.0.11: version "1.0.11" resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz" integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== -varuint-bitcoin@^1.1.2, varuint-bitcoin@1.1.2: +value-or-promise@^1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz" + integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== + +varuint-bitcoin@1.1.2, varuint-bitcoin@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz" integrity sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw== @@ -14674,7 +14516,7 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -wcwidth@^1.0.1, wcwidth@>=1.0.1: +wcwidth@>=1.0.1, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== @@ -14907,6 +14749,11 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" +ws@8.14.2: + version "8.14.2" + resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + ws@^3.2.0: version "3.3.3" resolved "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz" @@ -14921,11 +14768,6 @@ ws@^8.14.1: resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== -ws@8.14.2: - version "8.14.2" - resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== - xml@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz" @@ -15040,7 +14882,7 @@ zod-to-json-schema@^3.25.0: resolved "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz" integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== -zod@^3.0.0, zod@^3.22.0, zod@^3.22.2, "zod@^3.25 || ^4", "zod@^3.25 || ^4.0": +zod@^3.22.0, zod@^3.22.2, "zod@^3.25 || ^4.0": version "3.25.76" resolved "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== From d448b2b8d2f402b8a5653fc605496738afe1b310 Mon Sep 17 00:00:00 2001 From: Island Bitcoin <34528298+islandbitcoin@users.noreply.github.com> Date: Tue, 26 May 2026 09:10:08 -0400 Subject: [PATCH 16/22] docs(bridge): add operations and cutover runbooks (#371) * docs(bridge): add operations runbook * docs(bridge): add cutover day runbook * docs(bridge): tighten operations runbook safety * docs(bridge): align cutover runbook with lifecycle behavior --- docs/bridge-integration/CUTOVER-RUNBOOK.md | 509 +++++++++++++++++ docs/bridge-integration/OPERATIONS.md | 623 +++++++++++++++++++++ 2 files changed, 1132 insertions(+) create mode 100644 docs/bridge-integration/CUTOVER-RUNBOOK.md create mode 100644 docs/bridge-integration/OPERATIONS.md diff --git a/docs/bridge-integration/CUTOVER-RUNBOOK.md b/docs/bridge-integration/CUTOVER-RUNBOOK.md new file mode 100644 index 000000000..a608e7f1c --- /dev/null +++ b/docs/bridge-integration/CUTOVER-RUNBOOK.md @@ -0,0 +1,509 @@ +# Cash Wallet Cutover Day Runbook + +This runbook is the one-time execution script for moving Flash Cash Wallet users from legacy IBEX USD presentation to IBEX USDT-backed Cash Wallet operation. + +Use `OPERATIONS.md` as the reference manual for steady-state Bridge operations, replay, reconciliation, webhook incidents, and general deploy checks. This document is intentionally focused on cutover-day decisions and sequencing. + +## Scope + +This runbook covers: + +- T-7 days through T-0 readiness checks. +- T-1 hour go/no-go decision. +- T-0 cutover execution. +- Hard abort criteria. +- Verification after `IN_PROGRESS` and `COMPLETE`. +- T+1 hour and T+24 hour reconciliation. +- Handoff to rollback planning when abort criteria are met. + +Out of scope: + +- Bridge steady-state operations. See `OPERATIONS.md`. +- Server implementation. See ENG-345. +- Mobile implementation. See ENG-346. +- Legal/customer communications approval. See COM-44. +- Full rollback procedure. See ENG-364. + +## Cutover Principles + +- No silent ambiguity: every go/no-go decision needs a named owner and timestamp. +- Do not manually mutate user wallet state during cutover without a written repair plan and second reviewer. +- Keep old-client compatibility intact throughout the cutover. Clients without `X-Flash-Client-Capabilities: cash-wallet-usdt-v1` must continue to see legacy `USD`. +- Capable clients with `X-Flash-Client-Capabilities: cash-wallet-usdt-v1` see `USDT` only after their account has completed migration, or after global cutover is `COMPLETE`. +- Do not mark cutover `COMPLETE` until migration, wallet presentation, reconciliation, and support readiness all pass. +- If rollback is needed after irreversible wallet/default changes, treat it as a data repair project, not a normal deploy rollback. + +## Roles + +Fill this table before the staging rehearsal and again before production cutover. + +| Role | Name | Backup | Required at T-1h? | Required at T-0? | +| --- | --- | --- | --- | --- | +| Cutover commander | Dread | TBD | Yes | Yes | +| Ops executor | Olaniran | TBD | Yes | Yes | +| Backend owner | TBD | TBD | Yes | Yes | +| Mobile owner | Nick | TBD | Yes | On call | +| Support lead | TBD | TBD | Yes | On call | +| Comms/legal approver | TBD | TBD | Yes | No | +| Incident commander fallback | TBD | TBD | Yes | Yes | + +## Required Artifacts + +Create or link these before T-1 day: + +- Production release commit SHA. +- Bridge operations `OPERATIONS.md` branch/commit. +- Staging rehearsal notes. +- Backend deployment evidence. +- Mobile rollout status for ENG-346. +- COM-44 approved comms. +- Dashboard links. +- PagerDuty/Slack on-call schedule. +- Bridge dashboard/config screenshots or internal references. +- Cutover run ID. +- Config snapshot reference. +- Rollback/repair owner for ENG-364. + +Do not paste secrets, bearer tokens, Bridge API keys, webhook public keys copied from private channels, replay secrets, or Bruno local environment files into this runbook. + +## Dependencies + +Do not proceed to production cutover unless all required dependencies are deployed or explicitly waived by the cutover commander. + +| Dependency | Required state | Owner | Status | +| --- | --- | --- | --- | +| ENG-345 server cutover support | Deployed and smoke-tested | Backend | TBD | +| ENG-346 mobile cutover UX | Shipped or rollout plan approved | Mobile | TBD | +| ENG-296 / ENG-297 USDT wallet support | Deployed and verified | Backend | TBD | +| COM-44 comms/legal | Approved | Comms/legal | TBD | +| Bridge webhook server | Healthy in target env | Ops | TBD | +| Bridge reconciliation | Runs cleanly or known exceptions documented | Ops/backend | TBD | +| Monitoring dashboards | Live | Ops | TBD | +| PagerDuty/Slack on-call | Active | Ops | TBD | +| ENG-364 rollback plan | Draft reviewed or waived | Ops/backend | TBD | + +## T-7 Days Checklist + +- Confirm production cutover date and window. +- Confirm the cutover commander, ops executor, backend owner, mobile owner, support lead, and comms/legal approver. +- Confirm ENG-345 is merged and deployed to staging. +- Confirm ENG-346 status and mobile rollout plan. +- Confirm COM-44 comms are drafted and routed for approval. +- Confirm Bridge dashboard access for the ops executor and backup. +- Confirm production config owner and secret owner. +- Confirm rollback owner and decision window. +- Confirm customer support has an escalation channel and internal FAQ draft. +- Schedule staging rehearsal. + +Exit criteria: + +- All named roles have accepted ownership. +- Dependencies have owners and target dates. +- Staging rehearsal is scheduled. + +## T-3 Days Checklist + +- Deploy the release candidate to staging. +- Run Bridge GraphQL smoke checks. +- Run Bridge webhook `/health`. +- Run replay dry-run for a narrow recent window. +- Run Bridge reconciliation with a 24-hour window. +- Verify no unresolved high-severity `bridge_without_ibex` orphan. +- Verify old-client no-header wallet query returns legacy `USD`. +- Verify capable-client wallet query matches the staged cutover phase: `USD` before `IN_PROGRESS`, `USDT` only for migrated accounts during `IN_PROGRESS`, and `USDT` after `COMPLETE`. +- Verify admin `cashWalletCutover` query works. +- Confirm dashboards show webhook status, reconciliation orphan counts, Bridge API errors, and cutover state changes. +- Confirm support can identify and escalate cutover issues. + +Exit criteria: + +- Staging passes the smoke matrix. +- Any known issue has an owner and explicit production go/no-go impact. + +## T-1 Day Checklist + +- Freeze unrelated Bridge/Cash Wallet deploys unless approved by the cutover commander. +- Confirm production release SHA. +- Confirm production config snapshot. +- Confirm production Bridge webhook public keys and replay secret are present. +- Confirm mobile rollout state. +- Confirm customer/support comms are approved. +- Confirm PagerDuty/Slack rotation. +- Confirm the rollback/repair decision tree is available. +- Confirm a staging rehearsal has completed, or record the explicit waiver. +- Create the production cutover run ID. + +Exit criteria: + +- Cutover commander signs off. +- Ops executor signs off. +- Backend owner signs off. +- Support lead signs off. +- Comms/legal approver signs off if user-facing comms are planned. + +## T-1 Hour Go/No-Go + +Record the decision in the issue, deployment channel, or operator log before touching production state. + +| Gate | Go condition | Abort condition | Owner | Result | +| --- | --- | --- | --- | --- | +| Release SHA | Expected SHA deployed | Wrong or unknown SHA | Backend | TBD | +| Migrations | Applied with no failure | Pending or failed migration | Backend | TBD | +| Webhook health | `/health` returns OK | Unhealthy webhook server | Ops | TBD | +| Bridge API | Normal latency/error rate | Bridge outage or auth failures | Ops | TBD | +| Reconciliation | No high-severity unresolved orphan | New critical orphan growth | Backend/Ops | TBD | +| Old-client compatibility | No-header query returns `USD` | No-header query returns `USDT` or errors | Backend | TBD | +| Capable-client behavior | Before `IN_PROGRESS`, capability query returns legacy `USD`; during cutover, migrated canary returns `USDT`; after `COMPLETE`, capability query returns `USDT` | Result does not match current cutover phase, or query errors | Backend/Mobile | TBD | +| Mobile readiness | ENG-346 status accepted | Unknown or unacceptable rollout risk | Mobile | TBD | +| Support readiness | Support lead online | Support not staffed | Support | TBD | +| On-call | PagerDuty/Slack rotation active | No accountable responder | Ops | TBD | +| Comms | Approved or waived | Required comms not approved | Comms/legal | TBD | + +Decision: + +- `GO`: proceed to T-0 execution. +- `NO-GO`: keep state at `PRE`, announce abort, record blocker and owner. +- `GO WITH WAIVER`: only allowed if the cutover commander records the waived gate, reason, risk owner, and rollback implication. + +Hard abort criteria: + +- Production webhook server is unhealthy. +- Bridge API auth fails. +- Old-client no-header query does not return legacy `USD`. +- Capable-client query does not return `USDT`. +- Reconciliation shows unresolved high-severity deposit mismatch. +- Required decision owner is unavailable. +- Support cannot staff the cutover window. +- Unknown production commit or config. + +## T-0 Execution + +Use one shared operator log. Record timestamps, operator, command/tool used, and result for each step. + +Set these variables once and record them in the operator log: + +```bash +export CUTOVER_VERSION=345 +export CUTOVER_RUN_ID="" +export OPERATOR="" +export PROD_CONFIG_PATH="" +``` + +Before running any command, confirm `git rev-parse HEAD`, `PROD_CONFIG_PATH`, and the config snapshot's Bridge environment/base URL match the target production environment. + +### 1. Confirm Starting State + +Query current admin cutover state: + +```graphql +query CashWalletCutoverState { + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pausedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } +} +``` + +Expected: + +- `state` is `PRE`. +- `runId` is either absent or matches the planned production run. +- No unexpected `pausedAt` or `pauseReason`. + +If unexpected, stop and resolve before proceeding. + +### 2. Run Final Pre-Migration Smoke Checks + +Run both wallet presentation checks against a controlled production canary. Before `IN_PROGRESS`, both no-header and capability-header requests are expected to remain on legacy `USD`; this verifies the cutover has not started early. + +No capability header expected result: + +- Cash wallet presents as `USD`. +- Legacy balance is USD-compatible. + +Capability header expected result: + +- Before `IN_PROGRESS`: cash wallet still presents as `USD`. +- During `IN_PROGRESS`: only accounts with migration status `complete` or `skipped_already_migrated` present as `USDT`; not-started accounts remain `USD`, and active migrations may block. +- After `COMPLETE`: cash wallet presents as `USDT`. +- When presented as `USDT`, balance uses USDT smallest-unit semantics. + +Example header: + +```text +X-Flash-Client-Capabilities: cash-wallet-usdt-v1 +``` + +Do not proceed if either presentation check fails. + +### 3. Set Cutover `IN_PROGRESS` + +Preview and prepare the production cohort before setting `IN_PROGRESS`: + +```bash +node lib/scripts/cash-wallet-cutover.js preview \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" + +node lib/scripts/cash-wallet-cutover.js prepare \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +Use the operator CLI for production `IN_PROGRESS`; it preserves the lifecycle path used by the cutover worker: + +```bash +node lib/scripts/cash-wallet-cutover.js start \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +The admin `cashWalletCutoverUpdate` mutation and Bruno `03-set-in-progress.bru` file directly mutate state and bypass CLI lifecycle checks. Use them for production `IN_PROGRESS` only as an emergency override with backend-owner approval, a written operator-log entry, and the reason the CLI cannot be used. Track additional start lifecycle hardening through ENG-403. + +Record: + +- Operator. +- Timestamp. +- Run ID. +- Cutover version. +- Commit SHA. +- State response. + +Expected: + +- Public and admin `cashWalletCutover.state` return `IN_PROGRESS`. +- No spike in GraphQL errors. +- Old-client/capable-client presentation still passes. + +### 4. Run Migration + +Run one batch at a time and inspect status after each batch: + +```bash +node lib/scripts/cash-wallet-cutover.js run-batch \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" \ + --worker-id "$OPERATOR-manual-1" \ + --limit 25 + +node lib/scripts/cash-wallet-cutover.js status \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +Repeat `run-batch` until `status` shows no remaining runnable migration records. Preserve each JSON output in the operator log. If a batch exits non-zero or reports `failed` or `requires_operator_review`, stop and invoke the abort/rollback handoff below. + +Record: + +- Command/tool name and exact args. +- Start timestamp. +- End timestamp. +- Cohort size. +- Success count. +- Failure count. +- Checkpoint reference. +- Log reference. + +Monitor: + +- Migration failures. +- GraphQL error rate. +- Bridge webhook failures. +- IBEX deposit/ledger anomalies. +- Cash wallet presentation errors. +- Support escalations. + +If the job fails before irreversible changes, pause and decide whether to resume or abort. If failures occur after irreversible changes, stop manual action and invoke the ENG-364 repair/rollback decision process. + +### 5. Verify During `IN_PROGRESS` + +Before completing: + +- Admin cutover state is `IN_PROGRESS`. +- Migration job reports complete or expected partial state. +- No unresolved migration failures. +- No-header wallet query returns `USD`. +- Capability wallet query returns `USDT`. +- Bridge reconciliation runs successfully. +- No new unresolved high-severity orphan. +- Support has no blocker-level customer escalation. + +Do not set `COMPLETE` if any check fails. + +### 6. Set Cutover `COMPLETE` + +Set the cutover state to `COMPLETE` only after the `IN_PROGRESS` verification passes. + +Use the operator CLI for production `COMPLETE`; it checks failed, manual-review, and runnable migration counts before completing: + +```bash +node lib/scripts/cash-wallet-cutover.js complete \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +The admin `cashWalletCutoverUpdate` mutation and Bruno `04-set-complete.bru` file directly mutate state and bypass completion checks. Use them for production `COMPLETE` only as an emergency override with backend-owner approval, a written operator-log entry, and preserved `status` output proving why the override is safe. + +Record: + +- Operator. +- Timestamp. +- State response. +- Commit SHA. +- Run ID. + +Expected: + +- Admin and public `cashWalletCutover.state` return `COMPLETE`. +- Old clients continue to see legacy `USD`. +- Capable clients see `USDT`. + +### 7. Post-Complete Smoke Checks + +Run immediately after `COMPLETE`: + +- No-header wallet query. +- Capability-header wallet query. +- Cash wallet transaction-history query. +- Known account transaction detail query. +- Bridge reconciliation with at least the cutover window. +- Webhook health check. +- Bridge GraphQL smoke query. + +Expected: + +- No-header clients: legacy `USD` compatibility. +- Capable clients: `USDT`. +- No new critical reconciliation orphan. +- No webhook signature or auth failures. +- No unexpected support escalation. + +## T+1 Hour Checks + +- Re-run Bridge reconciliation. +- Review dashboard error rates. +- Review webhook request status by route. +- Review Bridge API latency and non-2xx rate. +- Review support tickets. +- Spot-check at least three migrated accounts. +- Confirm old-client compatibility still holds. +- Confirm capable-client USDT presentation still holds. +- Record known issues and owners. + +If any high-severity issue appears, open an incident and decide whether to invoke ENG-364. + +## T+24 Hour Checks + +- Run reconciliation with a 24-hour window. +- Review all reconciliation orphans. +- Confirm no orphan needs manual repair. +- Spot-check migrated account balances and recent transaction history. +- Review failed/pending Bridge transfers. +- Review webhook replay logs. +- Review support volume and themes. +- Decide whether to continue, pause, or accelerate legacy sunset planning. +- Schedule postmortem if there was any incident, waiver, failed gate, or customer-impacting issue. + +## Abort and Rollback Handoff + +Abort before `IN_PROGRESS`: + +- Keep cutover state at `PRE`. +- Announce no-go. +- Record blocker, owner, and next review time. + +Abort during `IN_PROGRESS`: + +- Pause or stop the migration worker according to the implementation. +- Preserve logs, checkpoints, and run ID. +- Do not manually mutate wallets. +- Decide whether to resume, complete, or invoke ENG-364. + +Abort after `COMPLETE`: + +- Treat as production incident and data repair. +- Keep client-aware presentation intact unless it is the root cause. +- Invoke ENG-364 rollback/repair process. +- Require written repair plan and second reviewer before changing wallet/default state. + +Rollback handoff packet: + +- Run ID. +- Cutover version. +- Production commit SHA. +- Config snapshot. +- Migration checkpoint. +- Failed gate or incident trigger. +- Affected account count. +- Known affected account IDs, if any. +- Reconciliation output. +- Support impact summary. +- Proposed next action. + +## Staging Rehearsal Log + +Complete before production or record a signed waiver. + +| Date | Environment | Commit SHA | Operator | Result | Notes | +| --- | --- | --- | --- | --- | --- | +| TBD | Staging | TBD | TBD | TBD | TBD | + +Minimum rehearsal evidence: + +- `PRE` state verified. +- `IN_PROGRESS` state set. +- Migration/cutover flow exercised. +- Preview and prepare outputs preserved. +- At least one partial batch run exercised. +- Resume after a stopped or stale worker lock exercised. +- Failed batch handling exercised, including `failed` or `requires_operator_review` operator decision. +- Abort before irreversible changes exercised. +- Abort after partial irreversible changes tabletop completed. +- `COMPLETE` state set. +- No-header wallet check passed. +- Capability-header wallet check passed. +- Old-client compatibility checked during partial migration. +- Reconciliation ran. +- Reconciliation orphan triage path exercised. +- Abort/rollback tabletop completed. + +## Production Signoff + +| Checkpoint | Name | Timestamp | Result | Notes | +| --- | --- | --- | --- | --- | +| T-1d readiness | TBD | TBD | TBD | TBD | +| T-1h go/no-go | TBD | TBD | TBD | TBD | +| `IN_PROGRESS` set | TBD | TBD | TBD | TBD | +| Migration complete | TBD | TBD | TBD | TBD | +| `COMPLETE` set | TBD | TBD | TBD | TBD | +| T+1h check | TBD | TBD | TBD | TBD | +| T+24h check | TBD | TBD | TBD | TBD | + +## Links + +- OPS-50: Cash Wallet Cutover Day go/no-go runbook. +- ENG-345: Server-side Cash Wallet cutover orchestration. +- ENG-346: Mobile Cash Wallet cutover screens and USDT card state. +- ENG-364: Cash Wallet cutover rollback contingency plan. +- COM-44: Customer/legal communications. +- `OPERATIONS.md`: Bridge steady-state operations reference. diff --git a/docs/bridge-integration/OPERATIONS.md b/docs/bridge-integration/OPERATIONS.md new file mode 100644 index 000000000..1b64cd8c0 --- /dev/null +++ b/docs/bridge-integration/OPERATIONS.md @@ -0,0 +1,623 @@ +# Bridge Operations Runbook + +This runbook covers production operation for the Bridge.xyz integration and the related cash wallet cutover work. It is written for deploys, incident response, replay, reconciliation, and cutover execution. + +The code is the source of truth. Older Bridge docs may mention `/bridge/webhooks/*`; the current webhook server exposes `/kyc`, `/deposit`, `/transfer`, `/internal/replay`, and `/health`. + +## Scope + +Bridge enables Flash users to move between USD bank rails and USDT: + +- On-ramp: Bridge virtual account receives USD, Bridge converts to USDT, IBEX credits Flash. +- Off-ramp: Flash initiates Bridge transfer from USDT to a linked external bank account. +- Webhooks: Bridge reports KYC, deposit, and transfer events to Flash. +- Reconciliation: Flash compares Bridge deposits with IBEX deposit records and records orphans. +- Cash wallet cutover: ENG-345 makes USD/USDT wallet presentation client-aware while migration runs. + +## Services + +- GraphQL API: user and admin Bridge operations. +- Bridge webhook server: standalone Express server for Bridge webhooks. +- MongoDB: Bridge account mappings, deposit logs, replay logs, reconciliation orphans, and cutover state. +- Bridge.xyz API: KYC, virtual accounts, events, external accounts, and transfers. +- IBEX: USDT receive addresses and deposit crediting. + +Key code paths: + +- `src/services/bridge/client.ts` +- `src/services/bridge/index.ts` +- `src/services/bridge/webhook-server/index.ts` +- `src/services/bridge/reconciliation.ts` +- `src/scripts/replay-bridge-events.ts` +- `src/scripts/reconcile-bridge-ibex-deposits.ts` +- `src/graphql/admin/root/query/bridge-reconciliation-orphans.ts` +- `src/migrations/20260423000000-bridge-virtual-account-unique-accountid.ts` + +## Required Config + +Bridge config lives under `bridge` in the runtime config. + +Required fields: + +- `bridge.enabled` +- `bridge.apiKey` +- `bridge.baseUrl` +- `bridge.webhook.port` +- `bridge.webhook.publicKeys.kyc` +- `bridge.webhook.publicKeys.deposit` +- `bridge.webhook.publicKeys.transfer` +- `bridge.webhook.timestampSkewMs` + +Optional or defaulted fields: + +- `bridge.minWithdrawalAmount` +- `bridge.timeoutMs` defaults to `10000` +- `bridge.webhook.replaySecret`, or `BRIDGE_WEBHOOK_REPLAY_SECRET` + +Never commit production API keys, replay secrets, webhook public keys copied from private channels, bearer tokens, or Bruno local environment files. + +## Pre-Deploy Checklist + +1. Confirm the exact branch and commit being deployed. +2. Confirm Bridge config is present for the target environment. +3. Confirm webhook public keys match Bridge dashboard values for KYC, deposit, and transfer events. +4. Confirm the webhook replay secret is present for operators, not exposed to clients. +5. Confirm MongoDB migration status. +6. Confirm Bridge API base URL points at the intended environment. +7. Confirm mobile client rollout status for `cash-wallet-usdt-v1` capability. +8. Confirm rollback owner, deploy owner, and decision owner. + +Useful checks: + +```bash +git rev-parse HEAD +yarn build +git diff --check +``` + +Full `yarn test:unit` and `yarn tsc-check` may currently include unrelated repo debt. For Bridge deploy confidence, use focused Bridge and cutover suites plus build and lint checks. + +## Deployment Order + +Deploy in this order: + +1. Run database migrations. +2. Deploy GraphQL/backend. +3. Deploy Bridge webhook server. +4. Verify webhook health. +5. Verify GraphQL Bridge surfaces. +6. Verify replay dry run. +7. Run reconciliation. +8. Only after the above is healthy, execute cash wallet cutover phases. + +### 1. Run Migrations + +Use the repo's normal Mongo migration process for the target environment. The Bridge virtual account uniqueness migration must be applied before relying on production Bridge account mappings: + +```bash +./scripts/mongodb-migrate.sh +``` + +Confirm no migration failed before continuing. + +### 2. Deploy GraphQL + +Deploy the backend with Bridge config enabled for the environment. + +Post-deploy smoke checks: + +- authenticated `bridgeKycStatus` +- authenticated `bridgeVirtualAccount` +- authenticated `bridgeExternalAccounts` +- admin `bridgeReconciliationOrphans` +- shared `cashWalletCutover` after ENG-345 is deployed + +Example shape: + +```graphql +query BridgeSmoke { + bridgeKycStatus { + status + } + bridgeVirtualAccount { + id + status + } + bridgeExternalAccounts { + id + status + } +} +``` + +### 3. Deploy Webhook Server + +The package script for local/dev execution is: + +```bash +yarn bridge-webhook +``` + +Production should run the same server entrypoint with the production config and process supervisor. The server starts from: + +```text +src/servers/bridge-webhook-server.ts +``` + +Current routes: + +- `GET /health` +- `POST /kyc` +- `POST /deposit` +- `POST /transfer` +- `POST /internal/replay` + +Health check: + +```bash +curl -fsS "$BRIDGE_WEBHOOK_URL/health" +``` + +Expected response: + +```json +{"status":"ok","service":"bridge-webhook"} +``` + +### 4. Verify Webhook Signatures + +Bridge webhooks must include: + +- `X-Webhook-Signature` + +The expected header format is: + +```text +X-Webhook-Signature: t=,v0= +``` + +Flash is intended to verify the Bridge signature for `.` using RSA-SHA256 and the configured public key for the event family. Before production cutover, verify this behavior with a Bridge-provided fixture or captured sandbox webhook because the current middleware must match Bridge's exact signing input. Track this through ENG-402. + +Expected behavior: + +- `200`: accepted or idempotent duplicate. +- `400`: malformed payload. +- `401`: invalid or missing signature/timestamp. +- `500`: retryable server failure. + +If signature failures spike after deploy, treat it as a config or raw-body parsing incident until proven otherwise. + +## Replay Missed Bridge Events + +Replay is for missed or failed Bridge webhooks. Always dry-run first. + +Build first: + +```bash +yarn build +``` + +Production replay must use the target environment config. Do not use `dev/config/base-config.yaml` for production; that file is for local/non-production testing. + +Before any replay, record these values in the operator log: + +```bash +test -n "$PROD_CONFIG_PATH" +test -n "$BRIDGE_WEBHOOK_URL" +git rev-parse HEAD +printf 'configPath=%s\nwebhookUrl=%s\ndryRun=%s\n' \ + "$PROD_CONFIG_PATH" "$BRIDGE_WEBHOOK_URL" "true" +``` + +Also confirm the config snapshot's `bridge.baseUrl` and environment name match the target environment before continuing. + +Dry run: + +```bash +BRIDGE_WEBHOOK_REPLAY_SECRET="$BRIDGE_WEBHOOK_REPLAY_SECRET" \ +BRIDGE_WEBHOOK_URL="$BRIDGE_WEBHOOK_URL" \ +node lib/scripts/replay-bridge-events.js \ + --configPath "$PROD_CONFIG_PATH" \ + --start 2026-05-01T00:00:00Z \ + --end 2026-05-02T00:00:00Z \ + --event-type deposit \ + --dry-run \ + --operator "ops@example.com" +``` + +Live replay: + +```bash +BRIDGE_WEBHOOK_REPLAY_SECRET="$BRIDGE_WEBHOOK_REPLAY_SECRET" \ +BRIDGE_WEBHOOK_URL="$BRIDGE_WEBHOOK_URL" \ +node lib/scripts/replay-bridge-events.js \ + --configPath "$PROD_CONFIG_PATH" \ + --start 2026-05-01T00:00:00Z \ + --end 2026-05-02T00:00:00Z \ + --event-type deposit \ + --operator "ops@example.com" +``` + +Useful filters: + +- `--event-type kyc` +- `--event-type deposit` +- `--event-type transfer` +- `--transfer-id ` + +Replay writes to the replay log and posts to `/internal/replay`. If `/internal/replay` returns `503`, the replay secret is not configured on the webhook server. + +## Reconciliation + +Run reconciliation after deploy, after replay, and during incidents involving deposits. + +```bash +yarn build +node lib/scripts/reconcile-bridge-ibex-deposits.js \ + --configPath "$PROD_CONFIG_PATH" \ + --window-hours 24 +``` + +Default `--window-hours` is `0.25` or 15 minutes. Use a larger window for deploy verification or after an outage. + +Admin query: + +```graphql +query BridgeOrphans { + bridgeReconciliationOrphans(limit: 50, orphanType: null, status: null) { + id + orphanType + status + amount + createdAt + updatedAt + } +} +``` + +Primary orphan classes: + +- `bridge_without_ibex`: Bridge says a deposit completed, but IBEX credit is missing or delayed. +- `ibex_without_bridge`: IBEX has a deposit that Flash cannot match to a Bridge deposit. + +Do not manually credit or reverse funds from an orphan without a written repair plan and a second reviewer. + +## Cash Wallet Cutover + +ENG-345 introduces client-aware wallet presentation during the cash wallet migration. + +Compatibility rule: + +- `PRE`: old clients and capable clients both see legacy `USD`. +- `IN_PROGRESS`: not-started accounts stay legacy `USD`; active migrations may block; accounts with migration status `complete` or `skipped_already_migrated` show `USDT` to capable clients and legacy-compatible `USD` to old clients. +- `COMPLETE`: capable clients see `USDT`; old clients continue to see legacy-compatible `USD`. + +Operator Bruno files from ENG-345: + +- `dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/01-query-admin-state.bru` +- `dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/02-set-scheduled-pre.bru` +- `dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/03-set-in-progress.bru` +- `dev/bruno/Flash GraphQL API/admin/cash-wallet-cutover/04-set-complete.bru` +- `dev/bruno/Flash GraphQL API/notoken/queries/cash-wallet-cutover.bru` + +The no-token Bruno file is an operator smoke check. Client-facing docs and support snippets should use the minimal public query below unless wider operational field exposure has been explicitly approved. + +Cutover states: + +- `PRE`: migration not active, schedule metadata may be present. +- `IN_PROGRESS`: migration is running or being actively validated. +- `COMPLETE`: cutover completed and client-aware presentation remains active for compatibility. + +Admin state query: + +```graphql +query CashWalletCutoverAdminState { + cashWalletCutover { + state + scheduledAt + startedAt + completedAt + pausedAt + pauseReason + cutoverVersion + runId + updatedBy + updatedAt + } +} +``` + +Public/client state query: + +```graphql +query CashWalletCutoverPublicState { + cashWalletCutover { + state + scheduledAt + } +} +``` + +The schema may expose additional operational fields for shared resolver reuse. Do not rely on, publish, or paste `runId`, `updatedBy`, `updatedAt`, `pauseReason`, or migration audit fields in client-facing artifacts without explicit approval. + +### Cutover Preflight + +Before moving to `IN_PROGRESS`: + +1. Confirm Bridge GraphQL smoke tests pass. +2. Confirm webhook `/health` passes. +3. Run Bridge deposit reconciliation. +4. Confirm no unresolved high-severity `bridge_without_ibex` orphan. +5. Confirm old-client no-header wallet query returns legacy `USD`. +6. Confirm capable-client wallet query also returns legacy `USD`; `USDT` should not appear before cutover starts unless using a separately completed canary environment. +7. Confirm support and mobile teams know the scheduled window. +8. Save the exact run ID, operator, commit SHA, and config snapshot location. + +### Cutover Execution + +Set the operator variables once and reuse them: + +```bash +export CUTOVER_VERSION=345 +export CUTOVER_RUN_ID="" +export OPERATOR="" +export PROD_CONFIG_PATH="" +``` + +1. Set `PRE` with schedule metadata using the admin `cashWalletCutoverUpdate` mutation or the Bruno operator file `02-set-scheduled-pre.bru`. +2. Re-run no-header and capable-client wallet smoke checks. +3. Preview and prepare migration records: + +```bash +node lib/scripts/cash-wallet-cutover.js preview \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" + +node lib/scripts/cash-wallet-cutover.js prepare \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +4. Set `IN_PROGRESS` with the CLI: + +```bash +node lib/scripts/cash-wallet-cutover.js start \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +The admin `cashWalletCutoverUpdate` mutation and Bruno `03-set-in-progress.bru` file directly mutate state and bypass CLI lifecycle checks. Use them for production `IN_PROGRESS` only as an emergency override with backend-owner approval and a written operator-log entry. Track additional start lifecycle hardening through ENG-403. + +5. Run migration batches and inspect status after each batch: + +```bash +node lib/scripts/cash-wallet-cutover.js run-batch \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" \ + --worker-id "$OPERATOR-manual-1" \ + --limit 25 + +node lib/scripts/cash-wallet-cutover.js status \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +Repeat `run-batch` until `status` shows no remaining runnable migration records. Preserve every JSON output in the operator log. + +6. Monitor batch/checkpoint logs and GraphQL error rate. +7. Re-run wallet smoke checks during migration. +8. Run reconciliation. +9. Set `COMPLETE` only after migration and presentation checks pass, using the CLI: + +```bash +node lib/scripts/cash-wallet-cutover.js complete \ + --configPath "$PROD_CONFIG_PATH" \ + --cutover-version "$CUTOVER_VERSION" \ + --run-id "$CUTOVER_RUN_ID" \ + --operator "$OPERATOR" +``` + +The admin `cashWalletCutoverUpdate` mutation and Bruno `04-set-complete.bru` file directly mutate state and bypass completion checks. Use them for production `COMPLETE` only as an emergency override with backend-owner approval, a written operator-log entry, and preserved `status` output proving why the override is safe. + +10. Re-run old-client and capable-client wallet checks after `COMPLETE`. + +For a controlled canary account, record the expected balances before execution. Example from the review canary: + +- No capability header: legacy `USD` wallet, `balance: 1000`. +- `X-Flash-Client-Capabilities: cash-wallet-usdt-v1`: `USDT` wallet, `balance: 10000000`. + +## Rollback and Pause + +Rollback is safest before irreversible wallet updates or default-wallet flips. + +If Bridge deploy fails before cutover: + +1. Disable Bridge feature access if needed. +2. Keep webhook server online if it can safely accept retries. +3. Roll back GraphQL/backend to the previous release. +4. Re-run reconciliation after recovery. + +If webhook server fails: + +1. Keep GraphQL available if user flows are safe. +2. Restore webhook route health. +3. Replay missed events by time window. +4. Run reconciliation. + +If cutover is in `PRE`: + +1. Keep state at `PRE`. +2. Fix the blocker. +3. Do not start migration. + +If cutover is `IN_PROGRESS`: + +1. Pause or stop the worker according to the cutover implementation. +2. Preserve logs, checkpoints, and run ID. +3. Do not manually mutate wallet state. +4. Decide whether to resume, complete, or write a repair plan. + +If cutover is `COMPLETE`: + +1. Treat rollback as a data repair project, not a normal deploy rollback. +2. Keep client-aware presentation intact unless it is the root cause. +3. Require a written repair plan and second reviewer before changing wallet/default state. + +## Incident Playbooks + +### Bridge API Down + +Symptoms: + +- KYC, virtual account, external account, or transfer operations fail. +- Bridge client errors rise. + +Actions: + +1. Confirm Bridge status and API base URL. +2. Pause user-facing Bridge mutations if failure rate is high. +3. Keep read-only status endpoints available if they are safe. +4. Retry only idempotent operations. +5. Reconcile when Bridge recovers. + +### Webhook Signature Failures + +Symptoms: + +- High `401` rate on `/kyc`, `/deposit`, or `/transfer`. + +Actions: + +1. Confirm public keys for the affected event family. +2. Confirm proxy preserves raw body. +3. Confirm the timestamp in `X-Webhook-Signature` is inside `timestampSkewMs`. +4. Do not bypass signature verification in production. +5. After fixing, replay the affected event window. + +### Bridge Deposit Without IBEX Credit + +Symptoms: + +- Reconciliation creates `bridge_without_ibex`. + +Actions: + +1. Confirm Bridge deposit status and transfer hash if available. +2. Check IBEX deposit ingestion delay. +3. Re-run reconciliation with a larger window. +4. If still orphaned, prepare a written repair plan. +5. Do not manually credit without approval and audit trail. + +### IBEX Deposit Without Bridge Match + +Symptoms: + +- Reconciliation creates `ibex_without_bridge`. + +Actions: + +1. Confirm whether the IBEX deposit belongs to Bridge. +2. Check for delayed Bridge event ingestion. +3. Replay Bridge events around the deposit timestamp. +4. Investigate unknown source deposits before assigning them to a user. + +### Withdrawal Stuck or Failed + +Symptoms: + +- Bridge transfer remains pending or failed. + +Actions: + +1. Query Bridge transfer state. +2. Check `/transfer` webhook delivery. +3. Replay transfer events for the affected transfer ID. +4. Confirm user-visible status matches Bridge state. +5. Escalate to Bridge support for unclear terminal states. + +### Old Clients See Wrong Wallet + +Symptoms: + +- Clients without capability header see `USDT` unexpectedly. + +Actions: + +1. Confirm request headers at GraphQL edge. +2. Query wallet list with no capability header. +3. Query with `cash-wallet-usdt-v1`. +4. Check cash wallet cutover state. +5. Roll back presentation code if compatibility is broken before data repair is needed. + +### Capable Clients Do Not See USDT + +Symptoms: + +- Clients with `X-Flash-Client-Capabilities: cash-wallet-usdt-v1` still see legacy `USD`. + +Actions: + +1. Confirm the exact header reaches GraphQL. +2. Confirm cutover state. +3. Check account wallet inventory and migration records. +4. Re-run focused wallet presentation tests. +5. Do not mark cutover `COMPLETE` until fixed. + +## Monitoring Signals + +Track these at minimum: + +- Bridge GraphQL mutation error rate. +- Bridge API latency and non-2xx rate. +- Webhook request counts by route and status. +- Webhook signature failures. +- Webhook handler exceptions. +- Replay success, failure, and skipped counts. +- Reconciliation orphan counts by type and status. +- Bridge transfer pending age. +- Cash wallet cutover state changes. +- Cash wallet cutover batch/checkpoint failures. +- Old-client wallet presentation requests. +- Capable-client wallet presentation requests. + +Alert on: + +- webhook `5xx` sustained above baseline. +- any sustained webhook `401` after deploy. +- reconciliation orphan growth. +- transfer pending age exceeding expected Bridge SLA. +- cutover worker failure during `IN_PROGRESS`. +- old-client compatibility smoke failure. + +## Post-Deploy Verification + +After deploy: + +1. `GET /health` on webhook server returns OK. +2. Bridge GraphQL smoke queries return without GraphQL errors. +3. A no-header wallet query shows legacy `USD`. +4. A capable-client wallet query matches the active phase: `USD` during `PRE`, `USDT` only for migrated accounts during `IN_PROGRESS`, and `USDT` after `COMPLETE`. +5. Replay dry-run succeeds for a narrow recent window. +6. Reconciliation completes. +7. No new unresolved high-severity orphans appear. +8. Logs show no signature, config, or Bridge API auth errors. + +## Documentation Follow-Ups + +This runbook should be updated when: + +- Production process manager names and service names are finalized. +- Exact deploy commands for the target environment are confirmed. +- Bridge dashboard webhook URLs are finalized. +- ENG-345 cutover worker command names are finalized. +- Observability dashboards and alert links exist. From bdf63ef293e1d83f6d1ff0c7b2f96f1425e55009 Mon Sep 17 00:00:00 2001 From: heyolaniran Date: Thu, 28 May 2026 04:47:16 +0100 Subject: [PATCH 17/22] feat(externalAccounts): Webhook endpoint firing for external accounts feat: external accounts event triggering this commit initialize the webhook endpoint to register on Bridge to trigger external account link after plaid completion --- src/config/schema.ts | 3 +- src/config/schema.types.d.ts | 1 + .../mutation/bridge-add-external-account.ts | 4 +- .../object/bridge-external-account-link.ts | 11 +++ .../types/object/bridge-external-account.ts | 2 +- src/services/bridge/webhook-server/index.ts | 2 + .../middleware/verify-signature.ts | 2 +- .../webhook-server/routes/external-account.ts | 80 +++++++++++++++++++ 8 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/graphql/public/types/object/bridge-external-account-link.ts create mode 100644 src/services/bridge/webhook-server/routes/external-account.ts diff --git a/src/config/schema.ts b/src/config/schema.ts index c4e6077c2..11f0b9a53 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -661,8 +661,9 @@ export const configSchema = { kyc: { type: "string" }, deposit: { type: "string" }, transfer: { type: "string" }, + external_account: { type: "string" }, }, - required: ["kyc", "deposit", "transfer"], + required: ["kyc", "deposit", "transfer", "external_account"], }, timestampSkewMs: { type: "integer" }, replaySecret: { type: "string" }, diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index 20c1952a4..ed94535f1 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -35,6 +35,7 @@ type BridgeWebhookPublicKeys = { kyc: string deposit: string transfer: string + external_account: string } type BridgeWebhook = { diff --git a/src/graphql/public/root/mutation/bridge-add-external-account.ts b/src/graphql/public/root/mutation/bridge-add-external-account.ts index 0c4a2fd19..1c3ccfbe5 100644 --- a/src/graphql/public/root/mutation/bridge-add-external-account.ts +++ b/src/graphql/public/root/mutation/bridge-add-external-account.ts @@ -1,7 +1,7 @@ import { GT } from "@graphql/index" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" import IError from "@graphql/shared/types/abstract/error" -import BridgeExternalAccount from "@graphql/public/types/object/bridge-external-account" +import BridgeExternalAccountLink from "@graphql/public/types/object/bridge-external-account-link" import { BridgeConfig } from "@config" import BridgeService from "@services/bridge" import { BridgeDisabledError, BridgeAccountLevelError } from "@services/bridge/errors" @@ -10,7 +10,7 @@ const BridgeAddExternalAccountPayload = GT.Object({ name: "BridgeAddExternalAccountPayload", fields: () => ({ errors: { type: GT.NonNullList(IError) }, - externalAccount: { type: BridgeExternalAccount }, + externalAccount: { type: BridgeExternalAccountLink }, }), }) diff --git a/src/graphql/public/types/object/bridge-external-account-link.ts b/src/graphql/public/types/object/bridge-external-account-link.ts new file mode 100644 index 000000000..5d51c664d --- /dev/null +++ b/src/graphql/public/types/object/bridge-external-account-link.ts @@ -0,0 +1,11 @@ +import { GT } from "@graphql/index" + +const BridgeExternalAccountLink = GT.Object({ + name: "BridgeExternalAccountLink", + fields: () => ({ + linkUrl: { type: GT.NonNull(GT.String) }, + expiresAt: { type: GT.NonNull(GT.String) }, + }), +}) + +export default BridgeExternalAccountLink diff --git a/src/graphql/public/types/object/bridge-external-account.ts b/src/graphql/public/types/object/bridge-external-account.ts index 890733978..92cae18ad 100644 --- a/src/graphql/public/types/object/bridge-external-account.ts +++ b/src/graphql/public/types/object/bridge-external-account.ts @@ -3,7 +3,7 @@ import { GT } from "@graphql/index" const BridgeExternalAccount = GT.Object({ name: "BridgeExternalAccount", fields: () => ({ - id: { type: GT.NonNullID }, + id: { type: GT.NonNullID, resolve: (obj) => obj.bridgeExternalAccountId }, bankName: { type: GT.NonNull(GT.String) }, accountNumberLast4: { type: GT.NonNull(GT.String) }, status: { type: GT.NonNull(GT.String) }, diff --git a/src/services/bridge/webhook-server/index.ts b/src/services/bridge/webhook-server/index.ts index 2cf97522a..e89060179 100644 --- a/src/services/bridge/webhook-server/index.ts +++ b/src/services/bridge/webhook-server/index.ts @@ -14,6 +14,7 @@ import { verifyBridgeSignature } from "./middleware/verify-signature" import { kycHandler } from "./routes/kyc" import { depositHandler } from "./routes/deposit" import { transferHandler } from "./routes/transfer" +import { externalAccountHandler } from "./routes/external-account" import { replayAuthMiddleware, replayHandler } from "./routes/replay" type RawBodyRequest = express.Request & { rawBody?: string } @@ -43,6 +44,7 @@ export const startBridgeWebhookServer = () => { app.post("/kyc", verifyBridgeSignature("kyc"), kycHandler) app.post("/deposit", verifyBridgeSignature("deposit"), depositHandler) app.post("/transfer", verifyBridgeSignature("transfer"), transferHandler) + app.post("/external-account", verifyBridgeSignature("external_account"), externalAccountHandler) app.post("/internal/replay", replayAuthMiddleware, replayHandler) if (!BridgeConfig.webhook.replaySecret && !process.env.BRIDGE_WEBHOOK_REPLAY_SECRET) { diff --git a/src/services/bridge/webhook-server/middleware/verify-signature.ts b/src/services/bridge/webhook-server/middleware/verify-signature.ts index 03b75494f..447ff9aaf 100644 --- a/src/services/bridge/webhook-server/middleware/verify-signature.ts +++ b/src/services/bridge/webhook-server/middleware/verify-signature.ts @@ -14,7 +14,7 @@ import { baseLogger } from "@services/logger" type RawBodyRequest = Request & { rawBody?: string } -export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer") => { +export const verifyBridgeSignature = (publicKeyType: "kyc" | "deposit" | "transfer" | "external_account") => { return (req: Request, res: Response, next: NextFunction) => { const signature = req.headers["x-webhook-signature"] as string diff --git a/src/services/bridge/webhook-server/routes/external-account.ts b/src/services/bridge/webhook-server/routes/external-account.ts new file mode 100644 index 000000000..6618f5a31 --- /dev/null +++ b/src/services/bridge/webhook-server/routes/external-account.ts @@ -0,0 +1,80 @@ +/** + * Bridge External Account Webhook Handler + * Handles external_account.created and external_account.updated events from Bridge.xyz + * + * Fires after a user completes the Plaid bank-linking flow. + * Persists the linked account to MongoDB so it appears in bridgeExternalAccounts queries. + * + * Status mapping: + * active: true → "verified" + * active: false → "failed" + */ + +import { Request, Response } from "express" +import { AccountsRepository } from "@services/mongoose/accounts" +import { LockService } from "@services/lock" +import { baseLogger } from "@services/logger" +import { toBridgeCustomerId } from "@domain/primitives/bridge" +import * as BridgeAccountsRepo from "@services/mongoose/bridge-accounts" + +const toStatus = (active: boolean | undefined): "pending" | "verified" | "failed" => { + if (active === true) return "verified" + if (active === false) return "failed" + return "pending" +} + +export const externalAccountHandler = async (req: Request, res: Response) => { + const { event_id, event_object } = req.body + const { id, customer_id, bank_name, last_4, active } = event_object ?? {} + + if (!id || !customer_id || !event_id) { + return res.status(400).json({ error: "Invalid payload" }) + } + + try { + const bridgeCustomerId = toBridgeCustomerId(customer_id) + const account = await AccountsRepository().findByBridgeCustomerId(bridgeCustomerId) + if (account instanceof Error) { + baseLogger.warn( + { customer_id, event_id }, + "Account not found for Bridge customer — may be a timing issue, Bridge will retry", + ) + return res.status(503).json({ error: "Account not ready" }) + } + + const lockKey = `bridge-external-account:${event_id}` + const lockResult = await LockService().lockIdempotencyKey(lockKey as IdempotencyKey) + if (lockResult instanceof Error) { + baseLogger.info({ customer_id, event_id, id }, "Duplicate Bridge external account webhook") + return res.status(200).json({ status: "already_processed" }) + } + + const status = toStatus(active) + + const result = await BridgeAccountsRepo.createExternalAccount({ + accountId: String(account.id), + bridgeExternalAccountId: id, + bankName: bank_name ?? "Unknown", + accountNumberLast4: last_4 ?? "0000", + status, + }) + + if (result instanceof Error) { + baseLogger.error( + { accountId: account.id, event_id, id, error: result }, + "Failed to persist Bridge external account", + ) + return res.status(500).json({ error: "Failed to persist external account" }) + } + + baseLogger.info( + { accountId: account.id, bridgeExternalAccountId: id, status }, + "Bridge external account persisted", + ) + + return res.status(200).json({ status: "success" }) + } catch (error) { + baseLogger.error({ error, customer_id, event_id }, "Error processing Bridge external account webhook") + return res.status(500).json({ error: "Internal server error" }) + } +} From 85c67d584db922232aa00b217e1a3468b005c748 Mon Sep 17 00:00:00 2001 From: heyolaniran Date: Thu, 28 May 2026 04:58:09 +0100 Subject: [PATCH 18/22] fix(ENG-350) : reset pending withdraw state on transfer failure. this commit mark the transfer as failed in our bridge withdrawals collection when something went wrong during the trasfer flow, the user is notified with a popup and on return state, as well for transfer successfully completed --- src/services/bridge/webhook-server/routes/replay.ts | 6 +++++- src/services/bridge/webhook-server/routes/transfer.ts | 8 +++----- src/services/mongoose/bridge-accounts.ts | 7 ++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/services/bridge/webhook-server/routes/replay.ts b/src/services/bridge/webhook-server/routes/replay.ts index 33b499bd9..009414c28 100644 --- a/src/services/bridge/webhook-server/routes/replay.ts +++ b/src/services/bridge/webhook-server/routes/replay.ts @@ -14,14 +14,16 @@ import { } from "../transfer-direction" import { depositHandler } from "./deposit" +import { externalAccountHandler } from "./external-account" import { kycHandler } from "./kyc" import { transferHandler } from "./transfer" -type RouteKey = "kyc" | "deposit" | "transfer" +type RouteKey = "kyc" | "deposit" | "transfer" | "external_account" const HANDLERS: Record Promise> = { kyc: kycHandler, deposit: depositHandler, transfer: transferHandler, + external_account: externalAccountHandler, } const DEPOSIT_EVENT_TYPES = new Set([ @@ -39,6 +41,7 @@ const DEPOSIT_EVENT_TYPES = new Set([ const toRouteKey = (bridgeEventType: string): RouteKey | null => { if (bridgeEventType.startsWith("kyc")) return "kyc" if (bridgeEventType.startsWith("transfer")) return "transfer" + if (bridgeEventType.startsWith("external_account")) return "external_account" if (DEPOSIT_EVENT_TYPES.has(bridgeEventType)) return "deposit" return null } @@ -114,6 +117,7 @@ const toHandlerBody = ({ event_id: eventId, event_object: eventObject, } + } export const replayAuthMiddleware = (req: Request, res: Response, next: () => void) => { diff --git a/src/services/bridge/webhook-server/routes/transfer.ts b/src/services/bridge/webhook-server/routes/transfer.ts index a24a32fdc..6330ad94a 100644 --- a/src/services/bridge/webhook-server/routes/transfer.ts +++ b/src/services/bridge/webhook-server/routes/transfer.ts @@ -1,6 +1,6 @@ /** * Bridge Transfer Webhook Handler - * Handles transfer.completed and transfer.failed events from Bridge.xyz + * Handles transfer webhook events from Bridge.xyz (transfer.completed, transfer.updated.status_transitioned) */ import { Request, Response } from "express" @@ -65,7 +65,7 @@ export const transferHandler = async (req: Request, res: Response) => { event === "transfer.payment_processed" || state === "payment_processed" - const isFailure = event === "transfer.failed" || TERMINAL_FAILURE_STATES.has(state) + const isFailure = TERMINAL_FAILURE_STATES.has(state) if (!isCompletion && !isFailure) { baseLogger.info({ transfer_id, state, event }, "Bridge transfer event not handled") @@ -118,9 +118,7 @@ export const transferHandler = async (req: Request, res: Response) => { const failureReason = state === "refund_failed" ? (return_reason as string | undefined) - : event === "transfer.failed" - ? (reason as string | undefined) - : ((reason as string | undefined) ?? (return_reason as string | undefined)) + : ((reason as string | undefined) ?? (return_reason as string | undefined)) const result = await BridgeAccountsRepo.updateWithdrawalStatus( bridgeTransferId, diff --git a/src/services/mongoose/bridge-accounts.ts b/src/services/mongoose/bridge-accounts.ts index c045295be..4afd2c974 100644 --- a/src/services/mongoose/bridge-accounts.ts +++ b/src/services/mongoose/bridge-accounts.ts @@ -61,7 +61,12 @@ export const createExternalAccount = async (data: { status?: "pending" | "verified" | "failed" }) => { try { - const record = await BridgeExternalAccount.create(data) + const { bridgeExternalAccountId, accountId, status, ...immutable } = data + const record = await BridgeExternalAccount.findOneAndUpdate( + { bridgeExternalAccountId, accountId }, + { $setOnInsert: { bridgeExternalAccountId, accountId, ...immutable }, $set: { status: status ?? "pending" } }, + { upsert: true, new: true, setDefaultsOnInsert: true }, + ) return record } catch (error) { return new RepositoryError(String(error)) From b86c0865185bd8223c033fa6c19e9b692c8932c2 Mon Sep 17 00:00:00 2001 From: heyolaniran Date: Thu, 28 May 2026 04:59:15 +0100 Subject: [PATCH 19/22] feat: bruno configurations --- dev/bruno/Flash GraphQL API/collection.bru | 3 +- .../mutations/bridgeAddExternalAccount.bru | 35 ++++++++++++++ .../mutations/bridgeInitiateWithdrawal.bru | 47 +++++++++++++++++++ .../token/queries/bridgeExternalAccounts.bru | 31 ++++++++++++ .../token/queries/bridgeWithdrawals.bru | 32 +++++++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeAddExternalAccount.bru create mode 100644 dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeExternalAccounts.bru create mode 100644 dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawals.bru diff --git a/dev/bruno/Flash GraphQL API/collection.bru b/dev/bruno/Flash GraphQL API/collection.bru index 40b0cbaea..97ea30b6a 100644 --- a/dev/bruno/Flash GraphQL API/collection.bru +++ b/dev/bruno/Flash GraphQL API/collection.bru @@ -10,6 +10,7 @@ vars:pre-request { protocol: http domain: localhost port: 4000 + bridgeExternalAccountId: ext_plaid_a4a2b7e0175c } docs { @@ -32,7 +33,7 @@ docs { ``` ERPNEXT_JWT_SECRET= node -e "const c=require('crypto'),b=o=>Buffer.from(JSON.stringify(o)).toString('base64url'),h=b({alg:'HS256',typ:'JWT'}),p=b({userId:'admin',roles:['Accounts Manager']}),s=c.createHmac('sha256',process.env.ERPNEXT_JWT_SECRET).update(h+'.'+p).digest('base64url');console.log(h+'.'+p+'.'+s)" ``` - + # Extra Resources If you use Postman, we have a collection you can import to test the API. Download it here: [galoy_graphql_main_api.postman_collection.json](https://github.com/GaloyMoney/galoy/tree/main/src/graphql/docs/galoy_graphql_main_api.postman_collection.json) } diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeAddExternalAccount.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeAddExternalAccount.bru new file mode 100644 index 000000000..9535124f1 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeAddExternalAccount.bru @@ -0,0 +1,35 @@ +meta { + name: bridgeAddExternalAccount + type: graphql + seq: 38 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeAddExternalAccount { + bridgeAddExternalAccount { + errors { + message + code + } + externalAccount { + linkUrl + expiresAt + } + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru new file mode 100644 index 000000000..77df58a56 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/mutations/bridgeInitiateWithdrawal.bru @@ -0,0 +1,47 @@ +meta { + name: bridgeInitiateWithdrawal + type: graphql + seq: 39 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + mutation BridgeInitiateWithdrawal($input: BridgeInitiateWithdrawalInput!) { + bridgeInitiateWithdrawal(input: $input) { + errors { + message + code + } + withdrawal { + id + amount + currency + status + createdAt + } + } + } +} + +body:graphql:vars { + { + "input": { + "amount": "10.00", + "externalAccountId": "{{bridgeExternalAccountId}}" + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeExternalAccounts.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeExternalAccounts.bru new file mode 100644 index 000000000..4fae717a8 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeExternalAccounts.bru @@ -0,0 +1,31 @@ +meta { + name: bridgeExternalAccounts + type: graphql + seq: 19 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeExternalAccounts { + bridgeExternalAccounts { + id + bankName + accountNumberLast4 + status + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} diff --git a/dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawals.bru b/dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawals.bru new file mode 100644 index 000000000..397404f13 --- /dev/null +++ b/dev/bruno/Flash GraphQL API/token/queries/bridgeWithdrawals.bru @@ -0,0 +1,32 @@ +meta { + name: bridgeWithdrawals + type: graphql + seq: 20 +} + +post { + url: {{flashGraphqlUrl}} + body: graphql + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:graphql { + query BridgeWithdrawals { + bridgeWithdrawals { + id + amount + currency + status + createdAt + } + } +} + +settings { + encodeUrl: false + timeout: 30000 +} From 608b6ed2e740b241e23ce9c2eb4dff88bb8ccf48 Mon Sep 17 00:00:00 2001 From: heyolaniran Date: Sun, 31 May 2026 07:26:43 +0100 Subject: [PATCH 20/22] fix(bridge): normalize currency display in withdrawal notification Map "usdt" to "USD" and uppercase other currency codes so the push notification shows a human-readable label instead of a raw enum value. --- src/app/bridge/send-withdrawal-notification.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/bridge/send-withdrawal-notification.ts b/src/app/bridge/send-withdrawal-notification.ts index 8adf90f30..cc5c7ae29 100644 --- a/src/app/bridge/send-withdrawal-notification.ts +++ b/src/app/bridge/send-withdrawal-notification.ts @@ -70,7 +70,7 @@ export const sendBridgeWithdrawalNotification = async ({ data: { type: `bridge_withdrawal_${outcome}`, amount, - currency, + currency: currency == "usdt" ? "USD" : currency.toUpperCase(), ...(failureReason ? { failureReason } : {}), }, }) From e56d44bcf929de387bd45ffb9b9b2cafcc593b0b Mon Sep 17 00:00:00 2001 From: heyolaniran Date: Sun, 31 May 2026 07:27:18 +0100 Subject: [PATCH 21/22] feat(ibex): update config schema for M2M authentication Replace email/password fields with clientId, clientSecret, and environment ("production" | "sandbox") in the IbexConfig type, JSON schema, and dev YAML config. --- dev/config/base-config.yaml | 7 ++++--- src/config/schema.ts | 5 +++-- src/config/schema.types.d.ts | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/dev/config/base-config.yaml b/dev/config/base-config.yaml index 88fb2ada0..cdadf407f 100644 --- a/dev/config/base-config.yaml +++ b/dev/config/base-config.yaml @@ -6,9 +6,9 @@ admin_accounts: ibex: url: "https://api-sandbox.poweredbyibex.io" authUrl: "https://auth.hub.sandbox.poweredbyibex.io" - email: "" - password: "Ibex@1234" - apiKey: "sk-test-3bd6463c9cd77c3d8858c60b9997d0c6" + clientId: "your-m2m-client-id" + clientSecret: "your-m2m-client-secret" + environment: "sandbox" webhook: uri: "" # external uri defined in overrides port: 4008 @@ -45,6 +45,7 @@ bridge: dwIDAQAB -----END PUBLIC KEY----- transfer: "" + external_account: "" timestampSkewMs: 300000 # See Foreign Exchange Rates: https://www.firstglobal-bank.com/ diff --git a/src/config/schema.ts b/src/config/schema.ts index 11f0b9a53..3e2f69fd7 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -631,8 +631,9 @@ export const configSchema = { properties: { url: { type: "string" }, authUrl: { type: "string" }, - email: { type: "string" }, - password: { type: "string" }, + clientId: { type: "string" }, + clientSecret: { type: "string" }, + environment: { type: "string", enum: ["production", "sandbox"] }, webhook: { type: "object", properties: { diff --git a/src/config/schema.types.d.ts b/src/config/schema.types.d.ts index ed94535f1..e2f9eb7fc 100644 --- a/src/config/schema.types.d.ts +++ b/src/config/schema.types.d.ts @@ -25,9 +25,9 @@ type WebhookServer = { type IbexConfig = { url: string authUrl: string - email: string - password: string - apiKey?: string + clientId: string + clientSecret: string + environment: "production" | "sandbox" webhook: WebhookServer } From 4dc02a28994af9e1f75ec14c78f03c568f58caaf Mon Sep 17 00:00:00 2001 From: heyolaniran Date: Sun, 31 May 2026 07:27:36 +0100 Subject: [PATCH 22/22] feat(ibex): switch to machine-to-machine OAuth authentication Replace the deprecated email/password sign-in flow with OAuth 2.0 client credentials (M2M). IbexClient now takes clientId/clientSecret/ environment, and getIbexToken posts to {authUrl}/oauth/token with grant_type=client_credentials, caching the token until 60s before expiry. --- src/services/ibex/client.ts | 60 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/services/ibex/client.ts b/src/services/ibex/client.ts index 56c23c72e..9a1beeb46 100644 --- a/src/services/ibex/client.ts +++ b/src/services/ibex/client.ts @@ -47,8 +47,11 @@ import { errorHandler, IbexError, ParseError, UnexpectedIbexResponse } from "./e import WebhookServer from "./webhook-server" const Ibex = new IbexClient( - IbexConfig.url, - { email: IbexConfig.email, password: IbexConfig.password }, + { + clientId: IbexConfig.clientId, + clientSecret: IbexConfig.clientSecret, + environment: IbexConfig.environment, + }, Redis, ) @@ -255,15 +258,27 @@ const payToLnurl = async ( }).then(errorHandler) } +let m2mTokenCache: { token: string; expiresAt: number } | undefined + const getIbexToken = async (): Promise => { - const cached = await Ibex.authentication.storage.getAccessToken() - if (typeof cached === "string") return `${cached}` + if (m2mTokenCache && Date.now() < m2mTokenCache.expiresAt) { + return m2mTokenCache.token + } + + const audience = + IbexConfig.environment === "sandbox" + ? "https://api-sandbox.poweredbyibex.io" + : "https://ibexhub.ibexmercado.com" - // The SDK uses a single base URL for all calls, but the sandbox auth domain is separate - const resp = await fetch(`${IbexConfig.url}/auth/signin`, { + const resp = await fetch(`${IbexConfig.authUrl}/oauth/token`, { method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: IbexConfig.email, password: IbexConfig.password }), + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + grant_type: "client_credentials", + client_id: IbexConfig.clientId, + client_secret: IbexConfig.clientSecret, + audience, + }).toString(), }).catch( (err: unknown) => new IbexError(err instanceof Error ? err : new Error(String(err))), ) @@ -271,30 +286,19 @@ const getIbexToken = async (): Promise => { if (resp instanceof IbexError) return resp if (!resp.ok) { const body = await resp.text().catch(() => "") - return new IbexError(new Error(`IBEX sign-in failed: ${resp.status} — ${body}`)) + return new IbexError(new Error(`IBEX M2M auth failed: ${resp.status} — ${body}`)) } - const data = (await resp.json()) as { - accessToken?: string - accessTokenExpiresAt?: number - refreshToken?: string - refreshTokenExpiresAt?: number - } - if (!data.accessToken) - return new IbexError(new Error("IBEX sign-in: no access token in response")) + const data = (await resp.json()) as { access_token?: string; expires_in?: number } + if (!data.access_token) + return new IbexError(new Error("IBEX M2M auth: no access_token in response")) - await Ibex.authentication.storage.setAccessToken( - data.accessToken, - data.accessTokenExpiresAt, - ) - if (data.refreshToken) { - await Ibex.authentication.storage.setRefreshToken( - data.refreshToken, - data.refreshTokenExpiresAt, - ) + const expiresIn = data.expires_in ?? 3600 + m2mTokenCache = { + token: data.access_token, + expiresAt: Date.now() + (expiresIn - 60) * 1000, } - - return data.accessToken as string + return data.access_token } const ibexFetch = async (