diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index a63f0ea..00fe01b 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -9,20 +9,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - + - name: Use Node.js uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: 18.x registry-url: 'https://registry.npmjs.org' - + - name: Install dependencies run: npm ci - + - name: Build run: npm run build - + - name: Publish to NPM run: npm publish env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 4f15ae3..79e4827 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -2,7 +2,7 @@ name: PR Checks on: pull_request: - branches: [ main ] + branches: [main] types: [opened, synchronize, reopened] jobs: @@ -13,19 +13,19 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v3 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Run linter - run: npm run lint - - - name: Run tests - run: npm test \ No newline at end of file + - uses: actions/checkout@v3 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run linter + run: npm run lint + + - name: Run tests + run: npm test diff --git a/.gitignore b/.gitignore index 33cde39..95f9ee9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ +.cache +.DS_Store +.idea +*.log +*.tgz +coverage dist +lib-cov +logs node_modules -lib \ No newline at end of file +temp diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..536f0e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +ignore-workspace-root-check = true +shell-emulator = true diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index beb9bfc..0000000 --- a/.prettierignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -lib -dist -coverage -*.md -package-lock.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index e0c9e0f..0000000 --- a/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "semi": true, - "trailingComma": "all", - "singleQuote": true, - "printWidth": 100, - "tabWidth": 2, - "endOfLine": "lf" -} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a45fffd --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025-PRESENT xMoney + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..09d8775 --- /dev/null +++ b/README.md @@ -0,0 +1,479 @@ +[![npm version][npm-version-src]][npm-version-href] +[![npm downloads][npm-downloads-src]][npm-downloads-href] +[![bundle][bundle-src]][bundle-href] +[![JSDocs][jsdocs-src]][jsdocs-href] +[![License][license-src]][license-href] + +# XMoney API SDK + +A modern, type-safe API SDK for the XMoney payment platform. Built with TypeScript-first design principles, featuring explicit error handling, async generators for efficient pagination, and cross-platform support for Node.js and browsers. + +## Features + +- **TypeScript-first** - Full type safety with comprehensive TypeScript support +- **Async generators** - Memory-efficient pagination for large datasets +- **Cross-platform** - Works in Node.js, browsers, and edge environments +- **Type-safe errors** - Explicit error handling with typed error responses +- **Modular design** - Tree-shakeable with minimal bundle size +- **Secure by default** - Built-in encryption for sensitive operations + +## 📦 Installation + +```sh +# npm +npm install @xmoney/api-sdk + +# pnpm +pnpm add @xmoney/api-sdk + +# yarn +yarn add @xmoney/api-sdk + +# bun +bun add @xmoney/api-sdk +``` + +## 🚀 Quick Start + +### Initialize the SDK + +```typescript +import { createXMoneyClient } from '@xmoney/api-sdk' + +// Simple initialization +const xMoney = createXMoneyClient('sk_test_your_secret_key') + +// Advanced initialization with custom configuration +const advancedXMoney = createXMoneyClient({ + apiKey: 'sk_test_your_secret_key', + host: 'api-stage.xmoney.com', // optional + timeout: 30000, // optional, in milliseconds +}) +``` + +### Basic Usage Examples + +#### Create a Customer + +```typescript +const customer = await xMoney.customers.create({ + identifier: 'customer_123', + email: 'john.doe@example.com', + firstName: 'John', + lastName: 'Doe', + country: 'US', + state: 'NY', + city: 'New York', + zipCode: '10001', +}) +``` + +#### Create an Order + +```typescript +const order = await xMoney.orders.create({ + customerId: customer.id, + ip: '192.168.1.1', + amount: 10000, // Amount in smallest currency unit (e.g., cents) + currency: 'USD', + orderType: 'purchase', + externalOrderId: `order_${Date.now()}`, + description: 'Premium subscription', + cardTransactionMode: 'authAndCapture', + cardNumber: '4111111111111111', + cardExpiryDate: '12/25', + cardCvv: '123', + cardHolderName: 'John Doe', +}) +``` + +#### List Transactions with Pagination + +```typescript +// Using async iteration for memory-efficient pagination +for await (const transaction of xMoney.transactions.list({ + perPage: 20, + transactionStatus: ['complete-ok'], + currency: 'USD' +})) { + console.log(transaction.id, transaction.amount, transaction.status) +} +``` + +## 📚 API Resources + +### Customers + +Manage customer profiles and information. + +```typescript +// Create a customer (identifier and email required) +await xMoney.customers.create({ + identifier: 'customer_123', + email: 'john@example.com', + firstName: 'John', + lastName: 'Doe', + country: 'US', + state: 'NY', +}) + +// Retrieve a customer by ID +await xMoney.customers.retrieve(customerId) + +// Update customer details +await xMoney.customers.update(customerId, { + email: 'new@email.com', + city: 'Los Angeles', + state: 'CA' +}) + +// Delete a customer +await xMoney.customers.delete(customerId) + +// List customers with filters +for await (const customer of xMoney.customers.list({ + perPage: 50, + country: 'US', + createdAtFrom: new Date('2024-01-01') +})) { + // Process each customer +} + +// Search customers +const results = await xMoney.customers.search({ + email: 'john.doe@example.com', + identifier: 'customer_123' +}) +``` + +### Orders + +Handle payment orders and recurring billing. + +```typescript +// Create a purchase order with new card +await xMoney.orders.create({ + customerId: 12345, + ip: '192.168.1.1', + amount: 5000, + currency: 'USD', + orderType: 'purchase', + externalOrderId: 'unique_order_id', + cardTransactionMode: 'authAndCapture', + cardNumber: '4111111111111111', + cardExpiryDate: '12/25', + cardCvv: '123', + saveCard: true, +}) + +// Create a recurring order +await xMoney.orders.create({ + customerId: 12345, + ip: '192.168.1.1', + amount: 2999, + currency: 'USD', + orderType: 'recurring', + intervalType: 'month', + intervalValue: 1, + cardId: 'saved_card_id', +}) + +// Retrieve an order +await xMoney.orders.retrieve(orderId) + +// Cancel an order +await xMoney.orders.cancel(orderId, { + reason: 'customer-demand', + message: 'Customer requested cancellation' +}) + +// Rebill a recurring order +await xMoney.orders.rebill(orderId, { + customerId: 12345, + amount: 2999 +}) + +// Update order card +await xMoney.orders.updateCard(orderId, { + customerId: '12345', // Note: string type required + ip: '192.168.1.1', + amount: 2999, + currency: 'USD', + cardNumber: '5555555555554444', + cardExpiryDate: '12/26', + cardCvv: '456' +}) +``` + +### Transactions + +Manage payment transactions, captures, and refunds. + +```typescript +// Retrieve a transaction +await xMoney.transactions.retrieve(transactionId) + +// Capture an authorized transaction +await xMoney.transactions.capture(transactionId, { + amount: 5000, // Required, can be partial +}) + +// Refund a transaction +await xMoney.transactions.refund(transactionId, { + amount: 2500, // Optional partial refund + reason: 'customer-demand', + message: 'Product return' +}) + +// List transactions with filters +for await (const transaction of xMoney.transactions.list({ + transactionStatus: ['complete-ok', 'in-progress'], + transactionMethod: 'card', + currency: 'USD', + amountFrom: 1000, + amountTo: 10000, + createdAtFrom: new Date('2024-01-01') +})) { + // Process transactions +} +``` + +### Cards + +Manage stored payment cards. + +```typescript +// Retrieve a card (both parameters required) +await xMoney.cards.retrieve(cardId, customerId) + +// Delete a card +await xMoney.cards.delete(cardId) + +// List customer cards +for await (const card of xMoney.cards.list({ + customerId: 12345, + hasToken: 'yes', + cardStatus: 'all' +})) { + console.log(card.last4, card.expiryMonth, card.expiryYear) +} +``` + +### Checkout + +Create hosted checkout sessions for secure payment collection. + +```typescript +// Create a checkout session +const checkout = await xMoney.checkout.create({ + publicKey: 'pk_test_your_public_key', + customer: { + identifier: 'customer_123', + firstName: 'John', + lastName: 'Doe', + email: 'john@example.com', + country: 'US', + state: 'NY', + city: 'New York', + zipCode: '10001', + phone: '+1234567890', + }, + order: { + orderId: 'order_123', + description: 'Product purchase', + type: 'purchase', + amount: 10000, + currency: 'USD', + items: [ + { + name: 'Premium Subscription', + units: 1, + unitPrice: 10000, + amount: 10000, + } + ], + tags: ['subscription', 'premium'], + }, + cardTransactionMode: 'authAndCapture', + backUrl: 'https://your-site.com/checkout/complete', +}) + +// Generate HTML form for embedded checkout +const htmlForm = await xMoney.checkout.form({ + // Same parameters as create() +}) + +// Decrypt webhook response +const decryptedData = await xMoney.checkout.decrypt(encryptedPayload) +``` + +### Notifications + +Access webhook notifications and delivery logs. + +```typescript +// List all notifications with filters +for await (const notification of xMoney.notifications.list({ + perPage: 50, + type: 'order', + status: 'delivered', + createdAtFrom: new Date('2024-01-01') +})) { + console.log(notification.type, notification.status) +} + +// List order-specific notifications +const orderNotifications = await xMoney.notifications.listForOrders({ + orderId: 12345, + perPage: 20, +}) + +// List transaction-specific notifications +const txNotifications = await xMoney.notifications.listForTransactions({ + transactionId: 67890, + status: 'failed', +}) +``` + +## ⚠️ Error Handling + +The SDK provides typed error responses for better error handling: + +```typescript +import { XMoneyError } from '@xmoney/api-sdk' + +try { + await xMoney.orders.create({ /* ... */ }) +} +catch (error) { + if (error instanceof XMoneyError) { + console.error('API Error:', error.message) + console.error('Error Code:', error.code) + console.error('Status:', error.status) + console.error('Request ID:', error.requestId) + } +} +``` + +## 📄 Pagination + +All list methods return async iterators for memory-efficient processing: + +```typescript +// Iterate through pages automatically +for await (const order of xMoney.orders.list({ + perPage: 100, + orderType: 'purchase', + currency: 'USD' +})) { + // Process each order +} + +// Get a specific page +const page = await xMoney.orders.list({ + page: 2, + perPage: 50, + reverseSorting: 1 +}) +console.log(page.data) // Array of orders +console.log(page.pagination) // Pagination + +// Use search method for complex queries +const searchResults = await xMoney.customers.search({ + email: 'john@example.com', + country: 'US', + tag: ['vip', 'premium'] +}) +``` + +## 🔐 Hosted Checkout Integration + +The checkout resource provides secure payment form generation and response handling: + +```typescript +// Create encrypted checkout payload +const { payload, checksum } = xMoney.checkout.create({ + publicKey: 'pk_test_your_public_key', + customer: { + identifier: 'customer_123', + firstName: 'John', + lastName: 'Doe', + email: 'john@example.com', + country: 'US', + state: 'NY', + city: 'New York', + }, + order: { + orderId: 'order_123', + description: 'Product purchase', + type: 'purchase', + amount: 10000, + currency: 'USD', + }, + cardTransactionMode: 'authAndCapture', + backUrl: 'https://your-site.com/checkout/complete', + saveCard: true, +}) + +// Generate complete HTML form for direct submission +const checkoutHtml = xMoney.checkout.form({ + publicKey: 'pk_test_your_public_key', + customer: { /* ... */ }, + order: { /* ... */ }, + url: 'https://secure.twispay.com', // optional, defaults to production URL +}) + +// Decrypt IPN webhook response +const orderData = xMoney.checkout.decrypt(encryptedPayload) +console.log(orderData.transactionStatus) // 'complete-ok', 'complete-failed', etc. +console.log(orderData.orderId, orderData.transactionId) +``` + +## 📘 TypeScript Support + +The SDK is written in TypeScript and provides comprehensive type definitions: + +```typescript +import type { + Card, + Customer, + Order, + PaginatedResponse, + Transaction, + XMoneyConfig, +} from '@xmoney/api-sdk' +``` + +## 🌐 Platform Support + +The SDK automatically detects and adapts to your runtime environment: + +- **Node.js**: Uses native crypto and Node.js HTTP client +- **Browsers**: Uses SubtleCrypto API and Fetch API +- **Edge Workers**: Cloudflare Workers, Vercel Edge, etc. +- **Deno/Bun**: Uses web-standard APIs + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 💬 Support + +- 📧 Email: support@xmoney.com +- 📖 Documentation: https://docs.xmoney.com +- 🐛 Issues: https://github.com/Twispay/api-sdk/issues + +[npm-version-src]: https://img.shields.io/npm/v/@xmoney/api-sdk?style=flat&colorA=080f12&colorB=7c4dff +[npm-version-href]: https://npmjs.com/package/@xmoney/api-sdk +[npm-downloads-src]: https://img.shields.io/npm/dm/@xmoney/api-sdk?style=flat&colorA=080f12&colorB=7c4dff +[npm-downloads-href]: https://npmjs.com/package/@xmoney/api-sdk +[bundle-src]: https://img.shields.io/bundlephobia/minzip/@xmoney/api-sdk?style=flat&colorA=080f12&colorB=7c4dff&label=minzip +[bundle-href]: https://bundlephobia.com/result?p=@xmoney/api-sdk +[license-src]: https://img.shields.io/github/license/Twispay/api-sdk.svg?style=flat&colorA=080f12&colorB=7c4dff +[license-href]: https://github.com/Twispay/api-sdk/blob/main/LICENSE +[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=7c4dff +[jsdocs-href]: https://www.jsdocs.io/package/@xmoney/api-sdk diff --git a/Readme.md b/Readme.md deleted file mode 100644 index ef7c337..0000000 --- a/Readme.md +++ /dev/null @@ -1,86 +0,0 @@ -# @xmoney/api-sdk - -## Instalaltion - -`npm install @xmoney/api-sdk` -
-
- -## Usage - -#### Get needed params for checkout initialization: -```typescript -import xMoney from "@xmoney/api-sdk"; - -const xMoneyCheckout = new xMoney({ - secretKey: "sk_test_secretKey", -}); - -const order = xMoneyCheckout.initializeCheckout({ - publicKey: 'pk_test_abc123', - customer: { - identifier: "customerIdentifier", - firstName: "John", - lastName: "Doe", - country: "RO", - city: "Bucharest", - email: "john.doe@test.com", - }, - order: { - orderId: "myUniqueOrderId", - description: "Order Description", - type: "purchase", - amount: 100, - currency: "EUR", - }, - cardTransactionMode: "authAndCapture", - backUrl: "https://127.0.0.1:8080", -}); -``` - -#### Get HTML for hosted checkout (mobile/webview): -```typescript -import xMoney from "@xmoney/api-sdk"; - -const xMoneyCheckout = new xMoney({ - secretKey: "sk_test_secretKey", -}); - -const orderHtml = xMoneyCheckout.initializeHostedCheckout({ - publicKey: 'pk_test_abc123', - customer: { - identifier: "customerIdentifier", - firstName: "John", - lastName: "Doe", - country: "RO", - city: "Bucharest", - email: "john.doe@test.com", - }, - order: { - orderId: "myUniqueOrderId", - description: "Order Description", - type: "purchase", - amount: 100, - currency: "EUR", - }, - cardTransactionMode: "authAndCapture", - backUrl: "https://127.0.0.1:8080", -}); -``` - -#### How to decrypt order webhook payload: -```typescript -import xMoney from "@xmoney/api-sdk"; - -const xMoneyCheckout = new xMoney({ - secretKey: "sk_test_secretKey", -}); - -const webhookPayload = 'ecryptedPayload'; - -console.log( - xMoneyCheckout.decryptOrderResponse(webhookPayload) -); -``` - - diff --git a/build.config.ts b/build.config.ts new file mode 100644 index 0000000..bd669fd --- /dev/null +++ b/build.config.ts @@ -0,0 +1,13 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + clean: true, + declaration: true, + rollup: { + emitCJS: true, + }, + entries: [ + 'src/index.web', + 'src/index.node', + ], +}) diff --git a/eslint.config.js b/eslint.config.js index ccc6fed..0e5df75 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,61 +1,7 @@ -const eslint = require('@eslint/js'); -const tseslint = require('@typescript-eslint/eslint-plugin'); -const tsparser = require('@typescript-eslint/parser'); -const prettier = require('eslint-config-prettier'); +// @ts-check +import antfu from '@antfu/eslint-config' -module.exports = [ - eslint.configs.recommended, - { - files: ['**/*.ts'], - languageOptions: { - parser: tsparser, - parserOptions: { - project: './tsconfig.json', - sourceType: 'module', - }, - globals: { - describe: 'readonly', - it: 'readonly', - expect: 'readonly', - beforeEach: 'readonly', - afterEach: 'readonly', - Buffer: 'readonly', - process: 'readonly', - }, - }, - plugins: { - '@typescript-eslint': tseslint, - }, - rules: { - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unused-vars": ["error", { - "varsIgnorePattern": "^[A-Z]", - "argsIgnorePattern": "^_", - 'vars': 'all', - 'args': 'after-used', - 'ignoreRestSiblings': true, - }], - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-inferrable-types": "off", - "require-await": "error", - "@typescript-eslint/no-floating-promises": "error", - "max-len": "off", - "semi": "off", - "comma-dangle": "off", - "eol-last": "off", - "no-undef": "off", // TypeScript handles this - "no-unused-vars": "off", // Let @typescript-eslint/no-unused-vars handle this - }, - }, - { - files: ['**/*.enum.ts'], - rules: { - "@typescript-eslint/no-unused-vars": "off", - "no-unused-vars": "off", - }, - }, - prettier, -]; \ No newline at end of file +export default antfu({ + type: 'lib', + pnpm: true, +}) diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 9f1e61d..0000000 --- a/jest.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - roots: ['/src'], - testMatch: ['**/*.spec.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest', - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], -}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ec107d4..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5241 +0,0 @@ -{ - "name": "@xmoney/api-sdk", - "version": "0.1.2", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@xmoney/api-sdk", - "version": "0.1.2", - "license": "MIT", - "dependencies": { - "axios": "^1.8.4", - "qs": "^6.14.0" - }, - "devDependencies": { - "@eslint/js": "^9.24.0", - "@types/jest": "^29.5.14", - "@types/node": "^22.15.3", - "@types/qs": "^6.9.18", - "@typescript-eslint/eslint-plugin": "^8.31.1", - "@typescript-eslint/parser": "^8.31.1", - "eslint": "^9.24.0", - "eslint-config-prettier": "^10.1.2", - "jest": "^29.7.0", - "prettier": "^3.5.3", - "ts-jest": "^29.3.2", - "tslib": "^2.8.1", - "typescript": "^5.8.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", - "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", - "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", - "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", - "dev": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz", - "integrity": "sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.31.1", - "@typescript-eslint/type-utils": "8.31.1", - "@typescript-eslint/utils": "8.31.1", - "@typescript-eslint/visitor-keys": "8.31.1", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.1.tgz", - "integrity": "sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.31.1", - "@typescript-eslint/types": "8.31.1", - "@typescript-eslint/typescript-estree": "8.31.1", - "@typescript-eslint/visitor-keys": "8.31.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz", - "integrity": "sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.31.1", - "@typescript-eslint/visitor-keys": "8.31.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz", - "integrity": "sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "8.31.1", - "@typescript-eslint/utils": "8.31.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.1.tgz", - "integrity": "sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz", - "integrity": "sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.31.1", - "@typescript-eslint/visitor-keys": "8.31.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.1.tgz", - "integrity": "sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.31.1", - "@typescript-eslint/types": "8.31.1", - "@typescript-eslint/typescript-estree": "8.31.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.31.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz", - "integrity": "sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.31.1", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001715", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", - "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.143", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.143.tgz", - "integrity": "sha512-QqklJMOFBMqe46k8iIOwA9l2hz57V2OKMmP5eSWcUvwx+mASAsbU+wkF1pHjn9ZVSBPrsYWr4/W/95y5SwYg2g==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", - "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-jest": { - "version": "29.3.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", - "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", - "dev": true, - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.1", - "type-fest": "^4.39.1", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz", - "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index aa59a3d..f7e892e 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,78 @@ { "name": "@xmoney/api-sdk", + "type": "module", "version": "0.1.2", - "scripts": { - "test": "jest", - "build": "tsc", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"", - "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "format": "prettier --write \"src/**/*.{ts,js,json}\"", - "format:check": "prettier --check \"src/**/*.{ts,js,json}\"" - }, - "main": "lib/main.js", - "types": "lib/main.d.ts", - "files": [ - "lib/**/*" - ], + "packageManager": "pnpm@10.11.0", + "description": "xMoney API SDK for Node.js", + "author": "xMoney", + "license": "MIT", + "homepage": "https://www.xmoney.com", "repository": { "type": "git", "url": "git+https://github.com/Twispay/api-sdk.git" }, + "bugs": "https://github.com/Twispay/api-sdk/issues", "keywords": [ "xmoney", - "api-sdk" + "api-sdk", + "payment" ], - "author": "xMoney", - "license": "MIT", + "sideEffects": false, + "exports": { + ".": { + "browser": "./dist/index.web.mjs", + "bun": "./dist/index.web.mjs", + "deno": "./dist/index.web.mjs", + "edge-light": "./dist/index.web.mjs", + "edge-routine": "./dist/index.web.mjs", + "netlify": "./dist/index.web.mjs", + "react-native": "./dist/index.web.mjs", + "wintercg": "./dist/index.web.mjs", + "worker": "./dist/index.web.mjs", + "workerd": "./dist/index.web.mjs", + "node": { + "types": "./dist/index.node.d.ts", + "import": "./dist/index.node.mjs", + "require": "./dist/index.node.cjs" + }, + "types": "./dist/index.web.d.ts", + "import": "./dist/index.web.mjs", + "require": "./dist/index.web.cjs" + } + }, + "main": "./dist/index.node.cjs", + "module": "./dist/index.web.mjs", + "browser": "./dist/index.web.mjs", + "types": "./dist/index.web.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "unbuild", + "dev": "unbuild --stub", + "lint": "eslint", + "test": "vitest", + "typecheck": "tsc --noEmit", + "prepare": "simple-git-hooks" + }, "devDependencies": { - "@eslint/js": "^9.24.0", - "@types/jest": "^29.5.14", - "@types/node": "^22.15.3", - "@types/qs": "^6.9.18", - "@typescript-eslint/eslint-plugin": "^8.31.1", - "@typescript-eslint/parser": "^8.31.1", - "eslint": "^9.24.0", - "eslint-config-prettier": "^10.1.2", - "jest": "^29.7.0", - "prettier": "^3.5.3", - "ts-jest": "^29.3.2", - "tslib": "^2.8.1", - "typescript": "^5.8.3" + "@antfu/eslint-config": "catalog:cli", + "@types/node": "catalog:types", + "@types/qs": "catalog:types", + "@vitest/coverage-v8": "catalog:testing", + "eslint": "catalog:cli", + "lint-staged": "catalog:cli", + "simple-git-hooks": "catalog:cli", + "typescript": "catalog:cli", + "unbuild": "catalog:cli", + "vite": "catalog:cli", + "vitest": "catalog:testing", + "vitest-package-exports": "catalog:testing" + }, + "simple-git-hooks": { + "pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && npx lint-staged" }, - "dependencies": { - "axios": "^1.8.4", - "qs": "^6.14.0" + "lint-staged": { + "*": "eslint --fix" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..2fd75b4 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5416 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + cli: + '@antfu/eslint-config': + specifier: ^4.13.2 + version: 4.13.2 + eslint: + specifier: ^9.24.0 + version: 9.27.0 + lint-staged: + specifier: ^16.1.0 + version: 16.1.0 + simple-git-hooks: + specifier: ^2.13.0 + version: 2.13.0 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + unbuild: + specifier: ^3.5.0 + version: 3.5.0 + vite: + specifier: ^6.3.5 + version: 6.3.5 + testing: + '@vitest/coverage-v8': + specifier: ^3.1.4 + version: 3.1.4 + vitest: + specifier: ^3.1.4 + version: 3.1.4 + vitest-package-exports: + specifier: ^0.1.1 + version: 0.1.1 + types: + '@types/node': + specifier: ^22.15.3 + version: 22.15.23 + '@types/qs': + specifier: ^6.9.18 + version: 6.14.0 + +importers: + + .: + devDependencies: + '@antfu/eslint-config': + specifier: catalog:cli + version: 4.13.2(@vue/compiler-sfc@3.5.15)(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0)) + '@types/node': + specifier: catalog:types + version: 22.15.23 + '@types/qs': + specifier: catalog:types + version: 6.14.0 + '@vitest/coverage-v8': + specifier: catalog:testing + version: 3.1.4(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0)) + eslint: + specifier: catalog:cli + version: 9.27.0(jiti@2.4.2) + lint-staged: + specifier: catalog:cli + version: 16.1.0 + simple-git-hooks: + specifier: catalog:cli + version: 2.13.0 + typescript: + specifier: catalog:cli + version: 5.8.3 + unbuild: + specifier: catalog:cli + version: 3.5.0(typescript@5.8.3) + vite: + specifier: catalog:cli + version: 6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + vitest: + specifier: catalog:testing + version: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + vitest-package-exports: + specifier: catalog:testing + version: 0.1.1 + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@antfu/eslint-config@4.13.2': + resolution: {integrity: sha512-F+IVIQUCfw6eW4H06c9a9USJ3UOnoBx4I0qsTL3kO6GcyJB6mwk+nawFf95DfHKT3fJKv58YPPz0XCmsY/w0XA==} + hasBin: true + peerDependencies: + '@eslint-react/eslint-plugin': ^1.38.4 + '@prettier/plugin-xml': ^3.4.1 + '@unocss/eslint-plugin': '>=0.50.0' + astro-eslint-parser: ^1.0.2 + eslint: ^9.10.0 + eslint-plugin-astro: ^1.2.0 + eslint-plugin-format: '>=0.1.0' + eslint-plugin-react-hooks: ^5.2.0 + eslint-plugin-react-refresh: ^0.4.19 + eslint-plugin-solid: ^0.14.3 + eslint-plugin-svelte: '>=2.35.1' + eslint-plugin-vuejs-accessibility: ^2.4.1 + prettier-plugin-astro: ^0.14.0 + prettier-plugin-slidev: ^1.0.5 + svelte-eslint-parser: '>=0.37.0' + peerDependenciesMeta: + '@eslint-react/eslint-plugin': + optional: true + '@prettier/plugin-xml': + optional: true + '@unocss/eslint-plugin': + optional: true + astro-eslint-parser: + optional: true + eslint-plugin-astro: + optional: true + eslint-plugin-format: + optional: true + eslint-plugin-react-hooks: + optional: true + eslint-plugin-react-refresh: + optional: true + eslint-plugin-solid: + optional: true + eslint-plugin-svelte: + optional: true + eslint-plugin-vuejs-accessibility: + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-slidev: + optional: true + svelte-eslint-parser: + optional: true + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.3': + resolution: {integrity: sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.27.3': + resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@clack/core@0.4.2': + resolution: {integrity: sha512-NYQfcEy8MWIxrT5Fj8nIVchfRFA26yYKJcvBS7WlUIlw2OmQOY9DhGGXMovyI5J5PpxrCPGkgUi207EBrjpBvg==} + + '@clack/prompts@0.10.1': + resolution: {integrity: sha512-Q0T02vx8ZM9XSv9/Yde0jTmmBQufZhPJfYAg2XrrrxWWaZgq1rr8nU8Hv710BQ1dhoP8rtY7YUdpGej2Qza/cw==} + + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + + '@es-joy/jsdoccomment@0.50.2': + resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-plugin-eslint-comments@4.5.0': + resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.2.9': + resolution: {integrity: sha512-gCdSY54n7k+driCadyMNv8JSPzYLeDVM/ikZRtvtROBpRdFSkS8W9A82MqsaY7lZuwL0wiapgD0NT1xT0hyJsA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.20.0': + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.2': + resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.10.0': + resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.27.0': + resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/markdown@6.4.0': + resolution: {integrity: sha512-J07rR8uBSNFJ9iliNINrchilpkmCihPmTVotpThUeKEn5G8aBBZnkjNBy/zovhJA5LBk1vWU9UDlhqKSc/dViQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.1': + resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@napi-rs/wasm-runtime@0.2.10': + resolution: {integrity: sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.4': + resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@rollup/plugin-alias@5.1.1': + resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-commonjs@28.0.3': + resolution: {integrity: sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.1': + resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@6.0.2': + resolution: {integrity: sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.41.1': + resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.41.1': + resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.41.1': + resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.41.1': + resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.41.1': + resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.41.1': + resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.41.1': + resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.41.1': + resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.41.1': + resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.41.1': + resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.41.1': + resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': + resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.41.1': + resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.41.1': + resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.41.1': + resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.41.1': + resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.41.1': + resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.41.1': + resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.41.1': + resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.41.1': + resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==} + cpu: [x64] + os: [win32] + + '@stylistic/eslint-plugin@4.4.0': + resolution: {integrity: sha512-bIh/d9X+OQLCAMdhHtps+frvyjvAM4B1YlSJzcEEhl7wXLIqPar3ngn9DrHhkBOrTA/z9J0bUMtctAspe0dxdQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=9.0.0' + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.15.23': + resolution: {integrity: sha512-7Ec1zaFPF4RJ0eXu1YT/xgiebqwqoJz8rYPDi/O2BcZ++Wpt0Kq9cl0eg6NN6bYbPnR67ZLo7St5Q3UK0SnARw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@typescript-eslint/eslint-plugin@8.33.0': + resolution: {integrity: sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.33.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.33.0': + resolution: {integrity: sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.33.0': + resolution: {integrity: sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.33.0': + resolution: {integrity: sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.33.0': + resolution: {integrity: sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.33.0': + resolution: {integrity: sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.33.0': + resolution: {integrity: sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.33.0': + resolution: {integrity: sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.33.0': + resolution: {integrity: sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.33.0': + resolution: {integrity: sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-darwin-arm64@1.7.4': + resolution: {integrity: sha512-cyUy4VQpN9R9gEKbDfsErjDopQumQzKH75Rh171JoHeFUVOk1r2QcwvNXu+S2BPsM6gSdwtTEWBjPQv+crqr9Q==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.7.4': + resolution: {integrity: sha512-di1PSrdokgVsTQYI6eJEGywyj1k47JQPAWavqIYbwLfgvLit9w/EvUB2eV9vg9i8x8M13ZC2hjTumK5XtlP32A==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.7.4': + resolution: {integrity: sha512-F/actecQkYrRYvjcn9yH+Gjd0LMLP7P2y12nYjQS4WwuxpZ6IDXIc5eRQO0Omj4/a0BSlI4VcvswUzWGVJVr3A==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.4': + resolution: {integrity: sha512-+ttV8G5PFz8WJVrGPjQpy9kHh3/b6TT75Ckxt1iPD0eiUM1WaDvaH0E7IG7Z5s/vLls9HqwDjnrNGik6JQVz0w==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.7.4': + resolution: {integrity: sha512-0EZ/4CAaItMY95HgM/SGLpejJZmw1eI8V/V3pVdXt0WcpAKfKQistO8AEEPfcXGZJNJ08TX/OgqwFgCnZBG2+A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.7.4': + resolution: {integrity: sha512-Sft3f7z2sUxgo+WaR/EdHwPAqCyZfzbXP25GBbSZLLRFkFe+9eR+J1NvuxcxUCUXqhptURYHcBkoEsuHAcyjqg==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.7.4': + resolution: {integrity: sha512-PeaiFDCEJLQFcF0LvNZizjzoUm1vxsFmrwxJ0W0lREhEPrgrsy3wZIPItMR1UJJPg9AbDpHqLDo95pyTY9vXow==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.7.4': + resolution: {integrity: sha512-Nodq6VAFRW0yFqknizLgZsKHCdmI+L0utUSFoggCb66PAAPo+GzDYbt4h1eI1hA7oXzksN5rCRWxi2hEVTNxyg==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.7.4': + resolution: {integrity: sha512-d1HN0cTUG8eRUIR8HSrH98rdVLJIIx3TCn5+naiPXV0IiQ+DslhAAXAaUOu1md5jjEzQzOYrxDmqcB7fQ9FzpQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.7.4': + resolution: {integrity: sha512-JRN7y1x7NltIMtCtRroVj/9S0KsSNmvcmNiQ3MlBAV4TIG3jgRCeD9mDa+uKLBYWP1oOAMPkTG5j0Jy0ol6v/Q==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.7.4': + resolution: {integrity: sha512-QjznjkmS0zy+utl/aH1cH15QySQ9YCs2OuVrL9wEqdZY53/6wySgSheB9Nykk5eG7H60qriFMhYWH3tYLf+Gqw==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.7.4': + resolution: {integrity: sha512-nZkINWJ7lBg5BRtY2CwPs80GSbGdJLYseweXJJxD2lisR0xkIk2ao3b3wQuVQ/nho+DCyxSo+DGXrY/gaROZdA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.7.4': + resolution: {integrity: sha512-00YaOvTCoVwef87i/ouTwYCRUVMXajTe79POa/vjBYedtO7ta1KidP4r9CjW0ie4no4au9wCn61dPHL8DZRLbw==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.7.4': + resolution: {integrity: sha512-8a9+bokNHo3tOHSlRAR1Q5lwRCGOoKsch6LUbyHLwemg04CVDl1KRlqg/LJ+CvOKxoMj/8jAX2Or94jvWskmFg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.7.4': + resolution: {integrity: sha512-qP8j+fKfkMkUskDU4BG3tG7rl9vUoKRrHm6TtkdID6PdBBsn3vgqzEHvrH8y5sT4jnj4ewqmtcaUkFwv2/XtIw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.7.4': + resolution: {integrity: sha512-ExEnp2Yio6cfnTrwLffLjYAOvZJ9ktLivs5CWrbIYeyh/wP46NiGYtZhs+WVYpOdmwX2M32QRCw12nh/M+ViXw==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.7.4': + resolution: {integrity: sha512-+ebP/XNaMKCdrNNbxzbjZ2H8umZOybfL7FC/YAStsPNUJn0O3Tvm2I/vVhz1UrNCT+Ec/xHUX7t02CHQVPtwwQ==} + cpu: [x64] + os: [win32] + + '@vitest/coverage-v8@3.1.4': + resolution: {integrity: sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==} + peerDependencies: + '@vitest/browser': 3.1.4 + vitest: 3.1.4 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/eslint-plugin@1.2.1': + resolution: {integrity: sha512-JQr1jdVcrsoS7Sdzn83h9sq4DvREf9Q/onTZbJCqTVlv/76qb+TZrLv/9VhjnjSMHweQH5FdpMDeCR6aDe2fgw==} + peerDependencies: + eslint: '>= 8.57.0' + typescript: '>= 5.0.0' + vitest: '*' + peerDependenciesMeta: + typescript: + optional: true + vitest: + optional: true + + '@vitest/expect@3.1.4': + resolution: {integrity: sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==} + + '@vitest/mocker@3.1.4': + resolution: {integrity: sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.1.4': + resolution: {integrity: sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==} + + '@vitest/runner@3.1.4': + resolution: {integrity: sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==} + + '@vitest/snapshot@3.1.4': + resolution: {integrity: sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==} + + '@vitest/spy@3.1.4': + resolution: {integrity: sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==} + + '@vitest/utils@3.1.4': + resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==} + + '@vue/compiler-core@3.5.15': + resolution: {integrity: sha512-nGRc6YJg/kxNqbv/7Tg4juirPnjHvuVdhcmDvQWVZXlLHjouq7VsKmV1hIxM/8yKM0VUfwT/Uzc0lO510ltZqw==} + + '@vue/compiler-dom@3.5.15': + resolution: {integrity: sha512-ZelQd9n+O/UCBdL00rlwCrsArSak+YLZpBVuNDio1hN3+wrCshYZEDUO3khSLAzPbF1oQS2duEoMDUHScUlYjA==} + + '@vue/compiler-sfc@3.5.15': + resolution: {integrity: sha512-3zndKbxMsOU6afQWer75Zot/aydjtxNj0T2KLg033rAFaQUn2PGuE32ZRe4iMhflbTcAxL0yEYsRWFxtPro8RQ==} + + '@vue/compiler-ssr@3.5.15': + resolution: {integrity: sha512-gShn8zRREZbrXqTtmLSCffgZXDWv8nHc/GhsW+mbwBfNZL5pI96e7IWcIq8XGQe1TLtVbu7EV9gFIVSmfyarPg==} + + '@vue/shared@3.5.15': + resolution: {integrity: sha512-bKvgFJJL1ZX9KxMCTQY6xD9Dhe3nusd1OhyOb1cJYGqvAr0Vg8FIjHPMOEVbJ9GDT9HG+Bjdn4oS8ohKP8EvoA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + ansis@4.0.0: + resolution: {integrity: sha512-P8nrHI1EyW9OfBt1X7hMSwGN2vwRuqHSKJAT1gbLWZRzDa24oHjYwGHvEgHeBepupzk878yS/HBZ0NMPYtbolw==} + engines: {node: '>=14'} + + are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.5: + resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + builtin-modules@5.0.0: + resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} + engines: {node: '>=18.20'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001718: + resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + ci-info@4.2.0: + resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + engines: {node: '>=8'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + core-js-compat@3.42.0: + resolution: {integrity: sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@7.0.7: + resolution: {integrity: sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + cssnano-utils@5.0.1: + resolution: {integrity: sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + cssnano@7.0.7: + resolution: {integrity: sha512-evKu7yiDIF7oS+EIpwFlMF730ijRyLFaM2o5cTxRGJR9OKHKkc+qP443ZEVR9kZG0syaAJJCPJyfv5pbrxlSng==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.1.0: + resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.159: + resolution: {integrity: sha512-CEvHptWAMV5p6GJ0Lq8aheyvVbfzVrv5mmidu1D3pidoVNkB3tTBsTMVtPJ+rzRK5oV229mCLz9Zj/hNvU8GBA==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-compat-utils@0.6.5: + resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-config-flat-gitignore@2.1.0: + resolution: {integrity: sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA==} + peerDependencies: + eslint: ^9.5.0 + + eslint-flat-config-utils@2.1.0: + resolution: {integrity: sha512-6fjOJ9tS0k28ketkUcQ+kKptB4dBZY2VijMZ9rGn8Cwnn1SH0cZBoPXT8AHBFHxmHcLFQK9zbELDinZ2Mr1rng==} + + eslint-import-context@0.1.6: + resolution: {integrity: sha512-/e2ZNPDLCrU8niIy0pddcvXuoO2YrKjf3NAIX+60mHJBT4yv7mqCqvVdyCW2E720e25e4S/1OSVef4U6efGLFg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-json-compat-utils@0.2.1: + resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==} + engines: {node: '>=12'} + peerDependencies: + '@eslint/json': '*' + eslint: '*' + jsonc-eslint-parser: ^2.4.0 + peerDependenciesMeta: + '@eslint/json': + optional: true + + eslint-merge-processors@2.0.0: + resolution: {integrity: sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA==} + peerDependencies: + eslint: '*' + + eslint-plugin-antfu@3.1.1: + resolution: {integrity: sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg==} + peerDependencies: + eslint: '*' + + eslint-plugin-command@3.2.1: + resolution: {integrity: sha512-PcpzWe8dvAPaBobxE9zgz1w94fO4JYvzciDzw6thlUb9Uqf5e2/gJz97itOGxvdq+mFeudi71m1OGFgvWmb93w==} + peerDependencies: + eslint: '*' + + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-import-x@4.13.3: + resolution: {integrity: sha512-CDewJDEeYQhm94KGCDYiuwU1SdaWc/vh+SziSKkF7kichAqAFnQYtSYUvSwSBbiBjYLxV5uUxocxxQobRI9YXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + eslint-plugin-jsdoc@50.6.17: + resolution: {integrity: sha512-hq+VQylhd12l8qjexyriDsejZhqiP33WgMTy2AmaGZ9+MrMWVqPECsM87GPxgHfQn0zw+YTuhqjUfk1f+q67aQ==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-jsonc@2.20.1: + resolution: {integrity: sha512-gUzIwQHXx7ZPypUoadcyRi4WbHW2TPixDr0kqQ4miuJBU0emJmyGTlnaT3Og9X2a8R1CDayN9BFSq5weGWbTng==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + eslint-plugin-n@17.18.0: + resolution: {integrity: sha512-hvZ/HusueqTJ7VDLoCpjN0hx4N4+jHIWTXD4TMLHy9F23XkDagR9v+xQWRWR57yY55GPF8NnD4ox9iGTxirY8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.23.0' + + eslint-plugin-no-only-tests@3.3.0: + resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} + engines: {node: '>=5.0.0'} + + eslint-plugin-perfectionist@4.13.0: + resolution: {integrity: sha512-dsPwXwV7IrG26PJ+h1crQ1f5kxay/gQAU0NJnbVTQc91l5Mz9kPjyIZ7fXgie+QSgi8a+0TwGbfaJx+GIhzuoQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + eslint: '>=8.45.0' + + eslint-plugin-pnpm@0.3.1: + resolution: {integrity: sha512-vi5iHoELIAlBbX4AW8ZGzU3tUnfxuXhC/NKo3qRcI5o9igbz6zJUqSlQ03bPeMqWIGTPatZnbWsNR1RnlNERNQ==} + peerDependencies: + eslint: ^9.0.0 + + eslint-plugin-regexp@2.7.0: + resolution: {integrity: sha512-U8oZI77SBtH8U3ulZ05iu0qEzIizyEDXd+BWHvyVxTOjGwcDcvy/kEpgFG4DYca2ByRLiVPFZ2GeH7j1pdvZTA==} + engines: {node: ^18 || >=20} + peerDependencies: + eslint: '>=8.44.0' + + eslint-plugin-toml@0.12.0: + resolution: {integrity: sha512-+/wVObA9DVhwZB1nG83D2OAQRrcQZXy+drqUnFJKymqnmbnbfg/UPmEMCKrJNcEboUGxUjYrJlgy+/Y930mURQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + eslint-plugin-unicorn@59.0.1: + resolution: {integrity: sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==} + engines: {node: ^18.20.0 || ^20.10.0 || >=21.0.0} + peerDependencies: + eslint: '>=9.22.0' + + eslint-plugin-unused-imports@4.1.4: + resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 + eslint: ^9.0.0 || ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + + eslint-plugin-vue@10.1.0: + resolution: {integrity: sha512-/VTiJ1eSfNLw6lvG9ENySbGmcVvz6wZ9nA7ZqXlLBY2RkaF15iViYKxglWiIch12KiLAj0j1iXPYU6W4wTROFA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + vue-eslint-parser: ^10.0.0 + + eslint-plugin-yml@1.18.0: + resolution: {integrity: sha512-9NtbhHRN2NJa/s3uHchO3qVVZw0vyOIvWlXWGaKCr/6l3Go62wsvJK5byiI6ZoYztDsow4GnS69BZD3GnqH3hA==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + eslint-processor-vue-blocks@2.0.0: + resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==} + peerDependencies: + '@vue/compiler-sfc': ^3.3.0 + eslint: '>=9.0.0' + + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.27.0: + resolution: {integrity: sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + expect-type@1.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + engines: {node: '>=12.0.0'} + + exsolve@1.0.5: + resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fault@2.0.1: + resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} + + fdir@6.4.5: + resolution: {integrity: sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@16.2.0: + resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==} + engines: {node: '>=18'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.4: + resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + is-builtin-module@5.0.0: + resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} + engines: {node: '>=18.20'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} + engines: {node: '>=12.0.0'} + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + knitwork@1.2.0: + resolution: {integrity: sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lint-staged@16.1.0: + resolution: {integrity: sha512-HkpQh69XHxgCjObjejBT3s2ILwNjFx8M3nw+tJ/ssBauDlIpkx2RpqWSi1fBgkXLSSXnbR3iEq1NkVtpvV+FLQ==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + engines: {node: '>=18.0.0'} + + local-pkg@1.1.1: + resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} + engines: {node: '>=14'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-frontmatter@2.0.1: + resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-frontmatter@2.0.0: + resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdist@2.3.0: + resolution: {integrity: sha512-thkRk+pHdudjdZT3FJpPZ2+pncI6mGlH/B+KBVddlZj4MrFGW41sRIv1wZawZUHU8v7cttGaj+5nx8P+dG664A==} + hasBin: true + peerDependencies: + sass: ^1.85.0 + typescript: '>=5.7.3' + vue: ^3.5.13 + vue-sfc-transformer: ^0.1.1 + vue-tsc: ^1.8.27 || ^2.0.21 + peerDependenciesMeta: + sass: + optional: true + typescript: + optional: true + vue: + optional: true + vue-sfc-transformer: + optional: true + vue-tsc: + optional: true + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nano-spawn@1.0.2: + resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + engines: {node: '>=20.17'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.2.4: + resolution: {integrity: sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + natural-orderby@5.0.0: + resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} + engines: {node: '>=18'} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-gitignore@2.0.0: + resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} + engines: {node: '>=14'} + + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.1.0: + resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + pnpm-workspace-yaml@0.3.1: + resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==} + + postcss-calc@10.1.1: + resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + + postcss-colormin@7.0.3: + resolution: {integrity: sha512-xZxQcSyIVZbSsl1vjoqZAcMYYdnJsIyG8OvqShuuqf12S88qQboxxEy0ohNCOLwVPXTU+hFHvJPACRL2B5ohTA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-convert-values@7.0.5: + resolution: {integrity: sha512-0VFhH8nElpIs3uXKnVtotDJJNX0OGYSZmdt4XfSfvOMrFw1jKfpwpZxfC4iN73CTM/MWakDEmsHQXkISYj4BXw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-comments@7.0.4: + resolution: {integrity: sha512-6tCUoql/ipWwKtVP/xYiFf1U9QgJ0PUvxN7pTcsQ8Ns3Fnwq1pU5D5s1MhT/XySeLq6GXNvn37U46Ded0TckWg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-duplicates@7.0.2: + resolution: {integrity: sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-empty@7.0.1: + resolution: {integrity: sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-overridden@7.0.1: + resolution: {integrity: sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-merge-longhand@7.0.5: + resolution: {integrity: sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-merge-rules@7.0.5: + resolution: {integrity: sha512-ZonhuSwEaWA3+xYbOdJoEReKIBs5eDiBVLAGpYZpNFPzXZcEE5VKR7/qBEQvTZpiwjqhhqEQ+ax5O3VShBj9Wg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-font-values@7.0.1: + resolution: {integrity: sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-gradients@7.0.1: + resolution: {integrity: sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-params@7.0.3: + resolution: {integrity: sha512-vUKV2+f5mtjewYieanLX0xemxIp1t0W0H/D11u+kQV/MWdygOO7xPMkbK+r9P6Lhms8MgzKARF/g5OPXhb8tgg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-selectors@7.0.5: + resolution: {integrity: sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-nested@7.0.2: + resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-normalize-charset@7.0.1: + resolution: {integrity: sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-display-values@7.0.1: + resolution: {integrity: sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-positions@7.0.1: + resolution: {integrity: sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-repeat-style@7.0.1: + resolution: {integrity: sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-string@7.0.1: + resolution: {integrity: sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-timing-functions@7.0.1: + resolution: {integrity: sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-unicode@7.0.3: + resolution: {integrity: sha512-EcoA29LvG3F+EpOh03iqu+tJY3uYYKzArqKJHxDhUYLa2u58aqGq16K6/AOsXD9yqLN8O6y9mmePKN5cx6krOw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-url@7.0.1: + resolution: {integrity: sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-whitespace@7.0.1: + resolution: {integrity: sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-ordered-values@7.0.2: + resolution: {integrity: sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-reduce-initial@7.0.3: + resolution: {integrity: sha512-RFvkZaqiWtGMlVjlUHpaxGqEL27lgt+Q2Ixjf83CRAzqdo+TsDyGPtJUbPx2MuYIJ+sCQc2TrOvRnhcXQfgIVA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-reduce-transforms@7.0.1: + resolution: {integrity: sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss-svgo@7.0.2: + resolution: {integrity: sha512-5Dzy66JlnRM6pkdOTF8+cGsB1fnERTE8Nc+Eed++fOWo1hdsBptCsbG8UuJkgtZt75bRtMJIrPeZmtfANixdFA==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.4.32 + + postcss-unique-selectors@7.0.4: + resolution: {integrity: sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + quansync@0.2.10: + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + refa@0.12.1: + resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + regexp-ast-analysis@0.7.1: + resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + hasBin: true + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup-plugin-dts@6.2.1: + resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + + rollup@4.41.1: + resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + scslre@0.3.0: + resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} + engines: {node: ^14.0.0 || >=16.0.0} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-git-hooks@2.13.0: + resolution: {integrity: sha512-N+goiLxlkHJlyaYEglFypzVNMaNplPAk5syu0+OPp/Bk6dwVoXF6FfOw2vO0Dp+JHsBaI+w6cm8TnFl2Hw6tDA==} + hasBin: true + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-indent@4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + stylehacks@7.0.5: + resolution: {integrity: sha512-5kNb7V37BNf0Q3w+1pxfa+oiNPS++/b4Jil9e/kPDgrk1zjEd6uR7SZeJiYaLYH6RRSC1XX2/37OTeU/4FvuIA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + synckit@0.11.6: + resolution: {integrity: sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==} + engines: {node: ^14.18.0 || >=16.0.0} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toml-eslint-parser@0.10.0: + resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + unbuild@3.5.0: + resolution: {integrity: sha512-DPFttsiADnHRb/K+yJ9r9jdn6JyXlsmdT0S12VFC14DFSJD+cxBnHq+v0INmqqPVPxOoUjvJFYUVIb02rWnVeA==} + hasBin: true + peerDependencies: + typescript: ^5.7.3 + peerDependenciesMeta: + typescript: + optional: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + unrs-resolver@1.7.4: + resolution: {integrity: sha512-rpd1iZ7usb6pa41f9OnPpGxb6CasomiVg4XCXV6QS8s66jnvDJnUNG27h+siYeHFt7Gu/DKO225w2RC2/7Edkw==} + + untyped@2.0.0: + resolution: {integrity: sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==} + hasBin: true + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-node@3.1.4: + resolution: {integrity: sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest-package-exports@0.1.1: + resolution: {integrity: sha512-rXgU5noxaaMA5VJXgVPhXRzPiQ1WFmWwgo8dQFPg9j/yFM028scO0IAylFHVI8XMRevhozEt1usa6RQYQY26lg==} + + vitest@3.1.4: + resolution: {integrity: sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.1.4 + '@vitest/ui': 3.1.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vue-eslint-parser@10.1.3: + resolution: {integrity: sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + yaml-eslint-parser@1.3.0: + resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@antfu/eslint-config@4.13.2(@vue/compiler-sfc@3.5.15)(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0))': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@clack/prompts': 0.10.1 + '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.27.0(jiti@2.4.2)) + '@eslint/markdown': 6.4.0 + '@stylistic/eslint-plugin': 4.4.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@vitest/eslint-plugin': 1.2.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0)) + ansis: 4.0.0 + cac: 6.7.14 + eslint: 9.27.0(jiti@2.4.2) + eslint-config-flat-gitignore: 2.1.0(eslint@9.27.0(jiti@2.4.2)) + eslint-flat-config-utils: 2.1.0 + eslint-merge-processors: 2.0.0(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-antfu: 3.1.1(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-command: 3.2.1(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-import-x: 4.13.3(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint-plugin-jsdoc: 50.6.17(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-jsonc: 2.20.1(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-n: 17.18.0(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-no-only-tests: 3.3.0 + eslint-plugin-perfectionist: 4.13.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint-plugin-pnpm: 0.3.1(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-regexp: 2.7.0(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-toml: 0.12.0(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-unicorn: 59.0.1(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-vue: 10.1.0(eslint@9.27.0(jiti@2.4.2))(vue-eslint-parser@10.1.3(eslint@9.27.0(jiti@2.4.2))) + eslint-plugin-yml: 1.18.0(eslint@9.27.0(jiti@2.4.2)) + eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.15)(eslint@9.27.0(jiti@2.4.2)) + globals: 16.2.0 + jsonc-eslint-parser: 2.4.0 + local-pkg: 1.1.1 + parse-gitignore: 2.0.0 + toml-eslint-parser: 0.10.0 + vue-eslint-parser: 10.1.3(eslint@9.27.0(jiti@2.4.2)) + yaml-eslint-parser: 1.3.0 + transitivePeerDependencies: + - '@eslint/json' + - '@vue/compiler-sfc' + - supports-color + - typescript + - vitest + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + optional: true + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.27.3': + dependencies: + '@babel/types': 7.27.3 + + '@babel/types@7.27.3': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bcoe/v8-coverage@1.0.2': {} + + '@clack/core@0.4.2': + dependencies: + picocolors: 1.1.1 + sisteransi: 1.0.5 + + '@clack/prompts@0.10.1': + dependencies: + '@clack/core': 0.4.2 + picocolors: 1.1.1 + sisteransi: 1.0.5 + + '@emnapi/core@1.4.3': + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@es-joy/jsdoccomment@0.50.2': + dependencies: + '@types/estree': 1.0.7 + '@typescript-eslint/types': 8.33.0 + comment-parser: 1.4.1 + esquery: 1.6.0 + jsdoc-type-pratt-parser: 4.1.0 + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.27.0(jiti@2.4.2))': + dependencies: + escape-string-regexp: 4.0.0 + eslint: 9.27.0(jiti@2.4.2) + ignore: 5.3.2 + + '@eslint-community/eslint-utils@4.7.0(eslint@9.27.0(jiti@2.4.2))': + dependencies: + eslint: 9.27.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.2.9(eslint@9.27.0(jiti@2.4.2))': + optionalDependencies: + eslint: 9.27.0(jiti@2.4.2) + + '@eslint/config-array@0.20.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.2': {} + + '@eslint/core@0.10.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.27.0': {} + + '@eslint/markdown@6.4.0': + dependencies: + '@eslint/core': 0.10.0 + '@eslint/plugin-kit': 0.2.8 + mdast-util-from-markdown: 2.0.2 + mdast-util-frontmatter: 2.0.1 + mdast-util-gfm: 3.1.0 + micromark-extension-frontmatter: 2.0.0 + micromark-extension-gfm: 3.0.0 + transitivePeerDependencies: + - supports-color + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@eslint/plugin-kit@0.3.1': + dependencies: + '@eslint/core': 0.14.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@napi-rs/wasm-runtime@0.2.10': + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.4': {} + + '@rollup/plugin-alias@5.1.1(rollup@4.41.1)': + optionalDependencies: + rollup: 4.41.1 + + '@rollup/plugin-commonjs@28.0.3(rollup@4.41.1)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.41.1) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.5(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.17 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.41.1 + + '@rollup/plugin-json@6.1.0(rollup@4.41.1)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.41.1) + optionalDependencies: + rollup: 4.41.1 + + '@rollup/plugin-node-resolve@16.0.1(rollup@4.41.1)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.41.1) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 + optionalDependencies: + rollup: 4.41.1 + + '@rollup/plugin-replace@6.0.2(rollup@4.41.1)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.41.1) + magic-string: 0.30.17 + optionalDependencies: + rollup: 4.41.1 + + '@rollup/pluginutils@5.1.4(rollup@4.41.1)': + dependencies: + '@types/estree': 1.0.7 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.41.1 + + '@rollup/rollup-android-arm-eabi@4.41.1': + optional: true + + '@rollup/rollup-android-arm64@4.41.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.41.1': + optional: true + + '@rollup/rollup-darwin-x64@4.41.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.41.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.41.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.41.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.41.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.41.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.41.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.41.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.41.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.41.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.41.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.41.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.41.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.41.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.41.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.41.1': + optional: true + + '@stylistic/eslint-plugin@4.4.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + estraverse: 5.3.0 + picomatch: 4.0.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@trysound/sax@0.2.0': {} + + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree@1.0.7': {} + + '@types/json-schema@7.0.15': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@22.15.23': + dependencies: + undici-types: 6.21.0 + + '@types/qs@6.14.0': {} + + '@types/resolve@1.20.2': {} + + '@types/unist@3.0.3': {} + + '@typescript-eslint/eslint-plugin@8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.33.0 + '@typescript-eslint/type-utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.33.0 + eslint: 9.27.0(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 7.0.4 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.33.0 + '@typescript-eslint/types': 8.33.0 + '@typescript-eslint/typescript-estree': 8.33.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.33.0 + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.33.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.33.0(typescript@5.8.3) + '@typescript-eslint/types': 8.33.0 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/scope-manager@8.33.0': + dependencies: + '@typescript-eslint/types': 8.33.0 + '@typescript-eslint/visitor-keys': 8.33.0 + + '@typescript-eslint/tsconfig-utils@8.33.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.33.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.33.0': {} + + '@typescript-eslint/typescript-estree@8.33.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.33.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.33.0(typescript@5.8.3) + '@typescript-eslint/types': 8.33.0 + '@typescript-eslint/visitor-keys': 8.33.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.33.0 + '@typescript-eslint/types': 8.33.0 + '@typescript-eslint/typescript-estree': 8.33.0(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.33.0': + dependencies: + '@typescript-eslint/types': 8.33.0 + eslint-visitor-keys: 4.2.0 + + '@unrs/resolver-binding-darwin-arm64@1.7.4': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.7.4': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.7.4': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.7.4': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.7.4': + dependencies: + '@napi-rs/wasm-runtime': 0.2.10 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.7.4': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.7.4': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.7.4': + optional: true + + '@vitest/coverage-v8@3.1.4(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.9.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + transitivePeerDependencies: + - supports-color + + '@vitest/eslint-plugin@1.2.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0))': + dependencies: + '@typescript-eslint/utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) + optionalDependencies: + typescript: 5.8.3 + vitest: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.1.4': + dependencies: + '@vitest/spy': 3.1.4 + '@vitest/utils': 3.1.4 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.1.4(vite@6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.1.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + + '@vitest/pretty-format@3.1.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.1.4': + dependencies: + '@vitest/utils': 3.1.4 + pathe: 2.0.3 + + '@vitest/snapshot@3.1.4': + dependencies: + '@vitest/pretty-format': 3.1.4 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.1.4': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@3.1.4': + dependencies: + '@vitest/pretty-format': 3.1.4 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + + '@vue/compiler-core@3.5.15': + dependencies: + '@babel/parser': 7.27.3 + '@vue/shared': 3.5.15 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.15': + dependencies: + '@vue/compiler-core': 3.5.15 + '@vue/shared': 3.5.15 + + '@vue/compiler-sfc@3.5.15': + dependencies: + '@babel/parser': 7.27.3 + '@vue/compiler-core': 3.5.15 + '@vue/compiler-dom': 3.5.15 + '@vue/compiler-ssr': 3.5.15 + '@vue/shared': 3.5.15 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.3 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.15': + dependencies: + '@vue/compiler-dom': 3.5.15 + '@vue/shared': 3.5.15 + + '@vue/shared@3.5.15': {} + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + ansis@4.0.0: {} + + are-docs-informative@0.0.2: {} + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + autoprefixer@10.4.21(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + caniuse-lite: 1.0.30001718 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + balanced-match@1.0.2: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.5: + dependencies: + caniuse-lite: 1.0.30001718 + electron-to-chromium: 1.5.159 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.5) + + builtin-modules@5.0.0: {} + + cac@6.7.14: {} + + callsites@3.1.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.24.5 + caniuse-lite: 1.0.30001718 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001718: {} + + ccount@2.0.1: {} + + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + character-entities@2.0.2: {} + + check-error@2.1.1: {} + + ci-info@4.2.0: {} + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + clean-regexp@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + commander@14.0.0: {} + + commander@7.2.0: {} + + comment-parser@1.4.1: {} + + commondir@1.0.1: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + consola@3.4.2: {} + + core-js-compat@3.42.0: + dependencies: + browserslist: 4.24.5 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-declaration-sorter@7.2.0(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@7.0.7(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + css-declaration-sorter: 7.2.0(postcss@8.5.3) + cssnano-utils: 5.0.1(postcss@8.5.3) + postcss: 8.5.3 + postcss-calc: 10.1.1(postcss@8.5.3) + postcss-colormin: 7.0.3(postcss@8.5.3) + postcss-convert-values: 7.0.5(postcss@8.5.3) + postcss-discard-comments: 7.0.4(postcss@8.5.3) + postcss-discard-duplicates: 7.0.2(postcss@8.5.3) + postcss-discard-empty: 7.0.1(postcss@8.5.3) + postcss-discard-overridden: 7.0.1(postcss@8.5.3) + postcss-merge-longhand: 7.0.5(postcss@8.5.3) + postcss-merge-rules: 7.0.5(postcss@8.5.3) + postcss-minify-font-values: 7.0.1(postcss@8.5.3) + postcss-minify-gradients: 7.0.1(postcss@8.5.3) + postcss-minify-params: 7.0.3(postcss@8.5.3) + postcss-minify-selectors: 7.0.5(postcss@8.5.3) + postcss-normalize-charset: 7.0.1(postcss@8.5.3) + postcss-normalize-display-values: 7.0.1(postcss@8.5.3) + postcss-normalize-positions: 7.0.1(postcss@8.5.3) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.3) + postcss-normalize-string: 7.0.1(postcss@8.5.3) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.3) + postcss-normalize-unicode: 7.0.3(postcss@8.5.3) + postcss-normalize-url: 7.0.1(postcss@8.5.3) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.3) + postcss-ordered-values: 7.0.2(postcss@8.5.3) + postcss-reduce-initial: 7.0.3(postcss@8.5.3) + postcss-reduce-transforms: 7.0.1(postcss@8.5.3) + postcss-svgo: 7.0.2(postcss@8.5.3) + postcss-unique-selectors: 7.0.4(postcss@8.5.3) + + cssnano-utils@5.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + cssnano@7.0.7(postcss@8.5.3): + dependencies: + cssnano-preset-default: 7.0.7(postcss@8.5.3) + lilconfig: 3.1.3 + postcss: 8.5.3 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.1.0: + dependencies: + character-entities: 2.0.2 + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + defu@6.1.4: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.159: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + entities@4.5.0: {} + + environment@1.1.0: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-compat-utils@0.5.1(eslint@9.27.0(jiti@2.4.2)): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + semver: 7.7.2 + + eslint-compat-utils@0.6.5(eslint@9.27.0(jiti@2.4.2)): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + semver: 7.7.2 + + eslint-config-flat-gitignore@2.1.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@eslint/compat': 1.2.9(eslint@9.27.0(jiti@2.4.2)) + eslint: 9.27.0(jiti@2.4.2) + + eslint-flat-config-utils@2.1.0: + dependencies: + pathe: 2.0.3 + + eslint-import-context@0.1.6(unrs-resolver@1.7.4): + dependencies: + get-tsconfig: 4.10.1 + stable-hash: 0.0.5 + optionalDependencies: + unrs-resolver: 1.7.4 + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + eslint-json-compat-utils@0.2.1(eslint@9.27.0(jiti@2.4.2))(jsonc-eslint-parser@2.4.0): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + esquery: 1.6.0 + jsonc-eslint-parser: 2.4.0 + + eslint-merge-processors@2.0.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + + eslint-plugin-antfu@3.1.1(eslint@9.27.0(jiti@2.4.2)): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + + eslint-plugin-command@3.2.1(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@es-joy/jsdoccomment': 0.50.2 + eslint: 9.27.0(jiti@2.4.2) + + eslint-plugin-es-x@7.8.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + eslint: 9.27.0(jiti@2.4.2) + eslint-compat-utils: 0.5.1(eslint@9.27.0(jiti@2.4.2)) + + eslint-plugin-import-x@4.13.3(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): + dependencies: + '@typescript-eslint/utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + comment-parser: 1.4.1 + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + eslint-import-context: 0.1.6(unrs-resolver@1.7.4) + eslint-import-resolver-node: 0.3.9 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + stable-hash: 0.0.5 + tslib: 2.8.1 + unrs-resolver: 1.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-jsdoc@50.6.17(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@es-joy/jsdoccomment': 0.50.2 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint: 9.27.0(jiti@2.4.2) + espree: 10.3.0 + esquery: 1.6.0 + parse-imports-exports: 0.2.4 + semver: 7.7.2 + spdx-expression-parse: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-jsonc@2.20.1(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + eslint: 9.27.0(jiti@2.4.2) + eslint-compat-utils: 0.6.5(eslint@9.27.0(jiti@2.4.2)) + eslint-json-compat-utils: 0.2.1(eslint@9.27.0(jiti@2.4.2))(jsonc-eslint-parser@2.4.0) + espree: 10.3.0 + graphemer: 1.4.0 + jsonc-eslint-parser: 2.4.0 + natural-compare: 1.4.0 + synckit: 0.11.6 + transitivePeerDependencies: + - '@eslint/json' + + eslint-plugin-n@17.18.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + enhanced-resolve: 5.18.1 + eslint: 9.27.0(jiti@2.4.2) + eslint-plugin-es-x: 7.8.0(eslint@9.27.0(jiti@2.4.2)) + get-tsconfig: 4.10.1 + globals: 15.15.0 + ignore: 5.3.2 + minimatch: 9.0.5 + semver: 7.7.2 + + eslint-plugin-no-only-tests@3.3.0: {} + + eslint-plugin-perfectionist@4.13.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): + dependencies: + '@typescript-eslint/types': 8.33.0 + '@typescript-eslint/utils': 8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) + natural-orderby: 5.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-pnpm@0.3.1(eslint@9.27.0(jiti@2.4.2)): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + find-up-simple: 1.0.1 + jsonc-eslint-parser: 2.4.0 + pathe: 2.0.3 + pnpm-workspace-yaml: 0.3.1 + tinyglobby: 0.2.14 + yaml-eslint-parser: 1.3.0 + + eslint-plugin-regexp@2.7.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + comment-parser: 1.4.1 + eslint: 9.27.0(jiti@2.4.2) + jsdoc-type-pratt-parser: 4.1.0 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + scslre: 0.3.0 + + eslint-plugin-toml@0.12.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + eslint-compat-utils: 0.6.5(eslint@9.27.0(jiti@2.4.2)) + lodash: 4.17.21 + toml-eslint-parser: 0.10.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-unicorn@59.0.1(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@eslint/plugin-kit': 0.2.8 + ci-info: 4.2.0 + clean-regexp: 1.0.0 + core-js-compat: 3.42.0 + eslint: 9.27.0(jiti@2.4.2) + esquery: 1.6.0 + find-up-simple: 1.0.1 + globals: 16.2.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + jsesc: 3.1.0 + pluralize: 8.0.0 + regexp-tree: 0.1.27 + regjsparser: 0.12.0 + semver: 7.7.2 + strip-indent: 4.0.0 + + eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)): + dependencies: + eslint: 9.27.0(jiti@2.4.2) + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + + eslint-plugin-vue@10.1.0(eslint@9.27.0(jiti@2.4.2))(vue-eslint-parser@10.1.3(eslint@9.27.0(jiti@2.4.2))): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + eslint: 9.27.0(jiti@2.4.2) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.7.2 + vue-eslint-parser: 10.1.3(eslint@9.27.0(jiti@2.4.2)) + xml-name-validator: 4.0.0 + + eslint-plugin-yml@1.18.0(eslint@9.27.0(jiti@2.4.2)): + dependencies: + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint: 9.27.0(jiti@2.4.2) + eslint-compat-utils: 0.6.5(eslint@9.27.0(jiti@2.4.2)) + natural-compare: 1.4.0 + yaml-eslint-parser: 1.3.0 + transitivePeerDependencies: + - supports-color + + eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.15)(eslint@9.27.0(jiti@2.4.2)): + dependencies: + '@vue/compiler-sfc': 3.5.15 + eslint: 9.27.0(jiti@2.4.2) + + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.27.0(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.0 + '@eslint/config-helpers': 0.2.2 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.27.0 + '@eslint/plugin-kit': 0.3.1 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.7 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + espree@9.6.1: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.7 + + esutils@2.0.3: {} + + eventemitter3@5.0.1: {} + + expect-type@1.2.1: {} + + exsolve@1.0.5: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fault@2.0.1: + dependencies: + format: 0.2.2 + + fdir@6.4.5(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up-simple@1.0.1: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.17 + mlly: 1.7.4 + rollup: 4.41.1 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + format@0.2.2: {} + + fraction.js@4.3.7: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-east-asian-width@1.3.0: {} + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + globals@14.0.0: {} + + globals@15.15.0: {} + + globals@16.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hookable@5.5.3: {} + + html-escaper@2.0.2: {} + + ignore@5.3.2: {} + + ignore@7.0.4: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + is-builtin-module@5.0.0: + dependencies: + builtin-modules: 5.0.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-module@1.0.0: {} + + is-number@7.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.7 + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} + + jiti@2.4.2: {} + + js-tokens@4.0.0: + optional: true + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdoc-type-pratt-parser@4.1.0: {} + + jsesc@3.0.2: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonc-eslint-parser@2.4.0: + dependencies: + acorn: 8.14.1 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.7.2 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + knitwork@1.2.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lint-staged@16.1.0: + dependencies: + chalk: 5.4.1 + commander: 14.0.0 + debug: 4.4.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + nano-spawn: 1.0.2 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.0 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + local-pkg@1.1.1: + dependencies: + mlly: 1.7.4 + pkg-types: 2.1.0 + quansync: 0.2.10 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + longest-streak@3.1.0: {} + + loupe@3.1.3: {} + + lru-cache@10.4.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.27.3 + '@babel/types': 7.27.3 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.1.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-frontmatter@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + escape-string-regexp: 5.0.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-extension-frontmatter: 2.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.1.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-frontmatter@2.0.0: + dependencies: + fault: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.1 + decode-named-character-reference: 1.1.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-function@5.0.1: {} + + min-indent@1.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minipass@7.1.2: {} + + mkdist@2.3.0(typescript@5.8.3): + dependencies: + autoprefixer: 10.4.21(postcss@8.5.3) + citty: 0.1.6 + cssnano: 7.0.7(postcss@8.5.3) + defu: 6.1.4 + esbuild: 0.25.5 + jiti: 1.21.7 + mlly: 1.7.4 + pathe: 2.0.3 + pkg-types: 2.1.0 + postcss: 8.5.3 + postcss-nested: 7.0.2(postcss@8.5.3) + semver: 7.7.2 + tinyglobby: 0.2.14 + optionalDependencies: + typescript: 5.8.3 + + mlly@1.7.4: + dependencies: + acorn: 8.14.1 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + ms@2.1.3: {} + + nano-spawn@1.0.2: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.2.4: {} + + natural-compare@1.4.0: {} + + natural-orderby@5.0.0: {} + + node-releases@2.0.19: {} + + normalize-range@0.1.2: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + package-manager-detector@1.3.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-gitignore@2.0.0: {} + + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-statements@1.0.11: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + pathe@2.0.3: {} + + pathval@2.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pidtree@0.6.0: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.1.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.5 + pathe: 2.0.3 + + pluralize@8.0.0: {} + + pnpm-workspace-yaml@0.3.1: + dependencies: + yaml: 2.8.0 + + postcss-calc@10.1.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + postcss-colormin@7.0.3(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-convert-values@7.0.5(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@7.0.4(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + + postcss-discard-duplicates@7.0.2(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-discard-empty@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-discard-overridden@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-merge-longhand@7.0.5(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.5(postcss@8.5.3) + + postcss-merge-rules@7.0.5(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.1(postcss@8.5.3) + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + + postcss-minify-font-values@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@7.0.1(postcss@8.5.3): + dependencies: + colord: 2.9.3 + cssnano-utils: 5.0.1(postcss@8.5.3) + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-minify-params@7.0.3(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + cssnano-utils: 5.0.1(postcss@8.5.3) + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@7.0.5(postcss@8.5.3): + dependencies: + cssesc: 3.0.0 + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + + postcss-nested@7.0.2(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + + postcss-normalize-charset@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-normalize-display-values@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@7.0.3(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@7.0.2(postcss@8.5.3): + dependencies: + cssnano-utils: 5.0.1(postcss@8.5.3) + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@7.0.3(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + caniuse-api: 3.0.0 + postcss: 8.5.3 + + postcss-reduce-transforms@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@7.0.2(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + svgo: 3.3.2 + + postcss-unique-selectors@7.0.4(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + pretty-bytes@6.1.1: {} + + punycode@2.3.1: {} + + quansync@0.2.10: {} + + queue-microtask@1.2.3: {} + + refa@0.12.1: + dependencies: + '@eslint-community/regexpp': 4.12.1 + + regexp-ast-analysis@0.7.1: + dependencies: + '@eslint-community/regexpp': 4.12.1 + refa: 0.12.1 + + regexp-tree@0.1.27: {} + + regjsparser@0.12.0: + dependencies: + jsesc: 3.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rollup-plugin-dts@6.2.1(rollup@4.41.1)(typescript@5.8.3): + dependencies: + magic-string: 0.30.17 + rollup: 4.41.1 + typescript: 5.8.3 + optionalDependencies: + '@babel/code-frame': 7.27.1 + + rollup@4.41.1: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.41.1 + '@rollup/rollup-android-arm64': 4.41.1 + '@rollup/rollup-darwin-arm64': 4.41.1 + '@rollup/rollup-darwin-x64': 4.41.1 + '@rollup/rollup-freebsd-arm64': 4.41.1 + '@rollup/rollup-freebsd-x64': 4.41.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.41.1 + '@rollup/rollup-linux-arm-musleabihf': 4.41.1 + '@rollup/rollup-linux-arm64-gnu': 4.41.1 + '@rollup/rollup-linux-arm64-musl': 4.41.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.41.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.41.1 + '@rollup/rollup-linux-riscv64-gnu': 4.41.1 + '@rollup/rollup-linux-riscv64-musl': 4.41.1 + '@rollup/rollup-linux-s390x-gnu': 4.41.1 + '@rollup/rollup-linux-x64-gnu': 4.41.1 + '@rollup/rollup-linux-x64-musl': 4.41.1 + '@rollup/rollup-win32-arm64-msvc': 4.41.1 + '@rollup/rollup-win32-ia32-msvc': 4.41.1 + '@rollup/rollup-win32-x64-msvc': 4.41.1 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + scslre@0.3.0: + dependencies: + '@eslint-community/regexpp': 4.12.1 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + + scule@1.3.0: {} + + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + simple-git-hooks@2.13.0: {} + + sisteransi@1.0.5: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + source-map-js@1.2.1: {} + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.21 + + spdx-license-ids@3.0.21: {} + + stable-hash@0.0.5: {} + + stackback@0.0.2: {} + + std-env@3.9.0: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-indent@4.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + stylehacks@7.0.5(postcss@8.5.3): + dependencies: + browserslist: 4.24.5 + postcss: 8.5.3 + postcss-selector-parser: 7.1.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.1.1 + + synckit@0.11.6: + dependencies: + '@pkgr/core': 0.2.4 + + tapable@2.2.2: {} + + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.0.1: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.0.2: {} + + tinyrainbow@2.0.0: {} + + tinyspy@3.0.2: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toml-eslint-parser@0.10.0: + dependencies: + eslint-visitor-keys: 3.4.3 + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@5.8.3: {} + + ufo@1.6.1: {} + + unbuild@3.5.0(typescript@5.8.3): + dependencies: + '@rollup/plugin-alias': 5.1.1(rollup@4.41.1) + '@rollup/plugin-commonjs': 28.0.3(rollup@4.41.1) + '@rollup/plugin-json': 6.1.0(rollup@4.41.1) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.41.1) + '@rollup/plugin-replace': 6.0.2(rollup@4.41.1) + '@rollup/pluginutils': 5.1.4(rollup@4.41.1) + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + esbuild: 0.25.5 + fix-dts-default-cjs-exports: 1.0.1 + hookable: 5.5.3 + jiti: 2.4.2 + magic-string: 0.30.17 + mkdist: 2.3.0(typescript@5.8.3) + mlly: 1.7.4 + pathe: 2.0.3 + pkg-types: 2.1.0 + pretty-bytes: 6.1.1 + rollup: 4.41.1 + rollup-plugin-dts: 6.2.1(rollup@4.41.1)(typescript@5.8.3) + scule: 1.3.0 + tinyglobby: 0.2.14 + untyped: 2.0.0 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - sass + - vue + - vue-sfc-transformer + - vue-tsc + + undici-types@6.21.0: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + unrs-resolver@1.7.4: + dependencies: + napi-postinstall: 0.2.4 + optionalDependencies: + '@unrs/resolver-binding-darwin-arm64': 1.7.4 + '@unrs/resolver-binding-darwin-x64': 1.7.4 + '@unrs/resolver-binding-freebsd-x64': 1.7.4 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.7.4 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.7.4 + '@unrs/resolver-binding-linux-arm64-gnu': 1.7.4 + '@unrs/resolver-binding-linux-arm64-musl': 1.7.4 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.7.4 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.7.4 + '@unrs/resolver-binding-linux-riscv64-musl': 1.7.4 + '@unrs/resolver-binding-linux-s390x-gnu': 1.7.4 + '@unrs/resolver-binding-linux-x64-gnu': 1.7.4 + '@unrs/resolver-binding-linux-x64-musl': 1.7.4 + '@unrs/resolver-binding-wasm32-wasi': 1.7.4 + '@unrs/resolver-binding-win32-arm64-msvc': 1.7.4 + '@unrs/resolver-binding-win32-ia32-msvc': 1.7.4 + '@unrs/resolver-binding-win32-x64-msvc': 1.7.4 + + untyped@2.0.0: + dependencies: + citty: 0.1.6 + defu: 6.1.4 + jiti: 2.4.2 + knitwork: 1.2.0 + scule: 1.3.0 + + update-browserslist-db@1.1.3(browserslist@4.24.5): + dependencies: + browserslist: 4.24.5 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vite-node@3.1.4(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0): + dependencies: + esbuild: 0.25.5 + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.41.1 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.15.23 + fsevents: 2.3.3 + jiti: 2.4.2 + yaml: 2.8.0 + + vitest-package-exports@0.1.1: + dependencies: + find-up-simple: 1.0.1 + pathe: 2.0.3 + + vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0): + dependencies: + '@vitest/expect': 3.1.4 + '@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0)) + '@vitest/pretty-format': 3.1.4 + '@vitest/runner': 3.1.4 + '@vitest/snapshot': 3.1.4 + '@vitest/spy': 3.1.4 + '@vitest/utils': 3.1.4 + chai: 5.2.0 + debug: 4.4.1 + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.3.5(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + vite-node: 3.1.4(@types/node@22.15.23)(jiti@2.4.2)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.15.23 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vue-eslint-parser@10.1.3(eslint@9.27.0(jiti@2.4.2)): + dependencies: + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + lodash: 4.17.21 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + xml-name-validator@4.0.0: {} + + yaml-eslint-parser@1.3.0: + dependencies: + eslint-visitor-keys: 3.4.3 + yaml: 2.8.0 + + yaml@2.8.0: {} + + yocto-queue@0.1.0: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..f7c887f --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,24 @@ +packages: + - playground + - docs + - packages/* + - examples/* +catalogs: + cli: + '@antfu/eslint-config': ^4.13.2 + eslint: ^9.24.0 + lint-staged: ^16.1.0 + simple-git-hooks: ^2.13.0 + typescript: ^5.8.3 + unbuild: ^3.5.0 + vite: ^6.3.5 + testing: + '@vitest/coverage-v8': ^3.1.4 + vitest: ^3.1.4 + vitest-package-exports: ^0.1.1 + types: + '@types/node': ^22.15.3 + '@types/qs': ^6.9.18 +onlyBuiltDependencies: + - esbuild + - simple-git-hooks diff --git a/src/core/client.ts b/src/core/client.ts new file mode 100644 index 0000000..b403969 --- /dev/null +++ b/src/core/client.ts @@ -0,0 +1,240 @@ +import type { ApiResponse, HttpClient, PlatformProvider, RequestOptions, XMoneyConfig, XMoneyCore } from '../types' +import { DateTransformer } from '../utils' +import { XMoneyError } from './error' + +/** + * Core client for interacting with the XMoney Payment Gateway API + * + * This client handles: + * - Authentication with API key + * - Request retry logic with exponential backoff + * - Form-encoded request bodies + * - Date transformation for API compatibility + * - Platform-specific HTTP and crypto operations + */ +export class XMoneyClient implements XMoneyCore { + /** + * Client configuration (read-only) + */ + readonly config: Readonly + /** + * Platform-specific HTTP client for making requests + */ + private readonly httpClient: HttpClient + /** + * Platform-specific provider for crypto operations + */ + private readonly platformProvider: PlatformProvider + + /** + * Create a new XMoney client instance + * @param config - Configuration object or API key string + * @throws {Error} If HTTP client or platform provider is not provided + * + * @example + * ```typescript + * // With API key only + * const client = new XMoneyClient('your-api-key') + * + * // With full configuration + * const client = new XMoneyClient({ + * apiKey: 'your-api-key', + * host: 'api.xmoney.com', + * maxRetries: 5 + * }) + * ``` + */ + constructor(config: XMoneyConfig | string) { + const defaultConfig: Partial = { + protocol: 'https', + host: 'api.xmoney.com', + timeout: 80000, + maxRetries: 3, + } + + if (typeof config === 'string') { + this.config = { ...defaultConfig, apiKey: config } + } + else { + let finalConfig = { ...defaultConfig } + + if (config.host && (config.host.startsWith('http://') || config.host.startsWith('https://'))) { + const url = new URL(config.host) + + // Apply config but use parsed URL components + finalConfig = { + ...defaultConfig, + ...config, + host: url.hostname, + protocol: config.protocol ?? url.protocol.replace(':', '') as 'http' | 'https', + port: config.port ?? (url.port ? Number(url.port) : undefined), + } + } + else { + // No URL parsing needed + finalConfig = { ...finalConfig, ...config } + } + + this.config = finalConfig as XMoneyConfig + } + + // HTTP client must be provided + if (!this.config.httpClient) { + throw new Error('HTTP client is required') + } + this.httpClient = this.config.httpClient + + // Platform provider must be provided + if (!this.config.platformProvider) { + throw new Error('Platform provider is required') + } + this.platformProvider = this.config.platformProvider + } + + /** + * Make an authenticated request to the XMoney API + * + * @template T - Expected response data type + * @param options - Request configuration + * @returns Promise resolving to the API response + * @throws {XMoneyError} If the request fails or returns an error response + * + * Features: + * - Automatic retry with exponential backoff for server errors (5xx) + * - Form-encoded request bodies for POST/PUT/PATCH/DELETE + * - Date serialization to API format + * - Query parameter handling with array support + */ + async request(options: RequestOptions): Promise> { + // Transform dates in query and body parameters + const transformedOptions = { + ...options, + query: options.query ? DateTransformer.toApi(options.query) : undefined, + body: options.body ? DateTransformer.toApi(options.body) : undefined, + } + + // Build path with query parameters + let path = transformedOptions.path.startsWith('/') ? transformedOptions.path : `/${transformedOptions.path}` + + // Add query parameters + if (transformedOptions.query) { + const params = new URLSearchParams() + Object.entries(transformedOptions.query).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + if (Array.isArray(value)) { + value.forEach(v => params.append(key, String(v))) + } + else { + params.append(key, String(value)) + } + } + }) + const queryString = params.toString() + if (queryString) { + path += `?${queryString}` + } + } + + const headers: Record = { + Authorization: `Bearer ${this.config.apiKey}`, + ...transformedOptions.headers, + } + + // Add secure token if provided (for card operations) + if (this.config.secureToken) { + headers['x-secure-token'] = this.config.secureToken + } + + let body: string | undefined + if (transformedOptions.body && (transformedOptions.method === 'POST' || transformedOptions.method === 'PUT' || transformedOptions.method === 'PATCH' || transformedOptions.method === 'DELETE')) { + // XMoney uses form-encoded data + headers['Content-Type'] = 'application/x-www-form-urlencoded' + body = this.encodeFormData(transformedOptions.body) + } + + let lastError: Error + const maxRetries = this.config.maxRetries || 3 + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + const response = await this.httpClient.request({ + method: transformedOptions.method, + protocol: this.config.protocol!, + host: this.config.host!, + port: this.config.port || (this.config.protocol === 'https' ? 443 : 80), + path, + headers, + body, + timeout: this.config.timeout!, + }) + + const data = await response.json() + + if (!response.ok) { + throw new XMoneyError(data.message || 'Request failed', { + statusCode: response.status, + code: data.code, + errors: data.error, + }) + } + + return data + } + catch (error) { + lastError = error as Error + + // Don't retry on client errors (4xx) + if (error instanceof XMoneyError && error.details.statusCode && error.details.statusCode < 500) { + throw error + } + + // Retry with exponential backoff + if (attempt < maxRetries) { + const delay = Math.min(1000 * 2 ** (attempt - 1), 10000) + await new Promise(resolve => setTimeout(resolve, delay)) + } + } + } + + // eslint-disable-next-line no-throw-literal + throw lastError! + } + + /** + * Encode data as application/x-www-form-urlencoded + * + * Supports nested objects and arrays using bracket notation: + * - Objects: `parent[child]=value` + * - Arrays: `parent[]=value1&parent[]=value2` + * + * @param data - Data to encode + * @returns Form-encoded string + */ + private encodeFormData(data: Record): string { + // Note: URLSearchParams is available in Node.js 10+ and all modern browsers + // For older environments, a polyfill may be needed + const params = new URLSearchParams() + + const encode = (obj: any, prefix: string = ''): void => { + Object.entries(obj).forEach(([key, value]) => { + const fullKey = prefix ? `${prefix}[${key}]` : key + + if (value === null || value === undefined) + return + + if (Array.isArray(value)) { + value.forEach(v => params.append(`${fullKey}[]`, String(v))) + } + else if (typeof value === 'object') { + encode(value, fullKey) + } + else { + params.append(fullKey, String(value)) + } + }) + } + + encode(data) + return params.toString() + } +} diff --git a/src/core/error.ts b/src/core/error.ts new file mode 100644 index 0000000..5e8a6e9 --- /dev/null +++ b/src/core/error.ts @@ -0,0 +1,82 @@ +import type { ApiError } from '../types' + +/** + * Custom error class for XMoney API errors + * + * Provides detailed error information including: + * - HTTP status code + * - API error code + * - Validation errors with field-level details + * + * @example + * ```typescript + * try { + * await xMoney.customers.create({ email: 'invalid' }) + * } catch (error) { + * if (error instanceof XMoneyError) { + * console.log('Status:', error.details.statusCode) + * if (error.isValidationError) { + * console.log('Validation errors:', error.validationErrors) + * } + * } + * } + * ``` + */ +export class XMoneyError extends Error { + /** + * Create a new XMoneyError + * @param message - Error message + * @param details - Additional error details from the API response + * @param details.statusCode - HTTP status code of the failed request + * @param details.code - API-specific error code + * @param details.errors - Array of detailed error information + */ + constructor( + message: string, + public readonly details: { + /** + * HTTP status code of the failed request + */ + statusCode?: number + /** + * API-specific error code + */ + code?: number + /** + * Array of detailed error information + */ + errors?: ApiError[] + } = {}, + ) { + super(message) + this.name = 'XMoneyError' + Error.captureStackTrace(this, XMoneyError) + } + + /** + * Check if this error contains validation errors + * @returns True if any validation errors are present + */ + get isValidationError(): boolean { + return this.details.errors?.some(e => e.type === 'Validation') ?? false + } + + /** + * Get validation errors as a field-to-message mapping + * @returns Object with field names as keys and error messages as values + * + * @example + * ```typescript + * // Returns: { email: 'Invalid email format', name: 'Name is required' } + * ``` + */ + get validationErrors(): Record { + const errors: Record = {} + this.details.errors?.forEach((error) => { + if (error.type === 'Validation' && error.field) { + errors[error.field] = error.message + } + }) + return errors + } +} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..7a04db0 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,3 @@ +export * from './client' +export * from './error' +export * from './pagination' diff --git a/src/core/pagination.ts b/src/core/pagination.ts new file mode 100644 index 0000000..8943407 --- /dev/null +++ b/src/core/pagination.ts @@ -0,0 +1,264 @@ +import type { ApiResponse, Pagination } from '../types' + +/** + * Paginated list with automatic pagination support + * + * Implements both synchronous iteration (current page only) and + * asynchronous iteration (all pages). Memory-efficient for large datasets. + * + * @template T - Type of items in the list + * + * @example + * ```typescript + * // Get a paginated list + * const customers = await xMoney.customers.list({ perPage: 20 }) + * + * // Iterate current page only + * for (const customer of customers) { + * console.log(customer.email) + * } + * + * // Iterate all pages automatically + * for await (const customer of customers) { + * console.log(customer.email) + * } + * + * // Get all items as array (loads all pages) + * const allCustomers = await customers.toArrayAll() + * ``` + */ +export class PaginatedList implements AsyncIterable { + constructor( + public readonly data: T[], + public readonly pagination?: Pagination, + private searchParams?: Record, + private fetchNextPage?: (page: number) => Promise>, + ) {} + + /** + * Check if there are more pages available + */ + get hasMore(): boolean { + if (!this.pagination) + return false + return this.pagination.currentPageNumber < this.pagination.pageCount + } + + /** + * Total number of items across all pages + */ + get totalCount(): number { + return this.pagination?.totalItemCount ?? this.data.length + } + + /** + * Current page number (1-indexed) + */ + get currentPage(): number { + return this.pagination?.currentPageNumber ?? 1 + } + + /** + * Total number of pages + */ + get totalPages(): number { + return this.pagination?.pageCount ?? 1 + } + + /** + * Number of items per page + */ + get perPage(): number { + return this.pagination?.itemCountPerPage ?? this.data.length + } + + /** + * Synchronous iterator for current page only + * @returns Iterator for items on the current page + */ + [Symbol.iterator](): Iterator { + return this.data[Symbol.iterator]() + } + + /** + * Asynchronous iterator that automatically fetches all pages + * @yields Items from all pages in sequence + */ + async* [Symbol.asyncIterator](): AsyncIterator { + // First yield all items from current page + for (const item of this.data) { + yield item + } + + // If there are more pages and we have a way to fetch them + if (this.hasMore && this.fetchNextPage) { + let nextPage = this.currentPage + 1 + + while (nextPage <= this.totalPages) { + const response = await this.fetchNextPage(nextPage) + + if (response.data) { + for (const item of response.data) { + yield item + } + } + + // Check if we should continue + if (!response.pagination || nextPage >= response.pagination.pageCount) { + break + } + + nextPage++ + } + } + } + + /** + * Get current page items as array + * @returns Array of items from current page only + */ + toArray(): T[] { + return [...this.data] + } + + /** + * Fetch all pages and return as a single array + * + * Warning: This loads all pages into memory at once. + * Use async iteration for large datasets. + * + * @returns Array containing all items from all pages + */ + async toArrayAll(): Promise { + const allItems: T[] = [] + + for await (const item of this) { + allItems.push(item) + } + + return allItems + } + + /** + * Get a specific page by number + * @param pageNumber - Page number to fetch (1-indexed) + * @returns PaginatedList for the requested page, or null if invalid + */ + async getPage(pageNumber: number): Promise | null> { + if (!this.fetchNextPage || pageNumber < 1) { + return null + } + + if (pageNumber === this.currentPage) { + return this + } + + const response = await this.fetchNextPage(pageNumber) + + if (!response.data) { + return null + } + + return new PaginatedList( + response.data, + response.pagination, + response.searchParams, + this.fetchNextPage, + ) + } + + /** + * Take first N items across all pages + * @param count - Number of items to take + * @returns Array of up to count items + */ + async take(count: number): Promise { + const items: T[] = [] + + for await (const item of this) { + items.push(item) + if (items.length >= count) + break + } + + return items + } + + /** + * Find first item matching predicate across all pages + * @param predicate - Function to test each item + * @returns First matching item or undefined + */ + async find(predicate: (item: T) => boolean): Promise { + for await (const item of this) { + if (predicate(item)) + return item + } + return undefined + } + + /** + * Filter items across all pages + * @param predicate - Function to test each item + * @returns Array of items that match the predicate + */ + async filter(predicate: (item: T) => boolean): Promise { + const items: T[] = [] + + for await (const item of this) { + if (predicate(item)) + items.push(item) + } + + return items + } + + /** + * Map items across all pages to a new type + * @template U - Type to map to + * @param mapper - Function to transform each item + * @returns Array of mapped items + */ + async map(mapper: (item: T) => U): Promise { + const items: U[] = [] + + for await (const item of this) { + items.push(mapper(item)) + } + + return items + } +} + +/** + * Search result container for deferred search execution + * + * Holds a search ID that can be used to fetch results later + * + * @template T - Type of items in search results + * + * @example + * ```typescript + * const search = await xMoney.customers.search({ + * country: 'US', + * createdAtFrom: new Date('2024-01-01') + * }) + * + * // Fetch results when needed + * const results = await search.fetch() + * ``` + */ +export class SearchResult { + constructor( + public readonly searchId: string, + private fetchFn: () => Promise>, + ) {} + + /** + * Fetch search results + * @returns Paginated list of search results + */ + async fetch(): Promise> { + return this.fetchFn() + } +} diff --git a/src/factory.ts b/src/factory.ts new file mode 100644 index 0000000..a217295 --- /dev/null +++ b/src/factory.ts @@ -0,0 +1,78 @@ +import type { ApiResponse, RequestOptions, XMoneyConfig } from './types' +import { XMoneyClient } from './core' +import { CardsResource, CheckoutResource, CustomersResource, NotificationsResource, OrdersResource, TransactionsResource } from './resources' + +/** + * Main SDK interface providing access to all XMoney resources + */ +export interface XMoneySDK { + /** + * Customer management operations + */ + customers: CustomersResource + /** + * Order management operations + */ + orders: OrdersResource + /** + * Transaction management operations + */ + transactions: TransactionsResource + /** + * Card management operations + */ + cards: CardsResource + /** + * Notification webhook management + */ + notifications: NotificationsResource + /** + * Checkout payment request operations + */ + checkout: CheckoutResource + /** + * Direct API request method for custom endpoints + * @param options - Request configuration + * @returns API response + */ + request: (options: RequestOptions) => Promise> +} + +/** + * Internal factory function for creating XMoney SDK instances + * Used by platform-specific entry points (Node.js and Web) + * + * @param config - Configuration object or API key string + * @returns XMoney SDK instance with all resources + * @throws {Error} If httpClient or platformProvider is not provided + * + * @internal + */ +export function createXMoneyClientFactory(config: XMoneyConfig | string): XMoneySDK { + const finalConfig = typeof config === 'string' + ? { apiKey: config } + : config + + // Extract httpClient and platformProvider from config + const httpClient = finalConfig.httpClient + const platformProvider = finalConfig.platformProvider + + if (!httpClient) { + throw new Error('httpClient is required') + } + if (!platformProvider) { + throw new Error('platformProvider is required') + } + + const client = new XMoneyClient({ ...finalConfig, httpClient, platformProvider }) + + return { + customers: new CustomersResource(client), + orders: new OrdersResource(client), + transactions: new TransactionsResource(client), + cards: new CardsResource(client), + notifications: new NotificationsResource(client), + checkout: new CheckoutResource(client, platformProvider), + request: client.request.bind(client), + } +} diff --git a/src/http/fetch-client.ts b/src/http/fetch-client.ts new file mode 100644 index 0000000..bbaea8f --- /dev/null +++ b/src/http/fetch-client.ts @@ -0,0 +1,50 @@ +import type { HttpClient, HttpRequestOptions, HttpResponse } from './types' + +export class FetchHttpClient implements HttpClient { + constructor(private fetchFn: typeof fetch = globalThis.fetch) { + if (!this.fetchFn) { + throw new Error('Fetch is not available. Please provide a fetch implementation or use NodeHttpClient.') + } + } + + async request(options: HttpRequestOptions): Promise { + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), options.timeout) + + try { + const url = this.buildUrl(options) + const response = await this.fetchFn(url, { + method: options.method, + headers: options.headers, + body: options.body, + signal: controller.signal, + }) + + return { + ok: response.ok, + status: response.status, + statusText: response.statusText, + headers: this.parseHeaders(response.headers), + json: () => response.json(), + text: () => response.text(), + } + } + finally { + clearTimeout(timeoutId) + } + } + + private buildUrl(options: HttpRequestOptions): string { + const { protocol, host, port, path } = options + const portString = port && port !== (protocol === 'https' ? 443 : 80) ? `:${port}` : '' + return `${protocol}://${host}${portString}${path}` + } + + private parseHeaders(headers: Headers): Record { + const result: Record = {} + headers.forEach((value, key) => { + result[key.toLowerCase()] = value + }) + return result + } +} diff --git a/src/http/index.ts b/src/http/index.ts new file mode 100644 index 0000000..1b9bfb8 --- /dev/null +++ b/src/http/index.ts @@ -0,0 +1,3 @@ +export { FetchHttpClient } from './fetch-client' +export { NodeHttpClient } from './node-client' +export type * from './types' diff --git a/src/http/node-client.ts b/src/http/node-client.ts new file mode 100644 index 0000000..1f060cf --- /dev/null +++ b/src/http/node-client.ts @@ -0,0 +1,58 @@ +import type { RequestOptions } from 'node:https' +import type { HttpClient, HttpRequestOptions, HttpResponse } from './types' +import * as http_ from 'node:http' +import * as https_ from 'node:https' + +// Preserve compatibility with HTTP interception tools like MSW (Mock Service Worker) +// by accessing the CommonJS module instead of the immutable ES module namespace. +// This allows testing libraries to mock network requests at runtime. +const http = ((http_ as unknown) as { default: typeof http_ }).default || http_ +const https = ((https_ as unknown) as { default: typeof https_ }).default || https_ + +export class NodeHttpClient implements HttpClient { + async request(options: HttpRequestOptions): Promise { + return new Promise((resolve, reject) => { + const isHttps = options.protocol === 'https' + + const requestOptions: RequestOptions = { + hostname: options.host, + port: options.port || (isHttps ? 443 : 80), + path: options.path, + method: options.method, + headers: options.headers, + timeout: options.timeout, + } + + const req = (isHttps ? https : http).request(requestOptions, (res) => { + let data = '' + + res.on('data', (chunk) => { + data += chunk + }) + + res.on('end', () => { + resolve({ + ok: res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300, + status: res.statusCode || 0, + statusText: res.statusMessage || '', + headers: res.headers as Record, + json: async () => JSON.parse(data), + text: async () => data, + }) + }) + }) + + req.on('error', reject) + req.on('timeout', () => { + req.destroy() + reject(new Error('Request timeout')) + }) + + if (options.body) { + req.write(options.body) + } + + req.end() + }) + } +} diff --git a/src/http/types.ts b/src/http/types.ts new file mode 100644 index 0000000..0127e3e --- /dev/null +++ b/src/http/types.ts @@ -0,0 +1,27 @@ +// https://www.rfc-editor.org/rfc/rfc7231#section-4.1 +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'CONNECT' | 'TRACE' +export type HttpProtocol = 'http' | 'https' + +export interface HttpClient { + request: (options: HttpRequestOptions) => Promise +} + +export interface HttpRequestOptions { + host: string + port: string | number + path: string + method: HttpMethod + headers: Record + body?: string + protocol: HttpProtocol + timeout: number +} + +export interface HttpResponse { + ok: boolean + status: number + statusText: string + headers: Record + json: () => Promise + text: () => Promise +} diff --git a/src/index.node.ts b/src/index.node.ts new file mode 100644 index 0000000..858e1a9 --- /dev/null +++ b/src/index.node.ts @@ -0,0 +1,51 @@ +import type { XMoneySDK } from './factory' +import type { XMoneyConfig } from './types' +import { createXMoneyClientFactory } from './factory' +import { NodeHttpClient } from './http' +import { nodePlatformProvider } from './platform/node' + +/** + * Create an XMoney SDK client for Node.js environments + * + * Automatically configures Node.js-specific HTTP and crypto implementations + * + * @param config - Configuration object or API key string + * @returns XMoney SDK instance + * + * @example + * ```typescript + * import { createXMoneyClient } from '@xmoney/api-sdk' + * + * // Simple usage with API key only + * const xMoney = createXMoneyClient('your-api-key') + * + * // Advanced configuration + * const xMoney = createXMoneyClient({ + * apiKey: 'your-api-key', + * secureToken: 'your-secure-token', // For card operations + * host: 'api.xmoney.com', + * timeout: 30000, + * maxRetries: 5 + * }) + * + * // Use the SDK + * const customer = await xMoney.customers.create({ + * identifier: 'CUST-123', + * email: 'customer@example.com' + * }) + * ``` + */ +export function createXMoneyClient(config: XMoneyConfig | string): XMoneySDK { + const finalConfig = typeof config === 'string' + ? { apiKey: config, httpClient: new NodeHttpClient(), platformProvider: nodePlatformProvider } + : { + ...config, + httpClient: config.httpClient || new NodeHttpClient(), + platformProvider: config.platformProvider || nodePlatformProvider, + } + + return createXMoneyClientFactory(finalConfig) +} + +export { NodeHttpClient } from './http/node-client' +export * from './types' diff --git a/src/index.web.ts b/src/index.web.ts new file mode 100644 index 0000000..9c7c6ee --- /dev/null +++ b/src/index.web.ts @@ -0,0 +1,52 @@ +import type { XMoneySDK } from './factory' +import type { XMoneyConfig } from './types' +import { createXMoneyClientFactory } from './factory' +import { FetchHttpClient } from './http' +import { webPlatformProvider } from './platform/web' + +/** + * Create an XMoney SDK client for web browser environments + * + * Automatically configures browser-specific Fetch API and Web Crypto implementations + * + * @param config - Configuration object or API key string + * @returns XMoney SDK instance + * + * @example + * ```typescript + * import { createXMoneyClient } from '@xmoney/api-sdk' + * + * // Simple usage with API key only + * const xMoney = createXMoneyClient('your-api-key') + * + * // Advanced configuration + * const xMoney = createXMoneyClient({ + * apiKey: 'your-api-key', + * host: 'api.xmoney.com', + * timeout: 30000, + * maxRetries: 3 + * }) + * + * // Note: For PCI compliance, use hosted checkout for card payments + * const checkout = xMoney.checkout.create({ + * publicKey: 'pk_test_your-site-id', + * backUrl: window.location.origin + '/payment/complete', + * customer: { identifier: 'CUST-123', email: 'customer@example.com' }, + * order: { orderId: 'ORDER-123', type: 'purchase', amount: 99.99, currency: 'USD', description: 'Purchase' } + * }) + * ``` + */ +export function createXMoneyClient(config: XMoneyConfig | string): XMoneySDK { + const finalConfig = typeof config === 'string' + ? { apiKey: config, httpClient: new FetchHttpClient(), platformProvider: webPlatformProvider } + : { + ...config, + httpClient: config.httpClient || new FetchHttpClient(), + platformProvider: config.platformProvider || webPlatformProvider, + } + + return createXMoneyClientFactory(finalConfig) +} + +export { FetchHttpClient } from './http/fetch-client' +export * from './types' diff --git a/src/main.ts b/src/main.ts deleted file mode 100644 index 530e8a6..0000000 --- a/src/main.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - InitInputDto, - OrderInputDto, - OrderOutputDto, - xMoneyOrderDecryptResponseDto, -} from './typings/dtos'; -import { OrderService } from './services/order.service'; - -export default class xMoney { - private orderService: OrderService; - - constructor(initParams: InitInputDto) { - this.orderService = new OrderService( - initParams.secretKey, - ); - } - - public initializeCheckout(input: OrderInputDto): OrderOutputDto { - return this.orderService.createOrder(input); - } - - public initializeHostedCheckout(input: OrderInputDto): string { - return this.orderService.createOrderWithHtml(input); - } - - public decryptOrderResponse(input: string): xMoneyOrderDecryptResponseDto { - return this.orderService.decryptOrderResponse(input); - } -} diff --git a/src/platform/node.ts b/src/platform/node.ts new file mode 100644 index 0000000..3426294 --- /dev/null +++ b/src/platform/node.ts @@ -0,0 +1,59 @@ +import type { DecipherInterface, HmacInterface, PlatformBuffer, PlatformCrypto, PlatformProvider } from './types' +import { Buffer } from 'node:buffer' +import crypto from 'node:crypto' + +export class NodeCrypto implements PlatformCrypto { + private crypto: typeof import('crypto') + + constructor() { + // Dynamic import to avoid issues in browser environments + this.crypto = crypto + } + + createHmacSha512(key: string): HmacInterface { + const hmac = this.crypto.createHmac('sha512', key) + return { + update: (data: string) => { hmac.update(data) }, + digest: () => Buffer.from(hmac.digest()), + } + } + + createDecipherAes256Cbc(key: string, iv: Uint8Array): DecipherInterface { + const decipher = this.crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv)) + return { + update: (data: Uint8Array) => Buffer.from(decipher.update(Buffer.from(data))), + final: () => Buffer.from(decipher.final()), + } + } +} + +export class NodeBuffer implements PlatformBuffer { + from(input: string | ArrayBuffer | Uint8Array, encoding?: 'base64' | 'utf8'): Uint8Array { + if (typeof input === 'string') { + return Buffer.from(input, encoding) + } + return Buffer.from(input as ArrayLike) + } + + alloc(size: number, fill?: string): Uint8Array { + return fill ? Buffer.alloc(size, fill) : Buffer.alloc(size) + } + + byteLength(string: string): number { + return Buffer.byteLength(string) + } + + concat(arrays: Uint8Array[]): Uint8Array { + return Buffer.concat(arrays.map(arr => Buffer.from(arr))) + } + + toString(buffer: Uint8Array, encoding: 'base64' | 'utf8'): string { + return Buffer.from(buffer).toString(encoding) + } +} + +// Export the platform provider +export const nodePlatformProvider: PlatformProvider = { + crypto: new NodeCrypto(), + buffer: new NodeBuffer(), +} diff --git a/src/platform/types.ts b/src/platform/types.ts new file mode 100644 index 0000000..6627d68 --- /dev/null +++ b/src/platform/types.ts @@ -0,0 +1,27 @@ +export interface PlatformCrypto { + createHmacSha512: (key: string) => HmacInterface + createDecipherAes256Cbc: (key: string, iv: Uint8Array) => DecipherInterface +} + +export interface HmacInterface { + update: (data: string) => void + digest: () => Uint8Array +} + +export interface DecipherInterface { + update: (data: Uint8Array) => Uint8Array + final: () => Uint8Array +} + +export interface PlatformBuffer { + from: ((input: string, encoding: 'base64' | 'utf8') => Uint8Array) & ((input: ArrayBuffer | Uint8Array) => Uint8Array) + alloc: (size: number, fill?: string) => Uint8Array + byteLength: (string: string) => number + concat: (arrays: Uint8Array[]) => Uint8Array + toString: (buffer: Uint8Array, encoding: 'base64' | 'utf8') => string +} + +export interface PlatformProvider { + crypto: PlatformCrypto + buffer: PlatformBuffer +} diff --git a/src/platform/web.ts b/src/platform/web.ts new file mode 100644 index 0000000..0ce7169 --- /dev/null +++ b/src/platform/web.ts @@ -0,0 +1,193 @@ +import type { DecipherInterface, HmacInterface, PlatformBuffer, PlatformCrypto, PlatformProvider } from './types' + +/** + * Web implementation using Web Crypto API + */ +export class WebCrypto implements PlatformCrypto { + createHmacSha512(key: string): HmacInterface { + const encoder = new TextEncoder() + const _keyData = encoder.encode(key) + let _cryptoKey: any + const data: Uint8Array[] = [] + + return { + update: (input: string) => { + data.push(encoder.encode(input)) + }, + digest: () => { + // This is synchronous for simplicity, but in real usage should be async + const _combined = this.concatArrays(data) + + throw new Error('Web Crypto HMAC requires async implementation. Use WebCryptoAsync instead.') + }, + } + } + + createDecipherAes256Cbc(_key: string, _iv: Uint8Array): DecipherInterface { + throw new Error('Web Crypto decryption requires async implementation. Use WebCryptoAsync instead.') + } + + private concatArrays(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0) + const result = new Uint8Array(totalLength) + let offset = 0 + for (const arr of arrays) { + result.set(arr, offset) + offset += arr.length + } + return result + } +} + +/** + * Async Web Crypto implementation (recommended for browser) + */ +export class WebCryptoAsync { + async createHmacSha512(key: string): Promise { + const encoder = new TextEncoder() + const keyData = encoder.encode(key) + + const cryptoKey = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'HMAC', hash: 'SHA-512' }, + false, + ['sign'], + ) + + const data: Uint8Array[] = [] + + return { + update: (input: string) => { + data.push(encoder.encode(input)) + }, + digest: async () => { + const combined = concatUint8Arrays(data) + const signature = await crypto.subtle.sign('HMAC', cryptoKey, combined) + return new Uint8Array(signature) + }, + } + } + + async createDecipherAes256Cbc(key: string, iv: Uint8Array): Promise { + const encoder = new TextEncoder() + const keyData = encoder.encode(key).slice(0, 32) // Ensure 256-bit key + + const cryptoKey = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'AES-CBC' }, + false, + ['decrypt'], + ) + + const chunks: Uint8Array[] = [] + + return { + update: (data: Uint8Array) => { + chunks.push(data) + return new Uint8Array(0) // Return empty for compatibility + }, + final: async () => { + const combined = concatUint8Arrays(chunks) + const decrypted = await crypto.subtle.decrypt( + { name: 'AES-CBC', iv }, + cryptoKey, + combined, + ) + return new Uint8Array(decrypted) + }, + } + } +} + +export interface HmacAsyncInterface { + update: (data: string) => void + digest: () => Promise +} + +export interface DecipherAsyncInterface { + update: (data: Uint8Array) => Uint8Array + final: () => Promise +} + +export class WebBuffer implements PlatformBuffer { + from(input: string | ArrayBuffer | Uint8Array, encoding?: 'base64' | 'utf8'): Uint8Array { + if (typeof input === 'string') { + if (encoding === 'base64') { + return this.base64ToUint8Array(input) + } + return new TextEncoder().encode(input) + } + + if (input instanceof ArrayBuffer) { + return new Uint8Array(input) + } + + return new Uint8Array(input) + } + + alloc(size: number, fill?: string): Uint8Array { + const buffer = new Uint8Array(size) + if (fill) { + const encoder = new TextEncoder() + const fillBytes = encoder.encode(fill) + for (let i = 0; i < size; i++) { + buffer[i] = fillBytes[i % fillBytes.length] + } + } + return buffer + } + + byteLength(string: string): number { + return new TextEncoder().encode(string).length + } + + concat(arrays: Uint8Array[]): Uint8Array { + return concatUint8Arrays(arrays) + } + + toString(buffer: Uint8Array, encoding: 'base64' | 'utf8'): string { + if (encoding === 'base64') { + return this.uint8ArrayToBase64(buffer) + } + return new TextDecoder().decode(buffer) + } + + private base64ToUint8Array(base64: string): Uint8Array { + const binaryString = atob(base64) + const bytes = new Uint8Array(binaryString.length) + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i) + } + return bytes + } + + private uint8ArrayToBase64(bytes: Uint8Array): string { + let binary = '' + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]) + } + return btoa(binary) + } +} + +/** + * Helper function to concatenate Uint8Arrays + */ +function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0) + const result = new Uint8Array(totalLength) + let offset = 0 + for (const arr of arrays) { + result.set(arr, offset) + offset += arr.length + } + return result +} + +// Export the platform provider +export const webPlatformProvider: PlatformProvider = { + crypto: new WebCrypto(), + buffer: new WebBuffer(), +} diff --git a/src/resources/cards.ts b/src/resources/cards.ts new file mode 100644 index 0000000..7ae7261 --- /dev/null +++ b/src/resources/cards.ts @@ -0,0 +1,226 @@ +import type { ApiResponse, XMoneyCore } from '../types' +import { PaginatedList, XMoneyError } from '../core' + +/** + * Saved card information + */ +export interface Card { + /** + * Card ID + */ + id: number + /** + * Customer ID who owns the card + */ + customerId: number + /** + * Card type (visa, mastercard, etc.) + */ + type: string + /** + * Masked card number + */ + cardNumber: string + /** + * Expiry month (MM) + */ + expiryMonth: string + /** + * Expiry year (YYYY) + */ + expiryYear: string + /** + * Name on card + */ + nameOnCard?: string + /** + * Cardholder country (ISO 3166-1 alpha-2) + */ + cardHolderCountry?: string + /** + * Cardholder state + */ + cardHolderState?: string + /** + * Card payment provider + */ + cardProvider: string + /** + * Whether card has a token for processing + */ + hasToken?: boolean + /** + * Card status (active, deleted, etc.) + */ + cardStatus: string + /** + * Bank identification number information + */ + binInfo?: BinInfo +} + +/** + * Bank Identification Number (BIN) information + */ +export interface BinInfo { + /** + * First 6 digits of card number + */ + bin: string + /** + * Card brand (Visa, Mastercard, etc.) + */ + brand?: string + /** + * Card type (debit, credit, prepaid) + */ + type?: string + /** + * Card level (classic, gold, platinum, etc.) + */ + level?: string + /** + * Issuing country code + */ + countryCode?: string + /** + * Issuing bank name + */ + bank?: string +} + +/** + * Parameters for listing saved cards + */ +export interface CardListParams { + /** + * Search ID from previous search + */ + searchId?: string + /** + * Filter by customer ID + */ + customerId?: number + /** + * Filter by order ID + */ + orderId?: number + /** + * Filter by token availability + */ + hasToken?: 'yes' | 'no' + /** + * Filter by card status + * - all: Include all cards + * - deleted: Only deleted cards + */ + cardStatus?: 'all' | 'deleted' + /** + * Page number + */ + page?: number + /** + * Items per page + */ + perPage?: number + /** + * Reverse sort order + */ + reverseSorting?: 0 | 1 +} + +/** + * Resource for managing saved payment cards + * + * Note: Secure token required for card operations + * + * @example + * ```typescript + * const xMoney = createXMoneyClient({ + * apiKey: 'your-api-key', + * secureToken: 'your-secure-token' + * }) + * + * // Retrieve a saved card + * const card = await xMoney.cards.retrieve(cardId, customerId) + * console.log(card.cardNumber) // 411111******1111 + * ``` + */ +export class CardsResource { + constructor(private client: XMoneyCore) {} + + /** + * Retrieve a saved card + * @param id - Card ID + * @param customerId - Customer ID (required for security) + * @returns Card information + * @throws {XMoneyError} If card not found + */ + async retrieve(id: number, customerId: number): Promise { + const response = await this.client.request({ + method: 'GET', + path: `/card/${id}`, + query: { customerId }, + }) + + if (!response.data) { + throw new XMoneyError('Card not found') + } + + return response.data + } + + /** + * Delete a saved card + * @param id - Card ID + * @throws {XMoneyError} If deletion fails + */ + async delete(id: number): Promise { + await this.client.request({ + method: 'DELETE', + path: `/card/${id}`, + }) + } + + /** + * List saved cards with filtering + * @param params - List parameters + * @returns Paginated list of cards + * + * @example + * ```typescript + * // List all active cards for a customer + * const cards = await xMoney.cards.list({ + * customerId: 123, + * hasToken: 'yes' + * }) + * + * for await (const card of cards) { + * console.log(card.type, card.expiryMonth + '/' + card.expiryYear) + * } + * ``` + */ + async list(params?: CardListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/card', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/card', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } +} diff --git a/src/resources/checkout.ts b/src/resources/checkout.ts new file mode 100644 index 0000000..03c067f --- /dev/null +++ b/src/resources/checkout.ts @@ -0,0 +1,373 @@ +import type { PlatformProvider } from '../platform/types' +import type { XMoneyCore } from '../types' +import { XMoneyError } from '../core' + +/** + * Payment request data for hosted checkout + */ +export interface PaymentRequest { + /** + * Site ID extracted from public key + */ + siteId: string + /** + * Card transaction mode + * - auth: Authorization only + * - authAndCapture: Authorize and capture immediately + * - credit: Credit/refund transaction + */ + cardTransactionMode: 'auth' | 'authAndCapture' | 'credit' + /** + * Email for invoice delivery + */ + invoiceEmail?: string + /** + * Save card for future use + */ + saveCard?: boolean + /** + * URL to redirect after payment completion + */ + backUrl: string + /** + * Custom data from order + */ + customData?: string + /** + * Customer information + */ + customer: { + /** + * External customer identifier + */ + identifier: string + /** + * Customer first name + */ + firstName?: string + /** + * Customer last name + */ + lastName?: string + /** + * Country code (ISO 3166-1 alpha-2) + */ + country?: string + /** + * City name + */ + city?: string + /** + * Phone number + */ + phone?: string + /** + * Email address + */ + email?: string + /** + * Customer tags + */ + tags?: string[] + } + /** + * Order information + */ + order: { + /** + * External order ID + */ + orderId: string + /** + * Order type + */ + type: 'purchase' | 'recurring' | 'managed' | 'credit' + /** + * Order amount + */ + amount: number + /** + * Currency code (ISO 4217) + */ + currency: string + /** + * Order description + */ + description: string + } +} + +/** + * Parameters for creating a checkout session + */ +export interface CheckoutCreateParams extends Omit { + /** + * Public key in format: pk__ + * Environment can be 'test' or 'live' + */ + publicKey: string +} + +/** + * Parameters for creating a checkout form + */ +export interface CheckoutCreateFormParams extends CheckoutCreateParams { + /** + * Checkout URL (default: https://secure.twispay.com) + */ + url?: string | null +} + +/** + * Response from checkout creation + */ +export interface CheckoutCreateResponse { + /** + * Base64-encoded payment request payload + */ + payload: string + /** + * HMAC-SHA512 checksum for request validation + */ + checksum: string +} + +/** + * Transaction status from checkout response + */ +type XMoneyTransactionStatus = 'complete-ok' | 'complete-failed' | 'in-progress' | 'refund-ok' + +/** + * Decrypted response from checkout completion + */ +export interface CheckoutResponse { + /** + * Transaction status + */ + transactionStatus: XMoneyTransactionStatus + /** + * Internal order ID + */ + orderId: number + /** + * External order ID provided in request + */ + externalOrderId: string + /** + * Transaction ID + */ + transactionId: number + /** + * Payment method used + */ + transactionMethod: string + /** + * Customer ID + */ + customerId: number + /** + * Customer identifier + */ + identifier: string + /** + * Transaction amount + */ + amount: number + /** + * Currency code + */ + currency: string + /** + * Custom data from order + */ + customData: string | null + /** + * Custom fields from order + */ + customFields: { [key: string]: string } | null + /** + * Unix timestamp + */ + timestamp: number + /** + * Saved card ID if card was saved + */ + cardId: number | undefined + /** + * Error details if transaction failed + */ + errors?: { + /** + * Error code + */ + code: number + /** + * Error message + */ + message: string + /** + * Error type + */ + type: string + }[] +} + +/** + * Resource for managing hosted checkout sessions + * + * Provides secure payment form generation and response decryption + * for PCI-compliant payment processing + * + * @example + * ```typescript + * const xMoney = createXMoneyClient({ apiKey: 'your-api-key' }) + * + * // Create checkout session + * const { payload, checksum } = xMoney.checkout.create({ + * publicKey: 'pk_test_your-site-id', + * backUrl: 'https://example.com/payment/complete', + * customer: { + * identifier: 'CUST-123', + * email: 'customer@example.com' + * }, + * order: { + * orderId: 'ORDER-123', + * type: 'purchase', + * amount: 99.99, + * currency: 'USD', + * description: 'Product purchase' + * }, + * cardTransactionMode: 'authAndCapture' + * }) + * ``` + */ +export class CheckoutResource { + constructor( + private readonly client: XMoneyCore, + private readonly platform: PlatformProvider, + ) {} + + /** + * Create a checkout session + * @param params - Checkout parameters + * @returns Payload and checksum for form submission + * @throws {XMoneyError} If public key is invalid + */ + create(params: CheckoutCreateParams): CheckoutCreateResponse { + if (!params.publicKey) { + throw new XMoneyError('Public key is required for hosted checkout') + } + + const publicKeyMatch = params.publicKey.match(/^pk_(test|live)_(.+)$/) + if (!publicKeyMatch) { + throw new XMoneyError('Invalid public key format. Expected: pk__') + } + + const siteId = publicKeyMatch[2] + + const data: PaymentRequest = { + siteId, + saveCard: params.saveCard ?? false, + ...params, + } + + const jsonText = JSON.stringify(data) + const uint8Array = this.platform.buffer.from(jsonText, 'utf8') + const payload = this.platform.buffer.toString(uint8Array, 'base64') + const checksum = this.generateChecksum(data) + + return { payload, checksum } + } + + /** + * Generate an HTML form for checkout + * @param params - Checkout parameters + * @returns HTML form with auto-submit script + * + * @example + * ```typescript + * const formHtml = xMoney.checkout.form({ + * publicKey: 'pk_test_your-site-id', + * backUrl: 'https://example.com/complete', + * // ... other params + * }) + * + * // Render formHtml in your page + * ``` + */ + form(params: CheckoutCreateFormParams): string { + const { payload, checksum } = this.create(params) + + const checkoutUrl = params.url ?? 'https://secure.twispay.com' + + return ` +
+ + + +
+` + } + + /** + * Decrypt the response from checkout completion + * @param encryptedResponse - Encrypted response string (format: "iv,encrypted_data") + * @returns Decrypted checkout response + * @throws {XMoneyError} If decryption fails + * + * @example + * ```typescript + * // In your return URL handler + * const encryptedResponse = request.body.opensslResult + * const response = xMoney.checkout.decrypt(encryptedResponse) + * + * if (response.transactionStatus === 'complete-ok') { + * // Payment successful + * } + * ``` + */ + decrypt(encryptedResponse: string): CheckoutResponse { + const [ivBase64, encryptedBase64] = encryptedResponse.split(',', 2) + + if (!ivBase64 || !encryptedBase64) { + throw new XMoneyError('Invalid encrypted response format') + } + + const iv = this.platform.buffer.from(ivBase64, 'base64') + const encryptedData = this.platform.buffer.from(encryptedBase64, 'base64') + + const decipher = this.platform.crypto.createDecipherAes256Cbc( + this.client.config.apiKey, + iv, + ) + + const decrypted = this.platform.buffer.concat([ + decipher.update(encryptedData), + decipher.final(), + ]).toString() + + try { + return JSON.parse(decrypted) as CheckoutResponse + } + catch { + throw new XMoneyError('Failed to parse decrypted response' /** { cause: error } */) + } + } + + /** + * Generate HMAC-SHA512 checksum for request validation + * @param data - Data to checksum + * @returns Base64-encoded checksum + * @private + */ + private generateChecksum(data: any): string { + const hmac = this.platform.crypto.createHmacSha512(this.client.config.apiKey) + hmac.update(JSON.stringify(data)) + const digest = hmac.digest() + return this.platform.buffer.toString(digest, 'base64') + } +} diff --git a/src/resources/customers.ts b/src/resources/customers.ts new file mode 100644 index 0000000..9b0641b --- /dev/null +++ b/src/resources/customers.ts @@ -0,0 +1,385 @@ +import type { ApiResponse, XMoneyCore } from '../types' +import type { Tag } from './types' +import { PaginatedList, SearchResult, XMoneyError } from '../core' + +/** + * Customer information stored in XMoney + */ +export interface Customer { + /** + * Unique customer ID + */ + id: number + /** + * Site ID the customer belongs to + */ + siteId?: number + /** + * External customer identifier + */ + identifier: string + /** + * Customer's first name + */ + firstName: string + /** + * Customer's last name + */ + lastName: string + /** + * Country code (ISO 3166-1 alpha-2) + */ + country: string + /** + * State or province + */ + state?: string + /** + * City name + */ + city: string + /** + * Postal/ZIP code + */ + zipCode: string + /** + * Street address + */ + address: string + /** + * Phone number + */ + phone: string + /** + * Email address + */ + email: string + /** + * Whitelist status (0 = not whitelisted, 1 = whitelisted) + */ + isWhitelisted?: number + /** + * ISO 8601 date-time until when the customer is whitelisted + */ + isWhitelistedUntil?: string + /** + * ISO 8601 date-time when the customer was created + */ + creationDate: string + /** + * Unix timestamp of customer creation + */ + creationTimestamp: number + /** + * Customer tags for categorization + */ + tags?: Tag[] +} + +/** + * Parameters for creating a new customer + */ +export interface CustomerCreateParams { + /** + * External customer identifier (required) + */ + identifier: string + /** + * Customer email address (required) + */ + email: string + /** + * Site ID to associate the customer with + */ + siteId?: number + /** + * Customer's first name + */ + firstName?: string + /** + * Customer's last name + */ + lastName?: string + /** + * Country code (ISO 3166-1 alpha-2) + */ + country?: string + /** + * State or province + */ + state?: string + /** + * City name + */ + city?: string + /** + * Postal/ZIP code + */ + zipCode?: string + /** + * Street address + */ + address?: string + /** + * Phone number + */ + phone?: string + /** + * Set to 1 to whitelist the customer + */ + isWhitelisted?: 0 | 1 + /** + * ISO 8601 date until when to whitelist the customer + */ + isWhitelistedUntil?: string + /** + * Tags to assign to the customer + */ + tag?: string[] +} + +/** + * Parameters for updating an existing customer + * All fields are optional except siteId which cannot be changed + */ +export interface CustomerUpdateParams extends Partial> {} + +/** + * Parameters for listing and filtering customers + */ +export interface CustomerListParams { + /** + * Search ID from a previous search operation + */ + searchId?: string + /** + * Filter by customer identifier + */ + identifier?: string + /** + * Enable partial matching for identifier (1 = partial, 0 = exact) + */ + identifierMatchPartial?: 0 | 1 + /** + * Filter by parent resource type + */ + parentResourceType?: 'partner' | 'merchant' | 'site' + /** + * Filter by parent resource IDs + */ + parentResourceId?: number[] + /** + * Filter by email address + */ + email?: string + /** + * Filter by whitelist status + */ + isWhitelisted?: 0 | 1 + /** + * Filter by whitelist expiry date (from) + */ + isWhitelistedUntilFrom?: string + /** + * Filter by whitelist expiry date (to) + */ + isWhitelistedUntilTo?: string + /** + * Filter by tag + */ + tag?: string + /** + * Filter by country code + */ + country?: string + /** + * Filter by state + */ + state?: string + /** + * Filter by creation date (from) + */ + createdAtFrom?: Date + /** + * Filter by creation date (to) + */ + createdAtTo?: Date + /** + * Page number for pagination + */ + page?: number + /** + * Number of items per page + */ + perPage?: number + /** + * Reverse sort order (1 = reverse, 0 = normal) + */ + reverseSorting?: 0 | 1 +} + +/** + * Resource for managing customers in XMoney + * + * @example + * ```typescript + * const xMoney = createXMoneyClient({ apiKey: 'your-api-key' }) + * + * // Create a customer + * const { id } = await xMoney.customers.create({ + * identifier: 'CUST-123', + * email: 'john.doe@example.com', + * firstName: 'John', + * lastName: 'Doe' + * }) + * + * // List customers with pagination + * const customers = await xMoney.customers.list({ perPage: 20 }) + * for await (const customer of customers) { + * console.log(customer.email) + * } + * ``` + */ +export class CustomersResource { + constructor(private client: XMoneyCore) {} + + /** + * Create a new customer + * @param params - Customer creation parameters + * @returns Object containing the new customer ID + * @throws {XMoneyError} If creation fails + */ + async create(params: CustomerCreateParams): Promise<{ id: number }> { + const response = await this.client.request<{ id: number }>({ + method: 'POST', + path: '/customer', + body: params, + }) + + if (!response.data) { + throw new XMoneyError('Failed to create customer') + } + + return response.data + } + + /** + * Retrieve a customer by ID + * @param id - Customer ID + * @returns Customer information + * @throws {XMoneyError} If customer not found + */ + async retrieve(id: number): Promise { + const response = await this.client.request({ + method: 'GET', + path: `/customer/${id}`, + }) + + if (!response.data) { + throw new XMoneyError('Customer not found') + } + + return response.data + } + + /** + * Update an existing customer + * @param id - Customer ID + * @param params - Fields to update + * @throws {XMoneyError} If update fails + */ + async update(id: number, params: CustomerUpdateParams): Promise { + await this.client.request({ + method: 'PUT', + path: `/customer/${id}`, + body: params, + }) + } + + /** + * Delete a customer + * @param id - Customer ID + * @throws {XMoneyError} If deletion fails + */ + async delete(id: number): Promise { + await this.client.request({ + method: 'DELETE', + path: `/customer/${id}`, + }) + } + + /** + * List customers with optional filtering and pagination + * @param params - List parameters + * @returns Paginated list of customers + * + * @example + * ```typescript + * // List all customers + * const customers = await xMoney.customers.list() + * + * // Filter by email and iterate through pages + * const filtered = await xMoney.customers.list({ + * email: 'john.doe@example.com', + * perPage: 50 + * }) + * for await (const customer of filtered) { + * console.log(customer) + * } + * ``` + */ + async list(params?: CustomerListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/customer', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/customer', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } + + /** + * Search for customers with advanced filtering + * + * Creates a search query that can be used to retrieve results + * @param params - Search parameters (excluding searchId and page) + * @returns SearchResult that can be used to fetch results + * @throws {XMoneyError} If search fails + * + * @example + * ```typescript + * const search = await xMoney.customers.search({ + * country: 'US', + * createdAtFrom: new Date('2024-01-01') + * }) + * const results = await search.fetch() + * ``` + */ + async search(params: Omit): Promise> { + const response = await this.client.request<{ searchId: string, url?: string }>({ + method: 'POST', + path: '/customer-search', + body: params, + }) + + if (!response.data?.searchId) { + throw new XMoneyError('Search failed') + } + + return new SearchResult(response.data.searchId, () => this.list({ searchId: response.data!.searchId })) + } +} diff --git a/src/resources/index.ts b/src/resources/index.ts new file mode 100644 index 0000000..30855bc --- /dev/null +++ b/src/resources/index.ts @@ -0,0 +1,7 @@ +export * from './cards' +export * from './checkout' +export * from './customers' +export * from './notifications' +export * from './orders' +export * from './transactions' +export * from './types' diff --git a/src/resources/notifications.ts b/src/resources/notifications.ts new file mode 100644 index 0000000..954e543 --- /dev/null +++ b/src/resources/notifications.ts @@ -0,0 +1,264 @@ +import type { ApiResponse, XMoneyCore } from '../types' +import { PaginatedList } from '../core' + +/** + * Notification message types for webhook events + */ +export type NotificationMessage = + | 'orderNew' + | 'orderChange' + | 'orderExtend' + | 'orderRetrying' + | 'orderInProgress' + | 'orderCancel' + | 'transactionNew' + | 'transactionCapture' + | 'transactionFail' + | 'transactionRefund' + | 'transactionCancel' + | 'transactionVoid' + | 'transactionChargeBack' + | 'transactionCredit' + | 'transaction3D' + | 'receiptSend' + +/** + * Webhook notification record + */ +export interface Notification { + /** + * Notification ID + */ + id: number + /** + * Site ID + */ + siteId: number + /** + * ID of the resource (order or transaction) + */ + resourceId: number + /** + * Type of resource + */ + resourceType: 'order' | 'transaction' + /** + * Notification message type + */ + message: NotificationMessage + /** + * ISO 8601 date-time when notification was created + */ + creationDate: string + /** + * Unix timestamp of creation + */ + creationTimestamp: number +} + +/** + * Parameters for listing notifications + */ +export interface NotificationListParams { + /** + * Search ID from previous search + */ + searchId?: string + /** + * Filter by parent resource type + */ + parentResourceType?: 'partner' | 'merchant' | 'site' + /** + * Filter by parent resource IDs + */ + parentResourceId?: number[] + /** + * Filter notifications with ID greater than + */ + greaterThanId?: number + /** + * Filter by resource ID + */ + resourceId?: number + /** + * Filter by resource type + */ + resourceType?: 'order' | 'transaction' + /** + * Filter by notification message types + */ + message?: NotificationMessage[] + /** + * Filter by occurrence date from + */ + occurredAtFrom?: Date + /** + * Filter by occurrence date to + */ + occurredAtTo?: Date + /** + * Page number + */ + page?: number + /** + * Items per page + */ + perPage?: number +} + +/** + * Parameters for listing order-specific notifications + */ +export interface OrderNotificationListParams { + parentResourceType?: 'partner' | 'merchant' | 'site' + parentResourceId?: number[] + greaterThanId?: number + message?: Array<'orderNew' | 'orderChange' | 'orderExtend' | 'orderRetrying' | 'orderInProgress' | 'orderCancel'> + occurredAtFrom?: Date + occurredAtTo?: Date + page?: number + perPage?: number +} + +/** + * Parameters for listing transaction-specific notifications + */ +export interface TransactionNotificationListParams { + parentResourceType?: 'partner' | 'merchant' | 'site' + parentResourceId?: number[] + greaterThanId?: number + message?: Array<'transactionNew' | 'transactionCapture' | 'transactionFail' | 'transactionRefund' | 'transactionCancel' | 'transactionVoid' | 'transactionChargeBack' | 'transaction3D' | 'transactionCredit' | 'receiptSend'> + occurredAtFrom?: Date + occurredAtTo?: Date + page?: number + perPage?: number +} + +/** + * Resource for managing webhook notifications + * + * Use to retrieve notification history for orders and transactions + * + * @example + * ```typescript + * const xMoney = createXMoneyClient({ apiKey: 'your-api-key' }) + * + * // List all notifications + * const notifications = await xMoney.notifications.list({ + * greaterThanId: lastProcessedId + * }) + * + * // Process new notifications + * for await (const notification of notifications) { + * if (notification.message === 'transactionNew') { + * // Handle new transaction + * } + * } + * ``` + */ +export class NotificationsResource { + constructor(private client: XMoneyCore) {} + + /** + * List all notifications + * @param params - List parameters + * @returns Paginated list of notifications + */ + async list(params?: NotificationListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/notification', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/notification', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } + + /** + * List notifications for orders only + * @param params - List parameters + * @returns Paginated list of order notifications + * + * @example + * ```typescript + * const orderNotifications = await xMoney.notifications.listForOrders({ + * message: ['orderNew', 'orderChange'], + * occurredAtFrom: '2024-01-01T00:00:00Z' + * }) + * ``` + */ + async listForOrders(params?: OrderNotificationListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/notification-for-order', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/notification-for-order', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } + + /** + * List notifications for transactions only + * @param params - List parameters + * @returns Paginated list of transaction notifications + * + * @example + * ```typescript + * const txNotifications = await xMoney.notifications.listForTransactions({ + * message: ['transactionNew', 'transactionFail'], + * greaterThanId: lastId + * }) + * ``` + */ + async listForTransactions(params?: TransactionNotificationListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/notification-for-transaction', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/notification-for-transaction', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } +} diff --git a/src/resources/orders.ts b/src/resources/orders.ts new file mode 100644 index 0000000..ff953c3 --- /dev/null +++ b/src/resources/orders.ts @@ -0,0 +1,643 @@ +import type { ApiResponse, RefundReason, XMoneyCore } from '../types' +import type { CardType, Tag, WalletType } from './types' +import { PaginatedList, SearchResult, XMoneyError } from '../core' + +/** + * Order information in XMoney + */ +export interface Order { + /** + * Unique order ID + */ + id: number + /** + * Site ID the order belongs to + */ + siteId?: number + /** + * Customer ID associated with the order + */ + customerId: number + /** + * External reference ID for the order + */ + externalOrderId?: string + /** + * Type of order + * - purchase: One-time payment + * - recurring: Subscription payment + * - managed: Managed recurring payment + * - credit: Credit/refund transaction + */ + orderType: 'purchase' | 'recurring' | 'managed' | 'credit' + /** + * Current order status + * - start: Order initiated + * - in-progress: Processing payment + * - retrying: Retrying failed payment + * - expiring: Subscription expiring + * - complete-ok: Successfully completed + * - complete-failed: Failed to complete + */ + orderStatus: 'start' | 'in-progress' | 'retrying' | 'expiring' | 'complete-ok' | 'complete-failed' + /** + * Order amount + */ + amount?: number + /** + * Currency code (ISO 4217) + */ + currency?: string + /** + * Order description + */ + description?: string + /** + * Email for invoice delivery + */ + invoiceEmail?: string + /** + * ISO 8601 date-time when order was created + */ + createdAt?: string + /** + * Recurring interval type + */ + intervalType?: 'day' | 'month' + /** + * Recurring interval value + */ + intervalValue?: number + /** + * Retry intervals for failed payments (comma-separated ISO 8601 durations) + */ + retryPayment?: string + /** + * ISO 8601 date-time for next payment due + */ + nextDueDate?: string + /** + * Payment method used + */ + transactionMethod?: 'card' | 'wallet' | string + /** + * Order tags for categorization + */ + tags?: Tag[] +} + +/** + * Parameters for creating a new order + */ +export interface OrderCreateParams { + /** + * Site ID for the order + */ + siteId?: number + /** + * Customer ID (required) + */ + customerId: number + /** + * Customer IP address (required) + */ + ip: string + /** + * Order amount (required) + */ + amount: number + /** + * Currency code ISO 4217 (required) + */ + currency: string + /** + * External order reference ID + */ + externalOrderId?: string + /** + * Force transaction (1 = force, 0 = normal) + */ + force?: 0 | 1 + /** + * Order description + */ + description?: string + /** + * Level 3 processing data (JSON) + */ + level3Data?: string + /** + * Email for invoice delivery + */ + invoiceEmail?: string + /** + * Tags to assign to the order + */ + tag?: string[] + /** + * Reference to another order ID + */ + referenceOrderId?: number + /** + * Order type (required) + */ + orderType: 'purchase' | 'recurring' | 'managed' | 'credit' + /** + * Recurring interval type + */ + intervalType?: 'day' | 'month' + /** + * Recurring interval value + */ + intervalValue?: number + /** + * Retry payment schedule (ISO 8601 durations) + */ + retryPayment?: string + /** + * Trial period amount + */ + trialAmount?: number + /** + * First billing date for recurring orders + */ + firstBillDate?: string + /** + * URL to redirect after payment + */ + backUrl?: string + /** + * Custom data to attach to order + */ + externalCustomData?: string + /** + * Payment method type + */ + transactionMethod?: 'card' | 'wallet' + /** + * Card transaction mode + * - auth: Authorization only + * - authAndCapture: Authorize and capture + * - credit: Credit/refund + */ + cardTransactionMode?: 'auth' | 'authAndCapture' | 'credit' + /** + * Existing card ID to use + */ + cardId?: string + /** + * Cardholder name + */ + cardHolderName?: string + /** + * Cardholder country (ISO 3166-1 alpha-2) + */ + cardHolderCountry?: string + /** + * Cardholder state + */ + cardHolderState?: string + /** + * Card type + */ + cardType?: CardType + /** + * Card number (PAN) + */ + cardNumber?: string + /** + * Card expiry date (MM/YY or MM/YYYY) + */ + cardExpiryDate?: string + /** + * Card security code (CVV/CVC) + */ + cardCvv?: string + /** + * Statement descriptor + */ + cardDescriptor?: string + /** + * 3D Secure authentication data + */ + threeDSecureData?: string + /** + * Save card for future use + */ + saveCard?: boolean + /** + * Wallet transaction mode + */ + walletTransactionMode?: 'transfer' | 'credit' + /** + * Wallet type + */ + wallet?: WalletType + /** + * Extra wallet parameters (JSON) + */ + walletExtraParams?: string + /** + * Transaction options (JSON) + */ + transactionOption?: string +} + +/** + * Response from order creation + */ +export interface CreateOrderResponse { + /** + * Created order ID + */ + orderId: number + /** + * Created transaction ID + */ + transactionId: number + /** + * Saved card ID if card was saved + */ + cardId?: number + /** + * Whether 3D Secure is required (1 = yes, 0 = no) + */ + is3d?: 0 | 1 + /** + * Whether redirect is required + */ + isRedirect?: boolean + /** + * Redirect information if required + */ + redirect?: { + /** + * URL to redirect to + */ + url: string + /** + * HTTP method for redirect + */ + formMethod?: 'POST' | 'GET' + /** + * Parameters to include in redirect + */ + params?: Record + } +} + +/** + * Parameters for listing and filtering orders + */ +export interface OrderListParams { + /** + * Search ID from previous search + */ + searchId?: string + /** + * Filter by parent resource type + */ + parentResourceType?: 'partner' | 'merchant' | 'site' + /** + * Filter by parent resource IDs + */ + parentResourceId?: number[] + /** + * Filter by external order ID + */ + externalOrderId?: string + /** + * Filter by customer ID + */ + customerId?: number + /** + * Filter by order type + */ + orderType?: 'purchase' | 'recurring' + /** + * Filter by order status + */ + orderStatus?: 'start' | 'in-progress' | 'retrying' | 'expiring' | 'complete-ok' | 'complete-failed' + /** + * Filter by creation date (from) + */ + createdAtFrom?: Date + /** + * Filter by creation date (to) + */ + createdAtTo?: Date + /** + * Filter by refund reason + */ + reason?: RefundReason + /** + * Filter by tag + */ + tag?: string + /** + * Page number + */ + page?: number + /** + * Items per page + */ + perPage?: number + /** + * Reverse sort order (1 = reverse) + */ + reverseSorting?: 0 | 1 +} + +/** + * Parameters for rebilling an order + */ +export interface OrderRebillParams { + /** + * Customer ID for the rebill + */ + customerId: number + /** + * Rebill amount + */ + amount: number + /** + * Transaction options (JSON) + * Validate against https://api-stage.xmoney.com/schema/transactionOption.schema.json + */ + transactionOption?: string +} + +/** + * Parameters for updating card on an order + */ +export interface OrderUpdateCardParams { + /** + * Customer ID + */ + customerId: string + /** + * Customer IP address + */ + ip: string + /** + * Transaction amount + */ + amount: number + /** + * Currency code (ISO 4217) + */ + currency: string + /** + * Transaction description + */ + transactionDescription?: string + /** + * Cardholder name + */ + cardHolderName?: string + /** + * Cardholder country (ISO 3166-1 alpha-2) + */ + cardHolderCountry?: string + /** + * Cardholder state + */ + cardHolderState?: string + /** + * Card type + */ + cardType?: CardType + /** + * Card number (required) + */ + cardNumber: string + /** + * Card expiry date (required, MM/YY or MM/YYYY) + */ + cardExpiryDate: string + /** + * Card security code (required) + */ + cardCvv: string + /** + * Statement descriptor + */ + cardDescriptor?: string +} + +/** + * Parameters for canceling an order + */ +export interface OrderCancelParams { + /** + * Cancellation reason + */ + reason?: 'fraud-confirm' | 'highly-suspicious' | 'duplicated-transaction' | 'customer-demand' | 'test-transaction' + /** + * Additional cancellation message + */ + message?: string + /** + * Set to 'yes' to terminate recurring orders + */ + terminateOrder?: 'yes' +} + +/** + * Resource for managing orders in XMoney + * + * @example + * ```typescript + * const xMoney = createXMoneyClient({ apiKey: 'your-api-key' }) + * + * // Create a one-time purchase + * const order = await xMoney.orders.create({ + * customerId: 123, + * ip: '192.168.1.1', + * amount: 99.99, + * currency: 'USD', + * orderType: 'purchase', + * transactionMethod: 'card', + * cardNumber: '4111111111111111', + * cardExpiryDate: '12/25', + * cardCvv: '123' + * }) + * + * // Handle 3D Secure redirect if needed + * if (order.isRedirect && order.redirect) { + * // Redirect user to order.redirect.url + * } + * ``` + */ +export class OrdersResource { + constructor(private client: XMoneyCore) {} + + /** + * Create a new order + * @param params - Order creation parameters + * @returns Order creation response with IDs and redirect info + * @throws {XMoneyError} If creation fails (402 for payment failure) + */ + async create(params: OrderCreateParams): Promise { + const response = await this.client.request({ + method: 'POST', + path: '/order', + body: params, + }) + + if (!response.data) { + throw new XMoneyError('Failed to create order', { + statusCode: response.code === 402 ? 402 : undefined, + }) + } + + return response.data + } + + /** + * Retrieve an order by ID + * @param id - Order ID or external order ID + * @returns Order information + * @throws {XMoneyError} If order not found + */ + async retrieve(id: string | number): Promise { + const response = await this.client.request({ + method: 'GET', + path: `/order/${id}`, + }) + + if (!response.data) { + throw new XMoneyError('Order not found') + } + + return response.data + } + + /** + * Cancel an order + * @param id - Order ID or external order ID + * @param params - Cancellation parameters + * @throws {XMoneyError} If cancellation fails + */ + async cancel(id: string | number, params?: OrderCancelParams): Promise { + await this.client.request({ + method: 'DELETE', + path: `/order/${id}`, + body: params, + }) + } + + /** + * Rebill an existing order + * @param id - Order ID or external order ID + * @param params - Rebill parameters + * @returns New order and transaction IDs + * @throws {XMoneyError} If rebill fails + */ + async rebill(id: string | number, params: OrderRebillParams): Promise<{ id: number, transactionId?: number, cardId?: number }> { + const response = await this.client.request<{ id: number, transactionId?: number, cardId?: number }>({ + method: 'PATCH', + path: `/order-rebill/${id}`, + body: params, + }) + + if (!response.data) { + throw new XMoneyError('Failed to rebill order') + } + + return response.data + } + + /** + * Update payment card for an order + * @param orderId - Order ID or external order ID + * @param params - New card information + * @returns Updated order and card IDs + * @throws {XMoneyError} If update fails + */ + async updateCard(orderId: string | number, params: OrderUpdateCardParams): Promise<{ id: number, transactionId?: number, cardId?: number }> { + const response = await this.client.request<{ id: number, transactionId?: number, cardId?: number }>({ + method: 'PATCH', + path: `/order-update-card/${orderId}`, + body: params, + }) + + if (!response.data) { + throw new XMoneyError('Failed to update card') + } + + return response.data + } + + /** + * List orders with optional filtering + * @param params - List parameters + * @returns Paginated list of orders + * + * @example + * ```typescript + * // List all orders for a customer + * const orders = await xMoney.orders.list({ + * customerId: 123, + * orderStatus: 'complete-ok' + * }) + * + * // Iterate through pages + * for await (const order of orders) { + * console.log(order.amount, order.currency) + * } + * ``` + */ + async list(params?: OrderListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/order', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/order', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } + + /** + * Search orders with advanced filtering + * @param params - Search parameters + * @returns Search result for fetching orders + * @throws {XMoneyError} If search fails + * + * @example + * ```typescript + * const search = await xMoney.orders.search({ + * createdAtFrom: new Date('2024-01-01'), + * orderType: 'recurring' + * }) + * const results = await search.fetch() + * ``` + */ + async search(params: Omit): Promise> { + const response = await this.client.request<{ searchId: string, url?: string }>({ + method: 'POST', + path: '/order-search', + body: params, + }) + + if (!response.data?.searchId) { + throw new XMoneyError('Search failed') + } + + return new SearchResult(response.data.searchId, () => this.list({ searchId: response.data!.searchId })) + } +} diff --git a/src/resources/transactions.ts b/src/resources/transactions.ts new file mode 100644 index 0000000..ec95cea --- /dev/null +++ b/src/resources/transactions.ts @@ -0,0 +1,554 @@ +import type { ApiResponse, Card, CardProvider, WalletProvider, XMoneyCore } from '../types' +import type { CardType, RefundReason, WalletBrand } from './types' +import { PaginatedList, SearchResult, XMoneyError } from '../core' + +/** + * Transaction status values + */ +export type TransactionStatus = + | 'start' + | 'in-progress' + | 'complete-ok' + | 'complete-failed' + | 'cancel-ok' + | 'refund-ok' + | 'void-ok' + | 'charge-back' + | '3d-pending' + +/** + * Transaction component details from payment provider + */ +export interface TransactionComponent { + /** + * Component ID + */ + componentId: number + /** + * Component type + */ + componentType: string + /** + * ISO 8601 date-time of component + */ + componentDate: string + /** + * Unix timestamp + */ + componentTimestamp: number + /** + * Provider internal reference + */ + providerIntRef?: string + /** + * Provider response code + */ + providerRc?: string + /** + * Provider response message + */ + providerMessage?: string + /** + * Provider authorization code + */ + providerAuth?: string + /** + * Provider retrieval reference number + */ + providerRrn?: string + /** + * Provider acquirer reference number + */ + providerArn?: string + /** + * Provider issuer reference data + */ + providerIrd?: string + /** + * Provider electronic commerce indicator + */ + providerEci?: string + /** + * Provider cavv/xid + */ + providerC?: string + /** + * Additional provider data + */ + data?: Record +} + +/** + * Transaction summary information + */ +export interface TransactionSummary { + /** + * Transaction ID + */ + id: number + /** + * Site ID + */ + siteId?: number + /** + * Associated order ID + */ + orderId: number + /** + * Associated customer ID + */ + customerId: number + /** + * Transaction type + */ + transactionType: 'deposit' | 'refund' | 'credit' | 'chargeback' | 'representment' + /** + * Payment method used + */ + transactionMethod: 'card' | 'wallet' | 'transfer' + /** + * Current transaction status + */ + transactionStatus: TransactionStatus + /** + * Customer IP address + */ + ip: string + /** + * Transaction amount + */ + amount: string + /** + * Currency code (ISO 4217) + */ + currency: string + /** + * Amount converted to EUR + */ + amountInEur: string + /** + * Transaction description + */ + description: string + /** + * Customer country code + */ + customerCountry: string + /** + * ISO 8601 creation date-time + */ + creationDate: string + /** + * Unix timestamp of creation + */ + creationTimestamp: number + /** + * Source of transaction + */ + transactionSource: 'service-call' | 're-bill' | 're-bill-micro' | 'card-change' + /** + * Admin user ID if applicable + */ + adminId?: number + /** + * Fraud risk score + */ + fraudScore?: number + /** + * Card provider ID + */ + cardProviderId?: number + /** + * Card provider name + */ + cardProvider?: string + /** + * Cardholder name + */ + cardHolderName?: string + /** + * Card provider display name + */ + cardProviderName?: string + /** + * Cardholder country + */ + cardHolderCountry?: string + /** + * Cardholder state + */ + cardHolderState?: string + /** + * Card type + */ + cardType?: string + /** + * Masked card number + */ + cardNumber?: string + /** + * Card expiry date + */ + cardExpiryDate?: string + /** + * Customer email + */ + email?: string + /** + * Saved card ID + */ + cardId?: number + /** + * Return URL after payment + */ + backUrl?: string + /** + * Custom data attached to transaction + */ + externalCustomData?: string + /** + * Statement descriptor + */ + cardDescriptor?: string +} + +/** + * Full transaction details including related data + */ +export interface Transaction extends TransactionSummary { + /** + * Parent transaction ID for refunds/chargebacks + */ + parentTransactionId?: number + /** + * Related transaction IDs + */ + relatedTransactionIds?: number[] + /** + * Card details if applicable + */ + card?: Card + /** + * Transaction component details from providers + */ + components?: TransactionComponent[] +} + +/** + * Parameters for listing and filtering transactions + */ +export interface TransactionListParams { + /** + * Search ID from previous search + */ + searchId?: string + /** + * Filter by parent resource type + */ + parentResourceType?: 'partner' | 'merchant' | 'site' + /** + * Filter by parent resource IDs + */ + parentResourceId?: number[] + /** + * Filter by order ID + */ + orderId?: number + /** + * Filter by customer ID + */ + customerId?: number + /** + * Filter by email + */ + email?: string + /** + * Filter by transaction method + */ + transactionMethod?: 'card' | 'wallet' | 'transfer' + /** + * Filter by currency + */ + currency?: string + /** + * Filter by minimum amount + */ + amountFrom?: number + /** + * Filter by maximum amount + */ + amountTo?: number + /** + * Filter by transaction type + */ + transactionType?: 'deposit' | 'refund' | 'credit' | 'chargeback' | 'representment' + /** + * Filter by transaction status + */ + transactionStatus?: TransactionStatus[] + /** + * Date field to filter by + */ + dateType?: 'creation' | 'approval' | 'refund' | 'cancellation' | 'charge-back' + /** + * Filter by date from + */ + createdAtFrom?: Date + /** + * Filter by date to + */ + createdAtTo?: Date + /** + * Filter transactions with ID greater than + */ + greaterThanId?: number + /** + * Filter by transaction source + */ + source?: Array<'service-call' | 're-bill' | 're-bill-micro' | 'card-change'> + /** + * Filter by IP address + */ + ip?: string + /** + * Filter by minimum fraud score + */ + fraudScoreGreaterThan?: number + /** + * Filter by refund reason + */ + reason?: RefundReason + /** + * Filter by user ID + */ + userId?: number + /** + * Page number + */ + page?: number + /** + * Items per page + */ + perPage?: number + /** + * Reverse sort order + */ + reverseSorting?: 0 | 1 + /** + * Filter by card provider + */ + cardProvider?: CardProvider + /** + * Filter by card type + */ + cardType?: CardType + /** + * Filter by card number (partial) + */ + cardNumber?: string + /** + * Filter by cardholder name + */ + cardHolderName?: string + /** + * Filter by country + */ + country?: string + /** + * Filter by state + */ + state?: string + /** + * Filter by wallet provider + */ + walletProvider?: WalletProvider + /** + * Filter by wallet brand + */ + walletBrand?: WalletBrand + /** + * Filter by wallet holder name + */ + walletHolderName?: string + /** + * Filter by wallet holder email + */ + walletHolderEmail?: string + /** + * Filter by initial transaction ID + */ + initialTransactionId?: number +} + +/** + * Parameters for capturing an authorized transaction + */ +export interface TransactionCaptureParams { + /** + * Amount to capture + */ + amount: number +} + +/** + * Parameters for refunding a transaction + */ +export interface TransactionRefundParams { + /** + * Refund reason + */ + reason?: RefundReason + /** + * Additional refund message + */ + message?: string + /** + * Partial refund amount (full refund if not specified) + */ + amount?: number + /** + * Transaction options (JSON) + */ + transactionOption?: string +} + +/** + * Resource for managing transactions in XMoney + * + * @example + * ```typescript + * const xMoney = createXMoneyClient({ apiKey: 'your-api-key' }) + * + * // Get transaction details + * const transaction = await xMoney.transactions.retrieve(12345) + * console.log(transaction.amount, transaction.transactionStatus) + * + * // Refund a transaction + * await xMoney.transactions.refund(12345, { + * reason: 'customer-demand', + * amount: 50.00 // partial refund + * }) + * ``` + */ +export class TransactionsResource { + constructor(private client: XMoneyCore) {} + + /** + * Retrieve transaction details + * @param id - Transaction ID + * @returns Full transaction information + * @throws {XMoneyError} If transaction not found + */ + async retrieve(id: number): Promise { + const response = await this.client.request({ + method: 'GET', + path: `/transaction/${id}`, + }) + + if (!response.data) { + throw new XMoneyError('Transaction not found') + } + + return response.data + } + + /** + * Capture an authorized transaction + * @param id - Transaction ID + * @param params - Capture parameters + * @throws {XMoneyError} If capture fails + */ + async capture(id: number, params: TransactionCaptureParams): Promise { + await this.client.request({ + method: 'PUT', + path: `/transaction/${id}`, + body: params, + }) + } + + /** + * Refund a transaction + * @param id - Transaction ID + * @param params - Refund parameters (optional for full refund) + * @throws {XMoneyError} If refund fails + */ + async refund(id: number, params?: TransactionRefundParams): Promise { + await this.client.request({ + method: 'DELETE', + path: `/transaction/${id}`, + body: params, + }) + } + + /** + * List transactions with filtering + * @param params - List parameters + * @returns Paginated list of transaction summaries + * + * @example + * ```typescript + * // List recent successful transactions + * const transactions = await xMoney.transactions.list({ + * transactionStatus: ['complete-ok'], + * createdAtFrom: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) + * }) + * + * for await (const tx of transactions) { + * console.log(tx.id, tx.amount, tx.currency) + * } + * ``` + */ + async list(params?: TransactionListParams): Promise> { + const response = await this.client.request({ + method: 'GET', + path: '/transaction', + query: params, + }) + + // Create a fetch function for pagination + const fetchNextPage = async (page: number): Promise> => { + return this.client.request({ + method: 'GET', + path: '/transaction', + query: { ...params, page }, + }) + } + + return new PaginatedList( + response.data || [], + response.pagination, + response.searchParams, + fetchNextPage, + ) + } + + /** + * Search transactions with advanced filtering + * @param params - Search parameters + * @returns Search result for fetching transactions + * @throws {XMoneyError} If search fails + * + * @example + * ```typescript + * const search = await xMoney.transactions.search({ + * amountFrom: 100, + * amountTo: 1000, + * transactionType: 'deposit' + * }) + * const results = await search.fetch() + * ``` + */ + async search(params: Omit): Promise> { + const response = await this.client.request<{ searchId: string, url?: string }>({ + method: 'POST', + path: '/transaction-search', + body: params, + }) + + if (!response.data?.searchId) { + throw new XMoneyError('Search failed') + } + + return new SearchResult(response.data.searchId, () => this.list({ searchId: response.data!.searchId })) + } +} diff --git a/src/resources/types.ts b/src/resources/types.ts new file mode 100644 index 0000000..72af3da --- /dev/null +++ b/src/resources/types.ts @@ -0,0 +1,116 @@ +/** + * Tag information for categorizing resources + */ +export interface Tag { + /** + * Tag identifier + */ + tag: string + /** + * ISO 8601 date-time when the tag was created + */ + creationDate: string + /** + * Unix timestamp of tag creation + */ + creationTimestamp: number +} + +/** + * Supported credit/debit card types + */ +export type CardType = + | 'visa' + | 'mastercard' + | 'maestro' + | 'amex' + | 'jcb' + | 'dankort' + | 'diners' + | 'discover' + | 'mir' + | 'unionpay' + +/** + * Supported card payment providers + */ +export type CardProvider = + | 'apcopay' + | 'argus' + | 'billfirst' + | 'decta' + | 'emerchantpay' + | 'epg' + | 'firstdata' + | 'kalixa' + | 'maxpay' + | 'nmi' + | 'optimal' + | 'payon' + | 'payone' + | 'payvision' + | 'postfinance' + | 'rocketgate' + | 'romcard' + | 'safecharge' + | 'wirecard' + | 'worldline' + +/** + * Supported wallet and alternative payment methods + */ +export type WalletType = + | 'neteller' + | 'paypal' + | 'paysafecard' + | 'skrill' + | 'sofort' + | 'trustly' + | 'astropaycard' + | 'astropaydirect' + | 'banamex' + | 'bancodechile' + | 'bancodeoccidente' + | 'bancodobrasil' + | 'bancontact' + | 'boletobancario' + | 'directpayeu' + | 'eps' + | 'giropay' + | 'ideal' + | 'mybank' + | 'poli' + | 'postfinance' + | 'sepa' + | 'verkkopankki' + | 'bitcoin' + | 'etherum' + +/** + * Supported wallet payment providers + */ +export type WalletProvider = + | 'alternativepayments' + | 'bitcoinromania' + | 'neteller' + | 'paypal' + | 'paysafecard' + | 'skrill' + | 'sofort' + | 'trustly' + +/** + * Alias for WalletType for backward compatibility + */ +export type WalletBrand = WalletType + +/** + * Reasons for transaction refunds + */ +export type RefundReason = + | 'fraud-confirm' + | 'highly-suspicious' + | 'duplicated-transaction' + | 'customer-demand' + | 'test-transaction' + | 'card-expired' diff --git a/src/services/order.service.spec.ts b/src/services/order.service.spec.ts deleted file mode 100644 index 5277738..0000000 --- a/src/services/order.service.spec.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { OrderService } from './order.service'; -import { OrderInputDto } from '../typings/dtos/order-input.dto'; - -describe('OrderService', () => { - let service: OrderService; - const mockOrderInput: OrderInputDto = { - publicKey: 'pk_test_abc123', - cardTransactionMode: 'authAndCapture', - backUrl: 'https://example.com', - customer: { - identifier: 'test-customer', - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@example.com', - }, - order: { - orderId: 'test-order-123', - type: 'purchase', - amount: 100, - currency: 'EUR', - description: 'Test order', - }, - }; - - beforeEach(() => { - service = new OrderService('test-secret-key'); - }); - - afterEach(() => { - }); - - describe('extractKeyFromSecretKey', () => { - it('should extract key from valid test secret key', () => { - const secretKey = 'sk_test_abc123'; - const result = (service as any).extractKeyFromSecretKey(secretKey); - expect(result).toBe('abc123'); - }); - - it('should extract key from valid live secret key', () => { - const secretKey = 'sk_live_xyz789'; - const result = (service as any).extractKeyFromSecretKey(secretKey); - expect(result).toBe('xyz789'); - }); - - it('should extract key from valid simple secret key', () => { - const secretKey = 'xyz789'; - const result = (service as any).extractKeyFromSecretKey(secretKey); - expect(result).toBe('xyz789'); - }); - - it('should return original key for invalid secret key format', () => { - const secretKey = 'invalid_key_format'; - const result = (service as any).extractKeyFromSecretKey(secretKey); - expect(result).toBe(secretKey); - }); - - it('should return original key for secret key with wrong prefix', () => { - const secretKey = 'wrong_prefix_test_abc123'; - const result = (service as any).extractKeyFromSecretKey(secretKey); - expect(result).toBe(secretKey); - }); - - it('should return original key for secret key with wrong environment', () => { - const secretKey = 'sk_staging_abc123'; - const result = (service as any).extractKeyFromSecretKey(secretKey); - expect(result).toBe(secretKey); - }); - }); - - describe('extractKeyFromPublicKey', () => { - it('should extract key from valid test public key', () => { - const publicKey = 'pk_test_abc123'; - const result = (service as any).extractKeyFromPublicKey(publicKey); - expect(result).toBe('abc123'); - }); - - it('should extract key from valid live public key', () => { - const publicKey = 'pk_live_xyz789'; - const result = (service as any).extractKeyFromPublicKey(publicKey); - expect(result).toBe('xyz789'); - }); - - it('should return null for invalid public key format', () => { - const publicKey = 'invalid_key_format'; - const result = (service as any).extractKeyFromPublicKey(publicKey); - expect(result).toBeNull(); - }); - - it('should return null for public key with wrong prefix', () => { - const publicKey = 'wrong_prefix_test_abc123'; - const result = (service as any).extractKeyFromPublicKey(publicKey); - expect(result).toBeNull(); - }); - - it('should return null for public key with wrong environment', () => { - const publicKey = 'pk_staging_abc123'; - const result = (service as any).extractKeyFromPublicKey(publicKey); - expect(result).toBeNull(); - }); - }); - - describe('createOrder', () => { - it('should successfully create order with valid test environment and public key', () => { - const result = service.createOrder(mockOrderInput); - - expect(result).toHaveProperty('payload'); - expect(result).toHaveProperty('checksum'); - expect(typeof result.payload).toBe('string'); - expect(typeof result.checksum).toBe('string'); - }); - - it('should successfully create order with valid live environment and public key', () => { - const liveOrderInput = { - ...mockOrderInput, - publicKey: 'pk_live_xyz789', - }; - const result = service.createOrder(liveOrderInput); - - expect(result).toHaveProperty('payload'); - expect(result).toHaveProperty('checksum'); - expect(typeof result.payload).toBe('string'); - expect(typeof result.checksum).toBe('string'); - }); - - it('should throw error when public key format is invalid', () => { - const invalidOrderInput = { - ...mockOrderInput, - publicKey: 'invalid_key_format', - }; - - expect(() => { - service.createOrder(invalidOrderInput); - }).toThrow('Invalid public key format. Expected format: pk__key'); - }); - - it('should set saveCard to false when not provided', () => { - const orderInputWithoutSaveCard = { ...mockOrderInput }; - delete orderInputWithoutSaveCard.saveCard; - - const result = service.createOrder(orderInputWithoutSaveCard); - - expect(result).toHaveProperty('payload'); - expect(result).toHaveProperty('checksum'); - expect(typeof result.payload).toBe('string'); - expect(typeof result.checksum).toBe('string'); - }); - - it('should preserve saveCard value when provided', () => { - const orderInputWithSaveCard = { - ...mockOrderInput, - saveCard: true, - }; - - const result = service.createOrder(orderInputWithSaveCard); - - expect(result).toHaveProperty('payload'); - expect(result).toHaveProperty('checksum'); - expect(typeof result.payload).toBe('string'); - expect(typeof result.checksum).toBe('string'); - }); - }); -}); diff --git a/src/services/order.service.ts b/src/services/order.service.ts deleted file mode 100644 index f89497d..0000000 --- a/src/services/order.service.ts +++ /dev/null @@ -1,122 +0,0 @@ -import * as crypto from 'crypto'; -import { OrderInputDto } from '../typings/dtos/order-input.dto'; -import { OrderOutputDto, xMoneyOrder, xMoneyOrderDecryptResponseDto } from '../typings/dtos'; -import { LIVE_ENV, LIVE_ENV_URL, TEST_ENV, TEST_ENV_URL } from '../typings/constants'; - -export class OrderService { - private secretKey: string; - private secretKeyEnv: string | null; - - private hostedCheckoutRedirectUrl: { [key: string]: string } = { - [TEST_ENV]: TEST_ENV_URL, - [LIVE_ENV]: LIVE_ENV_URL, - }; - - public constructor(secretKey: string) { - this.secretKey = this.extractKeyFromSecretKey(secretKey); - this.secretKeyEnv = this.extractEnvFromSecretKey(secretKey); - } - - public createOrder(orderInput: OrderInputDto): OrderOutputDto { - const publicKey = orderInput.publicKey; - - const key = this.extractKeyFromPublicKey(publicKey); - if (!key) { - throw new Error('Invalid public key format. Expected format: pk__key'); - } - - const order: xMoneyOrder = { - siteId: key, - ...orderInput, - }; - if (!order.saveCard) { - order.saveCard = false; - } - const base64Json = this.getBase64JsonRequest(order); - const base64Checksum = this.getBase64Checksum(order); - return { - payload: base64Json, - checksum: base64Checksum, - }; - } - - public createOrderWithHtml(orderInput: OrderInputDto) { - if (!this.secretKeyEnv) { - throw new Error('Cannot detect url based on secret key'); - } - - const envUrl = this.hostedCheckoutRedirectUrl[this.secretKeyEnv]; - - if (!envUrl) { - throw new Error('HostedCheckoutRedirect url missing'); - } - - const order = this.createOrder(orderInput); - - return `
- - - -
- `; - } - - private extractKeyFromPublicKey(publicKey: string): string | null { - const envPattern = `${TEST_ENV}|${LIVE_ENV}`; - const regexp = new RegExp(`^pk_(${envPattern})_(.+)$`); - const match = publicKey.match(regexp); - return match ? match[2] : null; - } - - private extractKeyFromSecretKey(secretKey: string): string { - const regexp = this.getSecretKeyRegex(); - const match = secretKey.match(regexp); - return match ? match[2] : secretKey; - } - - private extractEnvFromSecretKey(secretKey: string): string | null { - const regexp = this.getSecretKeyRegex(); - const match = secretKey.match(regexp); - return match ? match[1] : null; - } - - private getSecretKeyRegex(): RegExp { - const envPattern = `${TEST_ENV}|${LIVE_ENV}`; - return new RegExp(`^sk_(${envPattern})_(.+)$`); - } - - public decryptOrderResponse(encryptedResponse: string): xMoneyOrderDecryptResponseDto { - // get the IV and the encrypted data - const encryptedParts = encryptedResponse.split(',', 2), - iv = Buffer.from(encryptedParts[0], 'base64'), - encryptedData = Buffer.from(encryptedParts[1], 'base64'); - - // decrypt the encrypted data - const decipher = crypto.createDecipheriv('aes-256-cbc', this.secretKey, iv), - decryptedIpnResponse = Buffer.concat([ - decipher.update(encryptedData), - decipher.final(), - ]).toString(); - - // JSON decode the decrypted data - return JSON.parse(decryptedIpnResponse) as xMoneyOrderDecryptResponseDto; - } - - private getBase64JsonRequest(orderData: xMoneyOrder): string { - const jsonText = JSON.stringify(orderData); - - return Buffer.alloc(Buffer.byteLength(jsonText), jsonText).toString('base64'); - } - - private getBase64Checksum(orderData: xMoneyOrder): string { - const hmacSha512 = crypto.createHmac('sha512', this.secretKey); - hmacSha512.update(JSON.stringify(orderData)); - - return hmacSha512.digest('base64'); - } -} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..1382f68 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,172 @@ +import type { HttpClient, HttpMethod, HttpProtocol } from './http' +import type { PlatformProvider } from './platform/types' + +/** + * Configuration options for the XMoney SDK client + */ +export interface XMoneyConfig { + /** + * API key for authentication with XMoney Payment Gateway + */ + apiKey: string + /** + * Optional secure token for card operations + */ + secureToken?: string + /** + * API host (default: 'api.xmoney.com') + */ + host?: string + /** + * API port (default: 443 for https, 80 for http) + */ + port?: string | number + /** + * Protocol to use for API requests + * @default 'https' + */ + protocol?: HttpProtocol + /** + * Request timeout in milliseconds + * @default 80000 + */ + timeout?: number + /** + * Maximum number of retry attempts for failed requests + * @default 3 + */ + maxRetries?: number + /** + * HTTP client implementation (platform-specific) + */ + httpClient?: HttpClient + /** + * Platform provider for crypto operations (platform-specific) + */ + platformProvider?: PlatformProvider +} + +/** + * Core interface for XMoney client operations + */ +export interface XMoneyCore { + /** + * Make an API request to XMoney + * @param options - Request configuration + * @returns Promise resolving to the API response + */ + request: (options: RequestOptions) => Promise> + /** + * Read-only access to the client configuration + */ + config: Readonly +} + +/** + * Options for making API requests + */ +export interface RequestOptions { + /** + * HTTP method for the request + */ + method: HttpMethod + /** + * API endpoint path + */ + path: string + /** + * Query parameters to append to the URL + */ + query?: Record + /** + * Request body data (will be form-encoded) + */ + body?: Record + /** + * Additional headers to include in the request + */ + headers?: Record +} + +/** + * Standard API response format from XMoney + * @template T - The type of data in the response + */ +export interface ApiResponse { + /** + * Response code indicating success or failure + */ + code: number + /** + * Human-readable response message + */ + message: string + /** + * Response payload containing the requested data + */ + data?: T + /** + * Array of errors if the request failed + */ + error?: ApiError[] + /** + * Pagination metadata for list endpoints + */ + pagination?: Pagination + /** + * Search parameters used in the request + */ + searchParams?: Record +} + +/** + * Error details from API responses + */ +export interface ApiError { + /** + * Error code + */ + code: number + /** + * Error message describing the issue + */ + message: string + /** + * Type of error (Exception or Validation) + */ + type?: 'Exception' | 'Validation' + /** + * Field name for validation errors + */ + field?: string +} + +/** + * Pagination metadata for list responses + */ +export interface Pagination { + /** + * Current page number (1-indexed) + */ + currentPageNumber: number + /** + * Total number of items across all pages + */ + totalItemCount: number + /** + * Number of items per page + */ + itemCountPerPage: number + /** + * Number of items on the current page + */ + currentItemCount: number + /** + * Total number of pages + */ + pageCount: number +} + +export type * from './http' +export type * from './platform/types' +export type * from './resources' diff --git a/src/typings/constants.ts b/src/typings/constants.ts deleted file mode 100644 index b7618bc..0000000 --- a/src/typings/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const LIVE_ENV = 'live'; -export const TEST_ENV = 'test'; -export const LIVE_ENV_URL = 'https://secure.twispay.com'; -export const TEST_ENV_URL = 'https://secure-stage.xmoney.com'; diff --git a/src/typings/dtos/index.ts b/src/typings/dtos/index.ts deleted file mode 100644 index b0a8691..0000000 --- a/src/typings/dtos/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./init-input.dto"; -export * from "./order-output.dto"; -export * from "./order-input.dto"; -export * from "./xmoney"; diff --git a/src/typings/dtos/init-input.dto.ts b/src/typings/dtos/init-input.dto.ts deleted file mode 100644 index ff9fa62..0000000 --- a/src/typings/dtos/init-input.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class InitInputDto { - secretKey: string; -} diff --git a/src/typings/dtos/order-input.dto.ts b/src/typings/dtos/order-input.dto.ts deleted file mode 100644 index 4dbaf8f..0000000 --- a/src/typings/dtos/order-input.dto.ts +++ /dev/null @@ -1,24 +0,0 @@ -export class OrderInputDto { - publicKey: string; - cardTransactionMode: "auth" | "authAndCapture" | "credit"; - invoiceEmail?: string; - saveCard?: boolean; - backUrl: string; - customer: { - identifier: string; - firstName?: string; - lastName?: string; - country?: string; - city?: string; - phone?: string; - email?: string; - tags?: string[]; - }; - order: { - orderId: string; - type: "purchase" | "recurring" | "managed" | "credit"; - amount: number; - currency: string; - description: string; - }; -} diff --git a/src/typings/dtos/order-output.dto.ts b/src/typings/dtos/order-output.dto.ts deleted file mode 100644 index 7b58b71..0000000 --- a/src/typings/dtos/order-output.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class OrderOutputDto { - checksum: string; - payload: string; -} diff --git a/src/typings/dtos/xmoney/index.ts b/src/typings/dtos/xmoney/index.ts deleted file mode 100644 index 44f76c2..0000000 --- a/src/typings/dtos/xmoney/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./xmoney-api-error.dto"; -export * from "./xmoney-api-response.dto"; -export * from "./xmoney-order-response-data-redirect.dto"; -export * from "./xmoney-order-response-data.dto"; -export * from "./xmoney-order-decrypt-response.dto"; -export * from "./xmoney-order"; diff --git a/src/typings/dtos/xmoney/order-saved-card-output.dto.ts b/src/typings/dtos/xmoney/order-saved-card-output.dto.ts deleted file mode 100644 index 2886710..0000000 --- a/src/typings/dtos/xmoney/order-saved-card-output.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { xMoneyOrderResponseDataRedirectDto } from "./xmoney-order-response-data-redirect.dto"; -import { xMoneyOrderResponseDataDto } from "./xmoney-order-response-data.dto"; - -export class OrderSavedCardOutputDto { - isThreeDSecure: boolean; - data: xMoneyOrderResponseDataRedirectDto | xMoneyOrderResponseDataDto; -} diff --git a/src/typings/dtos/xmoney/xmoney-api-error.dto.ts b/src/typings/dtos/xmoney/xmoney-api-error.dto.ts deleted file mode 100644 index 7ced34a..0000000 --- a/src/typings/dtos/xmoney/xmoney-api-error.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { xMoneyResponseCodeEnum } from "../../enums"; - -export class xMoneyApiErrorDto { - code: xMoneyResponseCodeEnum; - message: string; - type: string; -} diff --git a/src/typings/dtos/xmoney/xmoney-api-response.dto.ts b/src/typings/dtos/xmoney/xmoney-api-response.dto.ts deleted file mode 100644 index 217f747..0000000 --- a/src/typings/dtos/xmoney/xmoney-api-response.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { xMoneyResponseCodeEnum } from "../../enums"; -import { xMoneyApiErrorDto } from "./xmoney-api-error.dto"; - -export class xMoneyApiResponseDto { - code: xMoneyResponseCodeEnum; - message: string; - data?: T; - error?: xMoneyApiErrorDto[]; -} diff --git a/src/typings/dtos/xmoney/xmoney-order-decrypt-response.dto.ts b/src/typings/dtos/xmoney/xmoney-order-decrypt-response.dto.ts deleted file mode 100644 index ab86060..0000000 --- a/src/typings/dtos/xmoney/xmoney-order-decrypt-response.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { xMoneyTransactionStatusEnum } from "../../enums/xmoney-transaction-status.enum"; - -export class xMoneyOrderDecryptResponseDto { - transactionStatus: xMoneyTransactionStatusEnum; - orderId: number; - externalOrderId: string; - transactionId: number; - transactionMethod: string; - customerId: number; - identifier: string; - amount: number; - currency: string; - customData: { - [key: string]: string; - } | null; - customFields: { - [key: string]: string; - } | null; - timestamp: number; - cardId: number | undefined; - errors?: { - code: number; - message: string; - type: string; - }[]; -} diff --git a/src/typings/dtos/xmoney/xmoney-order-response-data-redirect.dto.ts b/src/typings/dtos/xmoney/xmoney-order-response-data-redirect.dto.ts deleted file mode 100644 index 68f5ffe..0000000 --- a/src/typings/dtos/xmoney/xmoney-order-response-data-redirect.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class xMoneyOrderResponseDataRedirectDto { - url: string; - formMethod: number; - params: Record; -} diff --git a/src/typings/dtos/xmoney/xmoney-order-response-data.dto.ts b/src/typings/dtos/xmoney/xmoney-order-response-data.dto.ts deleted file mode 100644 index b4ae4c6..0000000 --- a/src/typings/dtos/xmoney/xmoney-order-response-data.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { xMoneyOrderResponseDataRedirectDto } from "./xmoney-order-response-data-redirect.dto"; - -export class xMoneyOrderResponseDataDto { - orderId: number; - transactionId: number; - cardId: number; - isRedirect?: boolean; - is3d?: number; - redirect?: xMoneyOrderResponseDataRedirectDto; -} diff --git a/src/typings/dtos/xmoney/xmoney-order.ts b/src/typings/dtos/xmoney/xmoney-order.ts deleted file mode 100644 index cd9cff5..0000000 --- a/src/typings/dtos/xmoney/xmoney-order.ts +++ /dev/null @@ -1,24 +0,0 @@ -export class xMoneyOrder { - siteId: string; - cardTransactionMode: "auth" | "authAndCapture" | "credit"; - invoiceEmail?: string; - saveCard?: boolean; - backUrl: string; - customer: { - identifier: string; - firstName?: string; - lastName?: string; - country?: string; - city?: string; - phone?: string; - email?: string; - tags?: string[]; - }; - order: { - orderId: string; - type: "purchase" | "recurring" | "managed" | "credit"; - amount: number; - currency: string; - description: string; - }; -} diff --git a/src/typings/enums/index.ts b/src/typings/enums/index.ts deleted file mode 100644 index ebc1c14..0000000 --- a/src/typings/enums/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./xmoney-respose-code.enum"; -export * from "./xmoney-transaction-status.enum"; diff --git a/src/typings/enums/xmoney-respose-code.enum.ts b/src/typings/enums/xmoney-respose-code.enum.ts deleted file mode 100644 index 246efd9..0000000 --- a/src/typings/enums/xmoney-respose-code.enum.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum xMoneyResponseCodeEnum { - SoftDecline = 839, - Created = 201, - Success = 200, -} diff --git a/src/typings/enums/xmoney-transaction-status.enum.ts b/src/typings/enums/xmoney-transaction-status.enum.ts deleted file mode 100644 index b9a1854..0000000 --- a/src/typings/enums/xmoney-transaction-status.enum.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum xMoneyTransactionStatusEnum { - CompleteOk = "complete-ok", - CompleteFailed = "complete-failed", - InProgress = "in-progress", - RefundOk = "refund-ok", -} diff --git a/src/utils/date-transformer.ts b/src/utils/date-transformer.ts new file mode 100644 index 0000000..16dffba --- /dev/null +++ b/src/utils/date-transformer.ts @@ -0,0 +1,154 @@ +/** + * Utility class for transforming dates between JavaScript Date objects + * and XMoney API date string format + * + * Handles automatic conversion of date fields in API requests and responses + */ +export class DateTransformer { + /** + * Set of field names that should be treated as dates + * @private + */ + private static readonly DATE_FIELDS = new Set([ + 'componentDate', + 'createdAt', + 'createdAtFrom', + 'createdAtTo', + 'creationDate', + 'deletedAt', + 'dueDate', + 'endDate', + 'firstBillDate', + 'isWhitelistedUntil', + 'isWhitelistedUntilFrom', + 'isWhitelistedUntilTo', + 'nextDueDate', + 'occurredAtFrom', + 'occurredAtTo', + 'startDate', + 'updatedAt', + ]) + + /** + * Transform Date objects to ISO 8601 strings for API requests + * + * Converts JavaScript Date objects to the format expected by XMoney API: + * "YYYY-MM-DDTHH:mm:ss+00:00" + * + * @template T - Type of the data + * @param data - Data containing potential Date objects + * @returns Data with Date objects converted to strings + * + * @example + * ```typescript + * const params = { + * createdAtFrom: new Date('2024-01-01'), + * customerId: 123 + * } + * const apiParams = DateTransformer.toApi(params) + * // Result: { createdAtFrom: '2024-01-01T00:00:00+00:00', customerId: 123 } + * ``` + */ + static toApi(data: T): T { + if (data === null || data === undefined) + return data + if (typeof data !== 'object') + return data + + if (Array.isArray(data)) { + return data.map(item => this.toApi(item)) as any + } + + // Handle Date objects + if (data instanceof Date) { + return (`${data.toISOString().slice(0, -5)}+00:00`) as any + } + + // Handle regular objects + const result: any = {} + + for (const [key, value] of Object.entries(data)) { + if (value instanceof Date) { + // Format date as required by API + result[key] = `${value.toISOString().slice(0, -5)}+00:00` + } + else if (value !== null && typeof value === 'object') { + // Recursively transform nested objects + result[key] = this.toApi(value) + } + else { + result[key] = value + } + } + + return result + } + + /** + * Transform date strings from API responses to JavaScript Date objects + * + * Automatically converts known date fields from string format to Date objects + * for easier manipulation in JavaScript + * + * @template T - Type of the data + * @param data - Data from API containing date strings + * @returns Data with date strings converted to Date objects + * + * @example + * ```typescript + * const apiResponse = { + * id: 123, + * creationDate: '2024-01-01T12:00:00+00:00' + * } + * const transformed = DateTransformer.fromApi(apiResponse) + * // Result: { id: 123, creationDate: Date object } + * ``` + */ + static fromApi(data: T): T { + if (data === null || data === undefined) + return data + if (typeof data !== 'object') + return data + + // Handle arrays + if (Array.isArray(data)) + return data.map(item => this.fromApi(item)) as any + + // Handle regular objects + const result: any = {} + + for (const [key, value] of Object.entries(data)) { + if (this.shouldConvertToDate(key, value)) { + result[key] = new Date(value as string) + } + else if (value !== null && typeof value === 'object') { + // Recursively transform nested objects + result[key] = this.fromApi(value) + } + else { + result[key] = value + } + } + + return result + } + + /** + * Check if a field should be converted to a Date object + * + * @param key - Field name to check + * @param value - Field value to validate + * @returns True if the field should be converted to Date + * @private + */ + private static shouldConvertToDate(key: string, value: any): boolean { + if (!this.DATE_FIELDS.has(key)) + return false + if (typeof value !== 'string') + return false + + // Check if it's a valid date string + const date = new Date(value) + return !Number.isNaN(date.getTime()) + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..3eff53e --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export { DateTransformer } from './date-transformer' diff --git a/test/core/client.test.ts b/test/core/client.test.ts new file mode 100644 index 0000000..aa9fce3 --- /dev/null +++ b/test/core/client.test.ts @@ -0,0 +1,436 @@ +import type { HttpClient, HttpResponse, PlatformProvider } from '../../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { XMoneyClient, XMoneyError } from '../../src/core' + +describe('xMoneyClient', () => { + let mockHttpClient: HttpClient + let mockPlatformProvider: PlatformProvider + let mockResponse: HttpResponse + + beforeEach(() => { + mockResponse = { + statusText: 'OK', + ok: true, + status: 200, + headers: {}, + json: vi.fn().mockResolvedValue({ data: 'test' }), + text: vi.fn().mockResolvedValue('test'), + } + + mockHttpClient = { + request: vi.fn().mockResolvedValue(mockResponse), + } + + mockPlatformProvider = { + crypto: { + createHmacSha512: vi.fn(), + createDecipherAes256Cbc: vi.fn(), + }, + buffer: { + from: vi.fn(), + alloc: vi.fn(), + byteLength: vi.fn(), + concat: vi.fn(), + toString: vi.fn(), + }, + } + }) + + describe('constructor', () => { + it('should create client with string API key', () => { + const client = new XMoneyClient({ + apiKey: 'test-key', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + expect(client.config.apiKey).toBe('test-key') + expect(client.config.protocol).toBe('https') + expect(client.config.host).toBe('api.xmoney.com') + expect(client.config.timeout).toBe(80000) + expect(client.config.maxRetries).toBe(3) + }) + + it('should create client with config object', () => { + const config = { + apiKey: 'test-key', + host: 'https://custom.api.com', + timeout: 5000, + maxRetries: 5, + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + } + + const client = new XMoneyClient(config) + + expect(client.config).toMatchObject({ + apiKey: 'test-key', + protocol: 'https', + host: 'custom.api.com', + timeout: 5000, + maxRetries: 5, + }) + }) + + it('should throw error if platform provider is not provided', () => { + expect(() => new XMoneyClient({ + apiKey: 'test-key', + httpClient: mockHttpClient, + } as any)).toThrow('Platform provider is required') + }) + + it('should parse host URL with protocol', () => { + const client = new XMoneyClient({ + apiKey: 'test-key', + host: 'https://api.xmoney.com', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + expect(client.config.protocol).toBe('https') + expect(client.config.host).toBe('api.xmoney.com') + expect(client.config.port).toBeUndefined() + }) + + it('should parse host URL with custom port', () => { + const client = new XMoneyClient({ + apiKey: 'test-key', + host: 'https://api.xmoney.com:8443', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + expect(client.config.protocol).toBe('https') + expect(client.config.host).toBe('api.xmoney.com') + expect(client.config.port).toBe(8443) + }) + + it('should use provided protocol and port over parsed values', () => { + const client = new XMoneyClient({ + apiKey: 'test-key', + host: 'https://api.xmoney.com:8443', + protocol: 'http', + port: 9000, + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + expect(client.config.protocol).toBe('http') + expect(client.config.host).toBe('api.xmoney.com') + expect(client.config.port).toBe(9000) + }) + }) + + describe('request', () => { + let client: XMoneyClient + + beforeEach(() => { + client = new XMoneyClient({ + apiKey: 'test-key', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + }) + + it('should make successful GET request', async () => { + const response = await client.request({ + method: 'GET', + path: '/test', + }) + + expect(response).toEqual({ data: 'test' }) + expect(mockHttpClient.request).toHaveBeenCalledWith({ + method: 'GET', + protocol: 'https', + host: 'api.xmoney.com', + port: 443, + path: '/test', + headers: { + Authorization: 'Bearer test-key', + }, + body: undefined, + timeout: 80000, + }) + }) + + it('should handle query parameters', async () => { + await client.request({ + method: 'GET', + path: '/test', + query: { + page: 1, + limit: 10, + filters: ['active', 'pending'], + nullValue: null, + undefinedValue: undefined, + }, + }) + + const callArgs = (mockHttpClient.request as any).mock.calls[0][0] + + expect(callArgs.path).toContain('page=1') + expect(callArgs.path).toContain('limit=10') + expect(callArgs.path).toContain('filters=active') + expect(callArgs.path).toContain('filters=pending') + expect(callArgs.path).not.toContain('nullValue') + expect(callArgs.path).not.toContain('undefinedValue') + }) + + it('should handle POST request with form data', async () => { + await client.request({ + method: 'POST', + path: '/test', + body: { + name: 'John', + age: 30, + hobbies: ['reading', 'coding'], + address: { + street: '123 Main St', + city: 'NYC', + }, + }, + }) + + expect(mockHttpClient.request).toHaveBeenCalledWith({ + method: 'POST', + protocol: 'https', + host: 'api.xmoney.com', + port: 443, + path: '/test', + headers: { + 'Authorization': 'Bearer test-key', + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: 'name=John&age=30&hobbies%5B%5D=reading&hobbies%5B%5D=coding&address%5Bstreet%5D=123+Main+St&address%5Bcity%5D=NYC', + timeout: 80000, + }) + }) + + it('should add secure token if provided', async () => { + const clientWithToken = new XMoneyClient({ + apiKey: 'test-key', + secureToken: 'secure-123', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + await clientWithToken.request({ + method: 'GET', + path: '/test', + }) + + expect(mockHttpClient.request).toHaveBeenCalledWith( + expect.objectContaining({ + headers: expect.objectContaining({ + 'x-secure-token': 'secure-123', + }), + }), + ) + }) + + it('should handle API errors', async () => { + mockResponse.ok = false + mockResponse.status = 400 + mockResponse.json = vi.fn().mockResolvedValue({ + message: 'Bad request', + code: 1001, + error: [{ type: 'Validation', field: 'email', message: 'Invalid email' }], + }) + + await expect(client.request({ + method: 'GET', + path: '/test', + })).rejects.toThrow(XMoneyError) + + try { + await client.request({ method: 'GET', path: '/test' }) + } + catch (error) { + expect(error).toBeInstanceOf(XMoneyError) + expect((error as XMoneyError).message).toBe('Bad request') + expect((error as XMoneyError).details).toEqual({ + statusCode: 400, + code: 1001, + errors: [{ type: 'Validation', field: 'email', message: 'Invalid email' }], + }) + } + }) + + it('should retry on server errors', async () => { + mockResponse.ok = false + mockResponse.status = 500 + mockResponse.json = vi.fn().mockResolvedValue({ message: 'Server error' }) + + mockHttpClient.request = vi.fn() + .mockRejectedValueOnce(new XMoneyError('Server error', { statusCode: 500 })) + .mockRejectedValueOnce(new XMoneyError('Server error', { statusCode: 500 })) + .mockResolvedValueOnce({ + ok: true, + status: 200, + json: vi.fn().mockResolvedValue({ data: 'success' }), + }) + + const response = await client.request({ + method: 'GET', + path: '/test', + }) + + expect(response).toEqual({ data: 'success' }) + expect(mockHttpClient.request).toHaveBeenCalledTimes(3) + }) + + it('should not retry on client errors', async () => { + const clientError = new XMoneyError('Bad request', { statusCode: 400 }) + mockHttpClient.request = vi.fn().mockRejectedValue(clientError) + + await expect(client.request({ + method: 'GET', + path: '/test', + })).rejects.toThrow(clientError) + + expect(mockHttpClient.request).toHaveBeenCalledTimes(1) + }) + + it('should respect maxRetries configuration', async () => { + const clientWithRetries = new XMoneyClient({ + apiKey: 'test-key', + maxRetries: 2, + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + mockHttpClient.request = vi.fn().mockRejectedValue(new Error('Network error')) + + await expect(clientWithRetries.request({ + method: 'GET', + path: '/test', + })).rejects.toThrow('Network error') + + expect(mockHttpClient.request).toHaveBeenCalledTimes(2) + }) + + it('should handle trailing slash in host URL', async () => { + const clientWithSlash = new XMoneyClient({ + apiKey: 'test-key', + host: 'https://api.example.com/', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + + await clientWithSlash.request({ + method: 'GET', + path: '/test', + }) + + const callArgs = (mockHttpClient.request as any).mock.calls[0][0] + expect(callArgs.host).toBe('api.example.com') + expect(callArgs.path).toBe('/test') + }) + + it('should handle path without leading slash', async () => { + await client.request({ + method: 'GET', + path: 'test', + }) + + const callArgs = (mockHttpClient.request as any).mock.calls[0][0] + expect(callArgs.path).toBe('/test') + }) + + it('should merge custom headers', async () => { + await client.request({ + method: 'GET', + path: '/test', + headers: { + 'X-Custom-Header': 'custom-value', + }, + }) + + expect(mockHttpClient.request).toHaveBeenCalledWith( + expect.objectContaining({ + headers: expect.objectContaining({ + 'Authorization': 'Bearer test-key', + 'X-Custom-Header': 'custom-value', + }), + }), + ) + }) + }) + + describe('encodeFormData', () => { + let client: XMoneyClient + + beforeEach(() => { + client = new XMoneyClient({ + apiKey: 'test-key', + platformProvider: mockPlatformProvider, + httpClient: mockHttpClient, + }) + }) + + it('should encode nested objects correctly', async () => { + await client.request({ + method: 'POST', + path: '/test', + body: { + user: { + name: 'John Doe', + email: 'john@example.com', + preferences: { + notifications: true, + theme: 'dark', + }, + }, + }, + }) + + const callArgs = (mockHttpClient.request as any).mock.calls[0][0] + const formData = new URLSearchParams(callArgs.body) + + expect(formData.get('user[name]')).toBe('John Doe') + expect(formData.get('user[email]')).toBe('john@example.com') + expect(formData.get('user[preferences][notifications]')).toBe('true') + expect(formData.get('user[preferences][theme]')).toBe('dark') + }) + + it('should handle null and undefined values', async () => { + await client.request({ + method: 'POST', + path: '/test', + body: { + name: 'John', + age: null, + email: undefined, + active: false, + count: 0, + }, + }) + + const callArgs = (mockHttpClient.request as any).mock.calls[0][0] + const formData = new URLSearchParams(callArgs.body) + + expect(formData.get('name')).toBe('John') + expect(formData.has('age')).toBe(false) + expect(formData.has('email')).toBe(false) + expect(formData.get('active')).toBe('false') + expect(formData.get('count')).toBe('0') + }) + + it('should handle arrays with bracket notation', async () => { + await client.request({ + method: 'POST', + path: '/test', + body: { + tags: ['javascript', 'typescript', 'node'], + scores: [100, 200, 300], + }, + }) + + const callArgs = (mockHttpClient.request as any).mock.calls[0][0] + const formData = new URLSearchParams(callArgs.body) + + expect(formData.getAll('tags[]')).toEqual(['javascript', 'typescript', 'node']) + expect(formData.getAll('scores[]')).toEqual(['100', '200', '300']) + }) + }) +}) diff --git a/test/core/error.test.ts b/test/core/error.test.ts new file mode 100644 index 0000000..566956b --- /dev/null +++ b/test/core/error.test.ts @@ -0,0 +1,146 @@ +import type { ApiError } from '../../src/types' +import { describe, expect, it } from 'vitest' +import { XMoneyError } from '../../src/core' + +describe('xMoneyError', () => { + it('should create error with message only', () => { + const error = new XMoneyError('Something went wrong') + + expect(error).toBeInstanceOf(Error) + expect(error).toBeInstanceOf(XMoneyError) + expect(error.message).toBe('Something went wrong') + expect(error.name).toBe('XMoneyError') + expect(error.details).toEqual({}) + }) + + it('should create error with details', () => { + const details = { + statusCode: 400, + code: 1001, + errors: [ + { type: 'Validation', field: 'email', message: 'Invalid email format' }, + ] as ApiError[], + } + + const error = new XMoneyError('Validation failed', details) + + expect(error.message).toBe('Validation failed') + expect(error.details).toEqual(details) + }) + + describe('isValidationError', () => { + it('should return true when validation errors exist', () => { + const error = new XMoneyError('Validation failed', { + errors: [ + { type: 'Validation', field: 'email', message: 'Invalid email', code: 1001 }, + { type: 'Validation', field: 'name', message: 'Name is required', code: 1002 }, + ], + }) + + expect(error.isValidationError).toBe(true) + }) + + it('should return false when no validation errors exist', () => { + const error = new XMoneyError('Server error', { + errors: [ + { type: 'Exception', message: 'Internal server error', code: 5001 }, + ], + }) + + expect(error.isValidationError).toBe(false) + }) + + it('should return false when no errors exist', () => { + const error = new XMoneyError('Error without details') + expect(error.isValidationError).toBe(false) + }) + + it('should return false when errors array is empty', () => { + const error = new XMoneyError('Error with empty errors', { + errors: [], + }) + expect(error.isValidationError).toBe(false) + }) + }) + + describe('validationErrors', () => { + it('should return validation errors as key-value pairs', () => { + const error = new XMoneyError('Validation failed', { + errors: [ + { type: 'Validation', field: 'email', message: 'Invalid email format', code: 1001 }, + { type: 'Validation', field: 'password', message: 'Password too short', code: 1002 }, + { type: 'Validation', field: 'name', message: 'Name is required', code: 1003 }, + ], + }) + + expect(error.validationErrors).toEqual({ + email: 'Invalid email format', + password: 'Password too short', + name: 'Name is required', + }) + }) + + it('should handle validation errors without field', () => { + const error = new XMoneyError('Validation failed', { + errors: [ + { type: 'Validation', message: 'General validation error', code: 1004 }, + { type: 'Validation', field: 'email', message: 'Invalid email', code: 1005 }, + ], + }) + + expect(error.validationErrors).toEqual({ + email: 'Invalid email', + }) + }) + + it('should return empty object when no validation errors', () => { + const error = new XMoneyError('Error', { + errors: [ + { type: 'Exception', message: 'Server error', code: 5001 }, + ], + }) + + expect(error.validationErrors).toEqual({}) + }) + + it('should return empty object when no errors', () => { + const error = new XMoneyError('Error without details') + expect(error.validationErrors).toEqual({}) + }) + + it('should handle multiple errors for the same field (last one wins)', () => { + const error = new XMoneyError('Validation failed', { + errors: [ + { type: 'Validation', field: 'email', message: 'Invalid format', code: 1001 }, + { type: 'Validation', field: 'email', message: 'Email already exists', code: 1002 }, + ], + }) + + expect(error.validationErrors).toEqual({ + email: 'Email already exists', + }) + }) + }) + + it('should have proper stack trace', () => { + const error = new XMoneyError('Test error') + expect(error.stack).toBeDefined() + expect(error.stack).toContain('XMoneyError: Test error') + }) + + it('should preserve all detail properties', () => { + const details = { + statusCode: 403, + code: 2001, + errors: [ + { type: 'Exception', message: 'Forbidden', code: 2001 }, + ] as ApiError[], + } + + const error = new XMoneyError('Forbidden', details) + + expect(error.details.statusCode).toBe(403) + expect(error.details.code).toBe(2001) + expect(error.details.errors).toEqual(details.errors) + }) +}) diff --git a/test/core/pagination.test.ts b/test/core/pagination.test.ts new file mode 100644 index 0000000..66710c3 --- /dev/null +++ b/test/core/pagination.test.ts @@ -0,0 +1,288 @@ +import type { Pagination } from '../../src/types' +import { describe, expect, it, vi } from 'vitest' +import { PaginatedList, SearchResult } from '../../src/core' + +describe('paginatedList', () => { + const mockData = [ + { id: 1, name: 'Item 1' }, + { id: 2, name: 'Item 2' }, + { id: 3, name: 'Item 3' }, + ] + + const mockPagination: Pagination = { + currentPageNumber: 1, + totalItemCount: 9, + itemCountPerPage: 3, + currentItemCount: 3, + pageCount: 3, + } + + describe('constructor and basic properties', () => { + it('should create paginated list with data only', () => { + const list = new PaginatedList(mockData) + + expect(list.data).toEqual(mockData) + expect(list.pagination).toBeUndefined() + expect(list.hasMore).toBe(false) + expect(list.totalCount).toBe(3) + expect(list.currentPage).toBe(1) + expect(list.totalPages).toBe(1) + expect(list.perPage).toBe(3) + }) + + it('should create paginated list with pagination info', () => { + const list = new PaginatedList(mockData, mockPagination) + + expect(list.data).toEqual(mockData) + expect(list.pagination).toEqual(mockPagination) + expect(list.hasMore).toBe(true) + expect(list.totalCount).toBe(9) + expect(list.currentPage).toBe(1) + expect(list.totalPages).toBe(3) + expect(list.perPage).toBe(3) + }) + + it('should handle last page correctly', () => { + const lastPagePagination = { ...mockPagination, currentPageNumber: 3 } + const list = new PaginatedList(mockData, lastPagePagination) + + expect(list.hasMore).toBe(false) + }) + }) + + describe('iterator', () => { + it('should iterate through current page data', () => { + const list = new PaginatedList(mockData, mockPagination) + const items = [] + + for (const item of list) { + items.push(item) + } + + expect(items).toEqual(mockData) + }) + + it('should support array spread operator', () => { + const list = new PaginatedList(mockData, mockPagination) + const items = [...list] + + expect(items).toEqual(mockData) + }) + }) + + describe('async iterator', () => { + it('should iterate through current page only when no fetch function', async () => { + const list = new PaginatedList(mockData, mockPagination) + const items = [] + + for await (const item of list) { + items.push(item) + } + + expect(items).toEqual(mockData) + }) + + it('should iterate through all pages when fetch function provided', async () => { + const page2Data = [ + { id: 4, name: 'Item 4' }, + { id: 5, name: 'Item 5' }, + { id: 6, name: 'Item 6' }, + ] + const page3Data = [ + { id: 7, name: 'Item 7' }, + { id: 8, name: 'Item 8' }, + { id: 9, name: 'Item 9' }, + ] + + const fetchNextPage = vi.fn().mockImplementation((page: number) => { + if (page === 2) { + return Promise.resolve({ + data: page2Data, + pagination: { ...mockPagination, currentPageNumber: 2 }, + }) + } + if (page === 3) { + return Promise.resolve({ + data: page3Data, + pagination: { ...mockPagination, currentPageNumber: 3 }, + }) + } + return Promise.resolve({ data: [] }) + }) + + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const items = [] + + for await (const item of list) { + items.push(item) + } + + expect(items).toEqual([...mockData, ...page2Data, ...page3Data]) + expect(fetchNextPage).toHaveBeenCalledTimes(2) + expect(fetchNextPage).toHaveBeenCalledWith(2) + expect(fetchNextPage).toHaveBeenCalledWith(3) + }) + + it('should handle empty response from fetchNextPage', async () => { + const fetchNextPage = vi.fn().mockResolvedValue({ data: null }) + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const items = [] + + for await (const item of list) { + items.push(item) + } + + expect(items).toEqual(mockData) + }) + }) + + describe('utility methods', () => { + it('toArray should return current page data', () => { + const list = new PaginatedList(mockData, mockPagination) + expect(list.toArray()).toEqual(mockData) + }) + + it('toArrayAll should fetch all pages', async () => { + const allData = [ + ...mockData, + { id: 4, name: 'Item 4' }, + { id: 5, name: 'Item 5' }, + ] + + const fetchNextPage = vi.fn().mockResolvedValue({ + data: [{ id: 4, name: 'Item 4' }, { id: 5, name: 'Item 5' }], + pagination: { ...mockPagination, currentPageNumber: 2, pageCount: 2 }, + }) + + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const result = await list.toArrayAll() + + expect(result).toEqual(allData) + }) + + it('getPage should return specific page', async () => { + const page2Data = [{ id: 4, name: 'Item 4' }] + const fetchNextPage = vi.fn().mockResolvedValue({ + data: page2Data, + pagination: { ...mockPagination, currentPageNumber: 2 }, + }) + + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const page2 = await list.getPage(2) + + expect(page2).not.toBeNull() + expect(page2!.data).toEqual(page2Data) + expect(page2!.currentPage).toBe(2) + }) + + it('getPage should return current page when requested', async () => { + const fetchNextPage = vi.fn() + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const currentPage = await list.getPage(1) + + expect(currentPage).toBe(list) + expect(fetchNextPage).not.toHaveBeenCalled() + }) + + it('getPage should return null for invalid page', async () => { + const list = new PaginatedList(mockData, mockPagination) + const result = await list.getPage(0) + + expect(result).toBeNull() + }) + + it('getPage should return null when no fetch function', async () => { + const list = new PaginatedList(mockData, mockPagination) + const result = await list.getPage(2) + + expect(result).toBeNull() + }) + + it('take should return limited items', async () => { + const allData = Array.from({ length: 10 }, (_, i) => ({ id: i + 1 })) + const fetchNextPage = vi.fn().mockResolvedValue({ + data: allData.slice(3), + pagination: { ...mockPagination, currentPageNumber: 2 }, + }) + + const list = new PaginatedList(allData.slice(0, 3), mockPagination, {}, fetchNextPage) + const result = await list.take(5) + + expect(result).toHaveLength(5) + expect(result).toEqual(allData.slice(0, 5)) + }) + + it('find should return first matching item', async () => { + const fetchNextPage = vi.fn().mockResolvedValue({ + data: [{ id: 4, name: 'Item 4' }, { id: 5, name: 'Target' }], + pagination: { ...mockPagination, currentPageNumber: 2 }, + }) + + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const result = await list.find(item => item.name === 'Target') + + expect(result).toEqual({ id: 5, name: 'Target' }) + }) + + it('find should return undefined when no match', async () => { + const list = new PaginatedList(mockData) + const result = await list.find(item => item.name === 'NonExistent') + + expect(result).toBeUndefined() + }) + + it('filter should return matching items from all pages', async () => { + const page2Data = [ + { id: 4, name: 'Item 4', active: true }, + { id: 5, name: 'Item 5', active: false }, + ] + + const fetchNextPage = vi.fn().mockResolvedValue({ + data: page2Data, + pagination: { ...mockPagination, currentPageNumber: 2, pageCount: 2 }, + }) + + const dataWithActive = mockData.map((item, i) => ({ ...item, active: i % 2 === 0 })) + const list = new PaginatedList(dataWithActive, mockPagination, {}, fetchNextPage) + const result = await list.filter(item => item.active) + + expect(result).toEqual([ + { id: 1, name: 'Item 1', active: true }, + { id: 3, name: 'Item 3', active: true }, + { id: 4, name: 'Item 4', active: true }, + ]) + }) + + it('map should transform all items', async () => { + const fetchNextPage = vi.fn().mockResolvedValue({ + data: [{ id: 4, name: 'Item 4' }], + pagination: { ...mockPagination, currentPageNumber: 2, pageCount: 2 }, + }) + + const list = new PaginatedList(mockData, mockPagination, {}, fetchNextPage) + const result = await list.map(item => item.id) + + expect(result).toEqual([1, 2, 3, 4]) + }) + }) +}) + +describe('searchResult', () => { + it('should store search ID and fetch function', () => { + const fetchFn = vi.fn() + const result = new SearchResult('search-123', fetchFn) + + expect(result.searchId).toBe('search-123') + }) + + it('should call fetch function when fetch is called', async () => { + const mockList = new PaginatedList([{ id: 1 }]) + const fetchFn = vi.fn().mockResolvedValue(mockList) + const result = new SearchResult('search-123', fetchFn) + + const list = await result.fetch() + + expect(fetchFn).toHaveBeenCalledOnce() + expect(list).toBe(mockList) + }) +}) diff --git a/test/factory.test.ts b/test/factory.test.ts new file mode 100644 index 0000000..64a5485 --- /dev/null +++ b/test/factory.test.ts @@ -0,0 +1,319 @@ +import type { HttpClient, PlatformProvider, XMoneyConfig } from '../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { createXMoneyClientFactory } from '../src/factory' +import { CardsResource, CustomersResource, NotificationsResource, OrdersResource, TransactionsResource } from '../src/resources' + +describe('createXMoneyClientFactory', () => { + let mockHttpClient: HttpClient + let mockPlatformProvider: PlatformProvider + + beforeEach(() => { + mockHttpClient = { + request: vi.fn(), + } + + mockPlatformProvider = { + crypto: { + createHmacSha512: vi.fn(), + createDecipherAes256Cbc: vi.fn(), + }, + buffer: { + from: vi.fn(), + alloc: vi.fn(), + byteLength: vi.fn(), + concat: vi.fn(), + toString: vi.fn(), + }, + } + }) + + describe('factory creation', () => { + it('should create SDK with string API key', () => { + const config = { apiKey: 'test-api-key', httpClient: mockHttpClient, platformProvider: mockPlatformProvider } + const sdk = createXMoneyClientFactory(config) + + expect(sdk).toBeDefined() + expect(sdk.customers).toBeInstanceOf(CustomersResource) + expect(sdk.orders).toBeInstanceOf(OrdersResource) + expect(sdk.transactions).toBeInstanceOf(TransactionsResource) + expect(sdk.cards).toBeInstanceOf(CardsResource) + expect(sdk.notifications).toBeInstanceOf(NotificationsResource) + expect(typeof sdk.request).toBe('function') + }) + + it('should create SDK with config object', () => { + const config: XMoneyConfig = { + apiKey: 'test-api-key', + host: 'https://custom.api.com', + timeout: 5000, + maxRetries: 5, + secureToken: 'secure-123', + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + const sdk = createXMoneyClientFactory(config) + + expect(sdk).toBeDefined() + expect(sdk.customers).toBeInstanceOf(CustomersResource) + expect(sdk.orders).toBeInstanceOf(OrdersResource) + expect(sdk.transactions).toBeInstanceOf(TransactionsResource) + expect(sdk.cards).toBeInstanceOf(CardsResource) + expect(sdk.notifications).toBeInstanceOf(NotificationsResource) + }) + + it('should pass HTTP client to the SDK', () => { + const config: XMoneyConfig = { + apiKey: 'test-api-key', + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + const sdk = createXMoneyClientFactory(config) + + // Test that the HTTP client is being used by making a request + mockHttpClient.request = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + headers: {}, + json: vi.fn().mockResolvedValue({ data: 'test' }), + text: vi.fn().mockResolvedValue('test'), + }) + + sdk.request({ + method: 'GET', + path: '/test', + }) + + expect(mockHttpClient.request).toHaveBeenCalled() + }) + }) + + describe('request method', () => { + it('should bind request method to client instance', async () => { + const config: XMoneyConfig = { + apiKey: 'test-api-key', + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + const mockResponse = { + ok: true, + status: 200, + statusText: 'OK', + headers: {}, + json: vi.fn().mockResolvedValue({ data: 'success' }), + text: vi.fn().mockResolvedValue('success'), + } + + mockHttpClient.request = vi.fn().mockResolvedValue(mockResponse) + + const sdk = createXMoneyClientFactory(config) + + const result = await sdk.request({ + method: 'POST', + path: '/test', + body: { test: true }, + }) + + expect(result).toEqual({ data: 'success' }) + expect(mockHttpClient.request).toHaveBeenCalledWith({ + method: 'POST', + protocol: 'https', + host: 'api.xmoney.com', + port: 443, + path: '/test', + headers: { + 'Authorization': 'Bearer test-api-key', + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: 'test=true', + timeout: 80000, + }) + }) + + it('should handle request errors', async () => { + const config: XMoneyConfig = { + apiKey: 'test-api-key', + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + mockHttpClient.request = vi.fn().mockRejectedValue(new Error('Network error')) + + const sdk = createXMoneyClientFactory(config) + + await expect(sdk.request({ + method: 'GET', + path: '/test', + })).rejects.toThrow('Network error') + }) + }) + + describe('resource initialization', () => { + it('should initialize all resources with the same client instance', () => { + const config: XMoneyConfig = { + apiKey: 'test-api-key', + host: 'https://api.example.com', + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + const sdk = createXMoneyClientFactory(config) + + // All resources should be properly initialized + expect(sdk.customers).toBeDefined() + expect(sdk.orders).toBeDefined() + expect(sdk.transactions).toBeDefined() + expect(sdk.cards).toBeDefined() + expect(sdk.notifications).toBeDefined() + + // Test that resources can make requests + mockHttpClient.request = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + headers: {}, + json: vi.fn().mockResolvedValue({ data: { id: 123 } }), + text: vi.fn().mockResolvedValue('{"data":{"id":123}}'), + }) + + // Example: calling a resource method + sdk.customers.retrieve(123) + + expect(mockHttpClient.request).toHaveBeenCalled() + }) + }) + + describe('configuration merging', () => { + it('should merge string config with defaults', () => { + const sdk = createXMoneyClientFactory({ apiKey: 'api-key-123', httpClient: mockHttpClient, platformProvider: mockPlatformProvider }) + + // Test that default configuration is applied + mockHttpClient.request = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + headers: {}, + json: vi.fn().mockResolvedValue({ data: 'test' }), + text: vi.fn().mockResolvedValue('test'), + }) + + sdk.request({ + method: 'GET', + path: '/test', + }) + + expect(mockHttpClient.request).toHaveBeenCalledWith( + expect.objectContaining({ + protocol: 'https', + host: 'api.xmoney.com', + port: 443, + path: '/test', + timeout: 80000, + headers: expect.objectContaining({ + Authorization: 'Bearer api-key-123', + }), + }), + ) + }) + + it('should override defaults with provided config', () => { + const config: XMoneyConfig = { + apiKey: 'custom-key', + host: 'https://custom.host.com', + timeout: 10000, + maxRetries: 10, + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + const sdk = createXMoneyClientFactory(config) + + mockHttpClient.request = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + headers: {}, + json: vi.fn().mockResolvedValue({ data: 'test' }), + text: vi.fn().mockResolvedValue('test'), + }) + + sdk.request({ + method: 'GET', + path: '/test', + }) + + expect(mockHttpClient.request).toHaveBeenCalledWith( + expect.objectContaining({ + protocol: 'https', + host: 'custom.host.com', + port: 443, + path: '/test', + timeout: 10000, + headers: expect.objectContaining({ + Authorization: 'Bearer custom-key', + }), + }), + ) + }) + }) + + describe('secure token handling', () => { + it('should include secure token in requests when provided', () => { + const config: XMoneyConfig = { + apiKey: 'test-key', + secureToken: 'secure-token-123', + httpClient: mockHttpClient, + platformProvider: mockPlatformProvider, + } + + const sdk = createXMoneyClientFactory(config) + + mockHttpClient.request = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + headers: {}, + json: vi.fn().mockResolvedValue({ data: 'test' }), + text: vi.fn().mockResolvedValue('test'), + }) + + sdk.request({ + method: 'POST', + path: '/cards/test', + }) + + expect(mockHttpClient.request).toHaveBeenCalledWith( + expect.objectContaining({ + headers: expect.objectContaining({ + 'x-secure-token': 'secure-token-123', + }), + }), + ) + }) + }) + + describe('sDK interface', () => { + it('should implement the complete XMoneySDK interface', () => { + const sdk = createXMoneyClientFactory({ apiKey: 'test-key', httpClient: mockHttpClient, platformProvider: mockPlatformProvider }) + + // Check that all required properties exist + expect(sdk).toHaveProperty('customers') + expect(sdk).toHaveProperty('orders') + expect(sdk).toHaveProperty('transactions') + expect(sdk).toHaveProperty('cards') + expect(sdk).toHaveProperty('notifications') + expect(sdk).toHaveProperty('request') + + // Check that they are the correct types + expect(sdk.customers).toBeInstanceOf(CustomersResource) + expect(sdk.orders).toBeInstanceOf(OrdersResource) + expect(sdk.transactions).toBeInstanceOf(TransactionsResource) + expect(sdk.cards).toBeInstanceOf(CardsResource) + expect(sdk.notifications).toBeInstanceOf(NotificationsResource) + expect(typeof sdk.request).toBe('function') + }) + }) +}) diff --git a/test/http/fetch-client.test.ts b/test/http/fetch-client.test.ts new file mode 100644 index 0000000..a51d7f1 --- /dev/null +++ b/test/http/fetch-client.test.ts @@ -0,0 +1,379 @@ +import type { HttpRequestOptions } from '../../src/http' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { FetchHttpClient } from '../../src/http' + +describe('fetchHttpClient', () => { + let mockFetch: ReturnType + let client: FetchHttpClient + + beforeEach(() => { + vi.useFakeTimers() + mockFetch = vi.fn() + client = new FetchHttpClient(mockFetch) + }) + + afterEach(() => { + vi.useRealTimers() + }) + + describe('constructor', () => { + it('should create client with provided fetch function', () => { + expect(() => new FetchHttpClient(mockFetch)).not.toThrow() + }) + + it('should throw error if no fetch function available', () => { + const originalFetch = globalThis.fetch + delete (globalThis as any).fetch + + expect(() => new FetchHttpClient()) + .toThrow('Fetch is not available. Please provide a fetch implementation or use NodeHttpClient.') + + globalThis.fetch = originalFetch + }) + + it('should use global fetch if available', () => { + const originalFetch = globalThis.fetch + globalThis.fetch = mockFetch + + const clientWithGlobal = new FetchHttpClient() + expect(() => clientWithGlobal).not.toThrow() + + globalThis.fetch = originalFetch + }) + }) + + describe('request', () => { + const mockHeaders = new Map([ + ['Content-Type', 'application/json'], + ['X-Custom-Header', 'value'], + ]) + + const mockResponse = { + ok: true, + status: 200, + statusText: 'OK', + headers: mockHeaders, + json: vi.fn().mockResolvedValue({ data: 'test' }), + text: vi.fn().mockResolvedValue('test response'), + } + + beforeEach(() => { + mockFetch.mockResolvedValue(mockResponse) + }) + + it('should make successful GET request', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: { Authorization: 'Bearer token' }, + timeout: 5000, + } + + const response = await client.request(options) + + expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/test', { + method: 'GET', + headers: { Authorization: 'Bearer token' }, + body: undefined, + signal: expect.any(AbortSignal), + }) + + expect(response.ok).toBe(true) + expect(response.status).toBe(200) + expect(response.statusText).toBe('OK') + expect(await response.json()).toEqual({ data: 'test' }) + }) + + it('should make POST request with body', async () => { + const options: HttpRequestOptions = { + method: 'POST', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: 'test' }), + timeout: 5000, + } + + await client.request(options) + + expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/test', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: 'test' }), + signal: expect.any(AbortSignal), + }) + }) + + it('should handle error responses', async () => { + mockFetch.mockResolvedValue({ + ...mockResponse, + ok: false, + status: 404, + statusText: 'Not Found', + }) + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/notfound', + headers: {}, + timeout: 5000, + } + + const response = await client.request(options) + + expect(response.ok).toBe(false) + expect(response.status).toBe(404) + expect(response.statusText).toBe('Not Found') + }) + + it('should parse headers correctly', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + const response = await client.request(options) + + expect(response.headers).toEqual({ + 'content-type': 'application/json', + 'x-custom-header': 'value', + }) + }) + + it('should handle timeout', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 100, + } + + // Create an abort error that will be thrown + const abortError = new Error('The operation was aborted') + abortError.name = 'AbortError' + + // Mock fetch to throw when aborted + mockFetch.mockImplementation((url: string, init: RequestInit) => { + // Return a promise that rejects when abort signal fires + return new Promise((resolve, reject) => { + // If signal is already aborted, reject immediately + if (init.signal?.aborted) { + reject(abortError) + return + } + + // Set up abort handler + init.signal?.addEventListener('abort', () => { + reject(abortError) + }, { once: true }) + + // This timeout would resolve after 200ms (longer than abort timeout) + // But it will be aborted before then + }) + }) + + // Start the request (don't await yet) + const requestPromise = client.request(options) + + // Advance time to trigger the timeout + vi.advanceTimersByTime(100) + + // Now the promise should reject + await expect(requestPromise).rejects.toThrow('abort') + + // Verify fetch was called with abort signal + expect(mockFetch).toHaveBeenCalledWith( + 'https://api.example.com/test', + expect.objectContaining({ + signal: expect.any(AbortSignal), + }), + ) + }) + + it('should clear timeout on successful response', async () => { + const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout') + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + await client.request(options) + + expect(clearTimeoutSpy).toHaveBeenCalledOnce() + }) + + it('should clear timeout on error', async () => { + const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout') + mockFetch.mockRejectedValue(new Error('Network error')) + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + try { + await client.request(options) + } + catch { + // Expected error + } + + expect(clearTimeoutSpy).toHaveBeenCalledOnce() + }) + + it('should handle fetch errors', async () => { + mockFetch.mockRejectedValue(new Error('Network error')) + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + await expect(client.request(options)).rejects.toThrow('Network error') + }) + + it('should handle empty headers', async () => { + mockFetch.mockResolvedValue({ + ...mockResponse, + headers: new Map(), + }) + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + const response = await client.request(options) + + expect(response.headers).toEqual({}) + }) + + it('should convert header names to lowercase', async () => { + const mixedCaseHeaders = new Map([ + ['Content-Type', 'application/json'], + ['X-Custom-HEADER', 'value'], + ]) + + mockFetch.mockResolvedValue({ + ...mockResponse, + headers: mixedCaseHeaders, + }) + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + const response = await client.request(options) + + expect(response.headers).toEqual({ + 'content-type': 'application/json', + 'x-custom-header': 'value', + }) + }) + + it('should provide text method', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + const response = await client.request(options) + const text = await response.text() + + expect(text).toBe('test response') + expect(mockResponse.text).toHaveBeenCalledOnce() + }) + + it('should build URL correctly with custom port', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 8443, + path: '/test', + headers: {}, + timeout: 5000, + } + + await client.request(options) + + expect(mockFetch).toHaveBeenCalledWith('https://api.example.com:8443/test', expect.any(Object)) + }) + + it('should build URL correctly without port for default ports', async () => { + const httpsOptions: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + await client.request(httpsOptions) + expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/test', expect.any(Object)) + + const httpOptions: HttpRequestOptions = { + method: 'GET', + protocol: 'http', + host: 'api.example.com', + port: 80, + path: '/test', + headers: {}, + timeout: 5000, + } + + await client.request(httpOptions) + expect(mockFetch).toHaveBeenCalledWith('http://api.example.com/test', expect.any(Object)) + }) + }) +}) diff --git a/test/http/node-client.test.ts b/test/http/node-client.test.ts new file mode 100644 index 0000000..d8052a5 --- /dev/null +++ b/test/http/node-client.test.ts @@ -0,0 +1,573 @@ +import type { HttpRequestOptions } from '../../src/http' +import { EventEmitter } from 'node:events' +import { PassThrough } from 'node:stream' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { NodeHttpClient } from '../../src/http' + +// Mock Node.js modules +vi.mock('node:https', () => { + const mockRequest = vi.fn() + const module = { request: mockRequest } + return { + ...module, + default: module, + } +}) + +vi.mock('node:http', () => { + const mockRequest = vi.fn() + const module = { request: mockRequest } + return { + ...module, + default: module, + } +}) + +// URL mock removed as we don't need to parse URLs anymore + +describe('nodeHttpClient', () => { + let client: NodeHttpClient + let mockHttpsRequest: ReturnType + let mockHttpRequest: ReturnType + + beforeEach(async () => { + client = new NodeHttpClient() + + // Get the mocked functions + const https = await import('node:https') + const http = await import('node:http') + + mockHttpsRequest = https.request as any + mockHttpRequest = http.request as any + }) + + afterEach(() => { + vi.clearAllMocks() + }) + + describe('request', () => { + let mockRequest: any + let mockResponse: any + + beforeEach(() => { + // Create mock request object + mockRequest = new EventEmitter() as any + mockRequest.write = vi.fn() + mockRequest.end = vi.fn() + mockRequest.destroy = vi.fn() + + // Create fresh mock response object each time + mockResponse = new PassThrough() as any + mockResponse.statusCode = 200 + mockResponse.statusMessage = 'OK' + mockResponse.headers = { + 'content-type': 'application/json', + 'x-custom-header': 'value', + } + + // Clear all mock implementations + mockHttpsRequest.mockClear() + mockHttpRequest.mockClear() + }) + + it('should make successful HTTPS GET request', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: { Authorization: 'Bearer token' }, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{"data":"test"}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(mockHttpsRequest).toHaveBeenCalledWith( + { + hostname: 'api.example.com', + port: 443, + path: '/test', + method: 'GET', + headers: { Authorization: 'Bearer token' }, + timeout: 5000, + }, + expect.any(Function), + ) + + expect(response.ok).toBe(true) + expect(response.status).toBe(200) + expect(response.statusText).toBe('OK') + expect(response.headers).toEqual({ + 'content-type': 'application/json', + 'x-custom-header': 'value', + }) + expect(await response.json()).toEqual({ data: 'test' }) + }) + + it('should make successful HTTP request', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'http', + host: 'api.example.com', + port: 80, + path: '/test', + headers: {}, + timeout: 5000, + } + + mockHttpRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{"success":true}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(mockHttpRequest).toHaveBeenCalled() + expect(mockHttpsRequest).not.toHaveBeenCalled() + expect(await response.json()).toEqual({ success: true }) + }) + + it('should handle custom port', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 8443, + path: '/test', + headers: {}, + timeout: 5000, + } + + await client.request(options) + + expect(mockHttpsRequest).toHaveBeenCalledWith( + expect.objectContaining({ + port: 8443, + }), + expect.any(Function), + ) + }) + + it('should handle query parameters', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test?page=1&limit=10', + headers: {}, + timeout: 5000, + } + + await client.request(options) + + expect(mockHttpsRequest).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/test?page=1&limit=10', + }), + expect.any(Function), + ) + }) + + it('should make POST request with body', async () => { + const options: HttpRequestOptions = { + method: 'POST', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: 'test' }), + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{"id":1}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(mockRequest.write).toHaveBeenCalledWith(JSON.stringify({ name: 'test' })) + expect(mockRequest.end).toHaveBeenCalled() + expect(await response.json()).toEqual({ id: 1 }) + }) + + it('should handle error responses', async () => { + mockResponse.statusCode = 404 + mockResponse.statusMessage = 'Not Found' + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/notfound', + headers: {}, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{"error":"Not found"}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(response.ok).toBe(false) + expect(response.status).toBe(404) + expect(response.statusText).toBe('Not Found') + }) + + it('should handle network errors', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((_options, _callback) => { + const req = new EventEmitter() as any + req.write = vi.fn() + req.end = vi.fn() + req.destroy = vi.fn() + + setImmediate(() => { + req.emit('error', new Error('Network error')) + }) + + return req + }) + + await expect(client.request(options)).rejects.toThrow('Network error') + }) + + it('should handle timeout', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 100, + } + + mockHttpsRequest.mockImplementation((_options, _callback) => { + const req = new EventEmitter() as any + req.write = vi.fn() + req.end = vi.fn() + req.destroy = vi.fn() + + setImmediate(() => { + req.emit('timeout') + }) + + return req + }) + + await expect(client.request(options)).rejects.toThrow('Request timeout') + }) + + it('should handle empty response', async () => { + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + // No data emitted, just end + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(await response.text()).toBe('') + await expect(response.json()).rejects.toThrow() // JSON.parse('') throws + }) + + it('should handle response in chunks', async () => { + // Reset all mocks for this test + mockHttpsRequest.mockReset() + mockHttpRequest.mockReset() + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + // Reset mock response to emit the expected data + const chunkedResponse = new PassThrough() as any + chunkedResponse.statusCode = 200 + chunkedResponse.statusMessage = 'OK' + chunkedResponse.headers = mockResponse.headers + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(chunkedResponse) + setImmediate(() => { + // Emit data in multiple chunks + chunkedResponse.emit('data', '{"name":') + chunkedResponse.emit('data', '"John",') + chunkedResponse.emit('data', '"age":30}') + chunkedResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(await response.json()).toEqual({ name: 'John', age: 30 }) + }) + + it('should handle missing status code', async () => { + mockResponse.statusCode = undefined + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(response.status).toBe(0) + expect(response.ok).toBe(false) + }) + + it('should handle PUT request with body', async () => { + const options: HttpRequestOptions = { + method: 'PUT', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test/1', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: 'updated' }), + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{"success":true}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + await client.request(options) + + expect(mockHttpsRequest).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'PUT', + }), + expect.any(Function), + ) + expect(mockRequest.write).toHaveBeenCalledWith(JSON.stringify({ name: 'updated' })) + }) + + it('should handle DELETE request', async () => { + const options: HttpRequestOptions = { + method: 'DELETE', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test/1', + headers: {}, + timeout: 5000, + } + + mockResponse.statusCode = 204 + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(response.status).toBe(204) + expect(response.ok).toBe(true) + }) + + it('should provide text method', async () => { + // Reset all mocks for this test + mockHttpsRequest.mockReset() + mockHttpRequest.mockReset() + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + // Create a new response for text test + const textResponse = new PassThrough() as any + textResponse.statusCode = 200 + textResponse.statusMessage = 'OK' + textResponse.headers = mockResponse.headers + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(textResponse) + setImmediate(() => { + textResponse.emit('data', 'Plain text response') + textResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + const text = await response.text() + + expect(text).toBe('Plain text response') + }) + + it('should handle 2xx status codes as success', async () => { + const testCases = [200, 201, 202, 203, 204, 299] + + for (const statusCode of testCases) { + mockResponse.statusCode = statusCode + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(response.ok).toBe(true) + expect(response.status).toBe(statusCode) + } + }) + + it('should handle non-2xx status codes as error', async () => { + const testCases = [100, 199, 300, 301, 400, 401, 403, 404, 500, 502] + + for (const statusCode of testCases) { + mockResponse.statusCode = statusCode + + const options: HttpRequestOptions = { + method: 'GET', + protocol: 'https', + host: 'api.example.com', + port: 443, + path: '/test', + headers: {}, + timeout: 5000, + } + + mockHttpsRequest.mockImplementation((options, callback) => { + setImmediate(() => { + callback(mockResponse) + setImmediate(() => { + mockResponse.emit('data', '{}') + mockResponse.emit('end') + }) + }) + return mockRequest + }) + + const response = await client.request(options) + + expect(response.ok).toBe(false) + expect(response.status).toBe(statusCode) + } + }) + }) +}) diff --git a/test/platform/node.test.ts b/test/platform/node.test.ts new file mode 100644 index 0000000..7001bbb --- /dev/null +++ b/test/platform/node.test.ts @@ -0,0 +1,233 @@ +import { Buffer } from 'node:buffer' +import crypto from 'node:crypto' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { NodeBuffer, NodeCrypto, nodePlatformProvider } from '../../src/platform/node' + +vi.mock('node:crypto') + +describe('nodeCrypto', () => { + let nodeCrypto: NodeCrypto + let mockCrypto: typeof crypto + + beforeEach(() => { + nodeCrypto = new NodeCrypto() + mockCrypto = crypto as any + }) + + describe('createHmacSha512', () => { + it('should create HMAC-SHA512 instance', () => { + const mockHmac = { + update: vi.fn(), + digest: vi.fn().mockReturnValue(Buffer.from('digest')), + } + + mockCrypto.createHmac = vi.fn().mockReturnValue(mockHmac) + + const hmac = nodeCrypto.createHmacSha512('secret-key') + + expect(mockCrypto.createHmac).toHaveBeenCalledWith('sha512', 'secret-key') + + // Test update + hmac.update('data') + expect(mockHmac.update).toHaveBeenCalledWith('data') + + // Test digest + const result = hmac.digest() + expect(mockHmac.digest).toHaveBeenCalled() + expect(result).toBeInstanceOf(Buffer) + expect(result.toString()).toBe('digest') + }) + + it('should handle multiple updates', () => { + const mockHmac = { + update: vi.fn(), + digest: vi.fn().mockReturnValue(Buffer.from('final-digest')), + } + + mockCrypto.createHmac = vi.fn().mockReturnValue(mockHmac) + + const hmac = nodeCrypto.createHmacSha512('key') + + hmac.update('part1') + hmac.update('part2') + hmac.update('part3') + + expect(mockHmac.update).toHaveBeenCalledTimes(3) + expect(mockHmac.update).toHaveBeenCalledWith('part1') + expect(mockHmac.update).toHaveBeenCalledWith('part2') + expect(mockHmac.update).toHaveBeenCalledWith('part3') + }) + }) + + describe('createDecipherAes256Cbc', () => { + it('should create AES-256-CBC decipher instance', () => { + const mockDecipher = { + update: vi.fn().mockReturnValue(Buffer.from('decrypted-update')), + final: vi.fn().mockReturnValue(Buffer.from('decrypted-final')), + } + + mockCrypto.createDecipheriv = vi.fn().mockReturnValue(mockDecipher) + + const iv = new Uint8Array(16) + const decipher = nodeCrypto.createDecipherAes256Cbc('32-byte-key-goes-here-padding!!', iv) + + expect(mockCrypto.createDecipheriv).toHaveBeenCalledWith( + 'aes-256-cbc', + '32-byte-key-goes-here-padding!!', + Buffer.from(iv), + ) + + // Test update + const encrypted = new Uint8Array([1, 2, 3, 4]) + const updateResult = decipher.update(encrypted) + expect(mockDecipher.update).toHaveBeenCalledWith(Buffer.from(encrypted)) + expect(updateResult).toBeInstanceOf(Buffer) + expect(updateResult.toString()).toBe('decrypted-update') + + // Test final + const finalResult = decipher.final() + expect(mockDecipher.final).toHaveBeenCalled() + expect(finalResult).toBeInstanceOf(Buffer) + expect(finalResult.toString()).toBe('decrypted-final') + }) + }) +}) + +describe('nodeBuffer', () => { + let nodeBuffer: NodeBuffer + + beforeEach(() => { + nodeBuffer = new NodeBuffer() + }) + + describe('from', () => { + it('should create buffer from string with UTF-8 encoding', () => { + const result = nodeBuffer.from('hello world', 'utf8') + expect(result).toBeInstanceOf(Uint8Array) + expect(Buffer.from(result).toString()).toBe('hello world') + }) + + it('should create buffer from string with base64 encoding', () => { + const base64 = 'aGVsbG8gd29ybGQ=' // "hello world" in base64 + const result = nodeBuffer.from(base64, 'base64') + expect(result).toBeInstanceOf(Uint8Array) + expect(Buffer.from(result).toString()).toBe('hello world') + }) + + it('should create buffer from string without encoding (default UTF-8)', () => { + const result = nodeBuffer.from('test') + expect(result).toBeInstanceOf(Uint8Array) + expect(Buffer.from(result).toString()).toBe('test') + }) + + it('should create buffer from ArrayBuffer', () => { + const arrayBuffer = new ArrayBuffer(4) + const view = new Uint8Array(arrayBuffer) + view[0] = 65 // 'A' + view[1] = 66 // 'B' + view[2] = 67 // 'C' + view[3] = 68 // 'D' + + const result = nodeBuffer.from(arrayBuffer) + expect(result).toBeInstanceOf(Uint8Array) + expect(Buffer.from(result).toString()).toBe('ABCD') + }) + + it('should create buffer from Uint8Array', () => { + const uint8Array = new Uint8Array([72, 101, 108, 108, 111]) // "Hello" + const result = nodeBuffer.from(uint8Array) + expect(result).toBeInstanceOf(Uint8Array) + expect(Buffer.from(result).toString()).toBe('Hello') + }) + }) + + describe('alloc', () => { + it('should allocate buffer with specified size', () => { + const result = nodeBuffer.alloc(10) + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(10) + expect(Array.from(result).every(byte => byte === 0)).toBe(true) + }) + + it('should allocate buffer with fill value', () => { + const result = nodeBuffer.alloc(5, 'A') + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(5) + expect(Buffer.from(result).toString()).toBe('AAAAA') + }) + + it('should handle multi-byte fill values', () => { + const result = nodeBuffer.alloc(6, 'AB') + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(6) + expect(Buffer.from(result).toString()).toBe('ABABAB') + }) + }) + + describe('byteLength', () => { + it('should return byte length of ASCII string', () => { + expect(nodeBuffer.byteLength('hello')).toBe(5) + }) + + it('should return byte length of UTF-8 string with multi-byte characters', () => { + expect(nodeBuffer.byteLength('🌟')).toBe(4) // Emoji is 4 bytes in UTF-8 + expect(nodeBuffer.byteLength('你好')).toBe(6) // Chinese characters are 3 bytes each + }) + + it('should return 0 for empty string', () => { + expect(nodeBuffer.byteLength('')).toBe(0) + }) + }) + + describe('concat', () => { + it('should concatenate multiple Uint8Arrays', () => { + const arr1 = new Uint8Array([1, 2, 3]) + const arr2 = new Uint8Array([4, 5]) + const arr3 = new Uint8Array([6, 7, 8, 9]) + + const result = nodeBuffer.concat([arr1, arr2, arr3]) + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]) + }) + + it('should handle empty arrays', () => { + const result = nodeBuffer.concat([]) + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(0) + }) + + it('should handle single array', () => { + const arr = new Uint8Array([1, 2, 3]) + const result = nodeBuffer.concat([arr]) + expect(Array.from(result)).toEqual([1, 2, 3]) + }) + }) + + describe('toString', () => { + it('should convert buffer to UTF-8 string', () => { + const buffer = new Uint8Array([72, 101, 108, 108, 111]) // "Hello" + const result = nodeBuffer.toString(buffer, 'utf8') + expect(result).toBe('Hello') + }) + + it('should convert buffer to base64 string', () => { + const buffer = new Uint8Array([72, 101, 108, 108, 111]) // "Hello" + const result = nodeBuffer.toString(buffer, 'base64') + expect(result).toBe('SGVsbG8=') + }) + + it('should handle empty buffer', () => { + const buffer = new Uint8Array([]) + expect(nodeBuffer.toString(buffer, 'utf8')).toBe('') + expect(nodeBuffer.toString(buffer, 'base64')).toBe('') + }) + }) +}) + +describe('nodePlatformProvider', () => { + it('should export platform provider with crypto and buffer', () => { + expect(nodePlatformProvider).toBeDefined() + expect(nodePlatformProvider.crypto).toBeInstanceOf(NodeCrypto) + expect(nodePlatformProvider.buffer).toBeInstanceOf(NodeBuffer) + }) +}) diff --git a/test/platform/web.test.ts b/test/platform/web.test.ts new file mode 100644 index 0000000..838ec1f --- /dev/null +++ b/test/platform/web.test.ts @@ -0,0 +1,371 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { WebBuffer, WebCrypto, WebCryptoAsync, webPlatformProvider } from '../../src/platform/web' + +// Mock global crypto +Object.defineProperty(globalThis, 'crypto', { + value: { + subtle: { + importKey: vi.fn(), + sign: vi.fn(), + decrypt: vi.fn(), + }, + }, + writable: true, +}) + +// Mock global TextEncoder/TextDecoder +globalThis.TextEncoder = vi.fn().mockImplementation(() => ({ + encode: vi.fn().mockImplementation((str: string) => { + return new Uint8Array(str.split('').map(c => c.charCodeAt(0))) + }), +})) + +globalThis.TextDecoder = vi.fn().mockImplementation(() => ({ + decode: vi.fn().mockImplementation((arr: Uint8Array) => { + return String.fromCharCode(...arr) + }), +})) + +// Mock atob/btoa +globalThis.atob = vi.fn().mockImplementation((base64: string) => { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + let result = '' + base64 = base64.replace(/=/g, '') + + for (let i = 0; i < base64.length; i += 4) { + const encoded = (chars.indexOf(base64[i]) << 18) + | (chars.indexOf(base64[i + 1]) << 12) + | (chars.indexOf(base64[i + 2] || 'A') << 6) + | chars.indexOf(base64[i + 3] || 'A') + + result += String.fromCharCode((encoded >> 16) & 0xFF) + if (base64[i + 2]) + result += String.fromCharCode((encoded >> 8) & 0xFF) + if (base64[i + 3]) + result += String.fromCharCode(encoded & 0xFF) + } + + return result +}) + +globalThis.btoa = vi.fn().mockImplementation((str: string) => { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + let result = '' + + for (let i = 0; i < str.length; i += 3) { + const a = str.charCodeAt(i) + const b = i + 1 < str.length ? str.charCodeAt(i + 1) : 0 + const c = i + 2 < str.length ? str.charCodeAt(i + 2) : 0 + + const bitmap = (a << 16) | (b << 8) | c + + result += chars.charAt((bitmap >> 18) & 63) + result += chars.charAt((bitmap >> 12) & 63) + result += i + 1 < str.length ? chars.charAt((bitmap >> 6) & 63) : '=' + result += i + 2 < str.length ? chars.charAt(bitmap & 63) : '=' + } + + return result +}) + +describe('webCrypto', () => { + let webCrypto: WebCrypto + + beforeEach(() => { + webCrypto = new WebCrypto() + vi.clearAllMocks() + }) + + describe('createHmacSha512', () => { + it('should throw error for sync HMAC implementation', () => { + const hmac = webCrypto.createHmacSha512('secret-key') + + // Update should work + expect(() => hmac.update('data')).not.toThrow() + + // Digest should throw + expect(() => hmac.digest()).toThrow('Web Crypto HMAC requires async implementation. Use WebCryptoAsync instead.') + }) + }) + + describe('createDecipherAes256Cbc', () => { + it('should throw error for sync decipher implementation', () => { + expect(() => webCrypto.createDecipherAes256Cbc('key', new Uint8Array(16))) + .toThrow('Web Crypto decryption requires async implementation. Use WebCryptoAsync instead.') + }) + }) +}) + +describe('webCryptoAsync', () => { + let webCryptoAsync: WebCryptoAsync + + beforeEach(() => { + webCryptoAsync = new WebCryptoAsync() + vi.clearAllMocks() + }) + + describe('createHmacSha512', () => { + it('should create async HMAC instance', async () => { + const mockCryptoKey = { type: 'secret' } + const mockSignature = new ArrayBuffer(64) + + globalThis.crypto.subtle.importKey = vi.fn().mockResolvedValue(mockCryptoKey) + globalThis.crypto.subtle.sign = vi.fn().mockResolvedValue(mockSignature) + + const hmac = await webCryptoAsync.createHmacSha512('secret-key') + + expect(crypto.subtle.importKey).toHaveBeenCalledWith( + 'raw', + expect.any(Uint8Array), + { name: 'HMAC', hash: 'SHA-512' }, + false, + ['sign'], + ) + + // Test update + hmac.update('part1') + hmac.update('part2') + + // Test digest + const result = await hmac.digest() + expect(crypto.subtle.sign).toHaveBeenCalledWith('HMAC', mockCryptoKey, expect.any(Uint8Array)) + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(64) + }) + + it('should handle empty updates', async () => { + const mockCryptoKey = { type: 'secret' } + const mockSignature = new ArrayBuffer(64) + + globalThis.crypto.subtle.importKey = vi.fn().mockResolvedValue(mockCryptoKey) + globalThis.crypto.subtle.sign = vi.fn().mockResolvedValue(mockSignature) + + const hmac = await webCryptoAsync.createHmacSha512('key') + + // No updates, just digest + const result = await hmac.digest() + + // Should sign empty data + expect(crypto.subtle.sign).toHaveBeenCalledWith('HMAC', mockCryptoKey, new Uint8Array(0)) + expect(result).toBeInstanceOf(Uint8Array) + }) + }) + + describe('createDecipherAes256Cbc', () => { + it('should create async decipher instance', async () => { + const mockCryptoKey = { type: 'secret' } + const mockDecrypted = new ArrayBuffer(16) + + globalThis.crypto.subtle.importKey = vi.fn().mockResolvedValue(mockCryptoKey) + globalThis.crypto.subtle.decrypt = vi.fn().mockResolvedValue(mockDecrypted) + + const iv = new Uint8Array(16) + const decipher = await webCryptoAsync.createDecipherAes256Cbc('32-byte-key-goes-here-padding!!', iv) + + expect(crypto.subtle.importKey).toHaveBeenCalledWith( + 'raw', + expect.any(Uint8Array), + { name: 'AES-CBC' }, + false, + ['decrypt'], + ) + + // Test update (stores data) + const encrypted1 = new Uint8Array([1, 2, 3, 4]) + const encrypted2 = new Uint8Array([5, 6, 7, 8]) + const updateResult = decipher.update(encrypted1) + expect(updateResult).toEqual(new Uint8Array(0)) // Returns empty for compatibility + + decipher.update(encrypted2) + + // Test final (performs actual decryption) + const result = await decipher.final() + expect(crypto.subtle.decrypt).toHaveBeenCalledWith( + { name: 'AES-CBC', iv }, + mockCryptoKey, + expect.any(Uint8Array), // Combined chunks + ) + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(16) + }) + + it('should handle key truncation to 32 bytes', async () => { + const mockCryptoKey = { type: 'secret' } + globalThis.crypto.subtle.importKey = vi.fn().mockResolvedValue(mockCryptoKey) + + const longKey = 'this-is-a-very-long-key-that-exceeds-32-bytes-length' + const iv = new Uint8Array(16) + await webCryptoAsync.createDecipherAes256Cbc(longKey, iv) + + const importKeyCall = (crypto.subtle.importKey as any).mock.calls[0] + const keyData = importKeyCall[1] as Uint8Array + expect(keyData.length).toBe(32) + }) + }) +}) + +describe('webBuffer', () => { + let webBuffer: WebBuffer + + beforeEach(() => { + webBuffer = new WebBuffer() + vi.clearAllMocks() + }) + + describe('from', () => { + it('should create buffer from string with UTF-8 encoding', () => { + const result = webBuffer.from('hello', 'utf8') + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([104, 101, 108, 108, 111]) + }) + + it('should create buffer from string with base64 encoding', () => { + // Mock atob to return "hello" + globalThis.atob = vi.fn().mockReturnValue('hello') + + const result = webBuffer.from('aGVsbG8=', 'base64') + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([104, 101, 108, 108, 111]) + expect(globalThis.atob).toHaveBeenCalledWith('aGVsbG8=') + }) + + it('should create buffer from string without encoding (default UTF-8)', () => { + const result = webBuffer.from('test') + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([116, 101, 115, 116]) + }) + + it('should create buffer from ArrayBuffer', () => { + const arrayBuffer = new ArrayBuffer(3) + const view = new Uint8Array(arrayBuffer) + view[0] = 1 + view[1] = 2 + view[2] = 3 + + const result = webBuffer.from(arrayBuffer) + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([1, 2, 3]) + }) + + it('should create buffer from Uint8Array', () => { + const uint8Array = new Uint8Array([10, 20, 30]) + const result = webBuffer.from(uint8Array) + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([10, 20, 30]) + }) + }) + + describe('alloc', () => { + it('should allocate buffer with specified size', () => { + const result = webBuffer.alloc(5) + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(5) + expect(Array.from(result)).toEqual([0, 0, 0, 0, 0]) + }) + + it('should allocate buffer with fill value', () => { + const result = webBuffer.alloc(3, 'A') + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(3) + expect(Array.from(result)).toEqual([65, 65, 65]) // 'A' = 65 + }) + + it('should handle multi-byte fill values', () => { + const result = webBuffer.alloc(6, 'AB') + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(6) + expect(Array.from(result)).toEqual([65, 66, 65, 66, 65, 66]) // 'A'=65, 'B'=66 + }) + + it('should handle fill value shorter than buffer size', () => { + const result = webBuffer.alloc(5, 'ABC') + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(5) + expect(Array.from(result)).toEqual([65, 66, 67, 65, 66]) // 'A'=65, 'B'=66, 'C'=67 + }) + }) + + describe('byteLength', () => { + it('should return byte length of ASCII string', () => { + expect(webBuffer.byteLength('hello')).toBe(5) + }) + + it('should return byte length of empty string', () => { + expect(webBuffer.byteLength('')).toBe(0) + }) + + it('should handle special characters', () => { + // Since our mock TextEncoder just uses charCodeAt, it won't handle multi-byte UTF-8 + // In real implementation, emojis would be 4 bytes + expect(webBuffer.byteLength('test')).toBe(4) + }) + }) + + describe('concat', () => { + it('should concatenate multiple Uint8Arrays', () => { + const arr1 = new Uint8Array([1, 2]) + const arr2 = new Uint8Array([3, 4, 5]) + const arr3 = new Uint8Array([6]) + + const result = webBuffer.concat([arr1, arr2, arr3]) + expect(result).toBeInstanceOf(Uint8Array) + expect(Array.from(result)).toEqual([1, 2, 3, 4, 5, 6]) + }) + + it('should handle empty arrays', () => { + const result = webBuffer.concat([]) + expect(result).toBeInstanceOf(Uint8Array) + expect(result.length).toBe(0) + }) + + it('should handle single array', () => { + const arr = new Uint8Array([7, 8, 9]) + const result = webBuffer.concat([arr]) + expect(Array.from(result)).toEqual([7, 8, 9]) + }) + + it('should handle arrays with empty Uint8Arrays', () => { + const arr1 = new Uint8Array([1, 2]) + const arr2 = new Uint8Array([]) + const arr3 = new Uint8Array([3, 4]) + + const result = webBuffer.concat([arr1, arr2, arr3]) + expect(Array.from(result)).toEqual([1, 2, 3, 4]) + }) + }) + + describe('toString', () => { + it('should convert buffer to UTF-8 string', () => { + const buffer = new Uint8Array([72, 101, 108, 108, 111]) // "Hello" + const result = webBuffer.toString(buffer, 'utf8') + expect(result).toBe('Hello') + }) + + it('should convert buffer to base64 string', () => { + // Mock btoa to return expected base64 + globalThis.btoa = vi.fn().mockReturnValue('SGVsbG8=') + + const buffer = new Uint8Array([72, 101, 108, 108, 111]) // "Hello" + const result = webBuffer.toString(buffer, 'base64') + expect(result).toBe('SGVsbG8=') + expect(globalThis.btoa).toHaveBeenCalled() + }) + + it('should handle empty buffer', () => { + const buffer = new Uint8Array([]) + + globalThis.btoa = vi.fn().mockReturnValue('') + + expect(webBuffer.toString(buffer, 'utf8')).toBe('') + expect(webBuffer.toString(buffer, 'base64')).toBe('') + }) + }) +}) + +describe('webPlatformProvider', () => { + it('should export platform provider with crypto and buffer', () => { + expect(webPlatformProvider).toBeDefined() + expect(webPlatformProvider.crypto).toBeInstanceOf(WebCrypto) + expect(webPlatformProvider.buffer).toBeInstanceOf(WebBuffer) + }) +}) diff --git a/test/resources/cards.test.ts b/test/resources/cards.test.ts new file mode 100644 index 0000000..437ede2 --- /dev/null +++ b/test/resources/cards.test.ts @@ -0,0 +1,240 @@ +import type { Card } from '../../src/resources' +import type { XMoneyCore } from '../../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { PaginatedList } from '../../src/core' +import { CardsResource } from '../../src/resources' + +describe('cardsResource', () => { + let mockCore: XMoneyCore + let cardsResource: CardsResource + + const mockCard: Card = { + id: 123, + customerId: 456, + cardStatus: 'active', + type: 'visa', + binInfo: { + bin: '424242', + brand: 'Visa', + type: 'credit', + }, + cardNumber: '4111111111111111', + expiryMonth: '01', + expiryYear: '2999', + cardProvider: 'Test Bank', + } + + beforeEach(() => { + mockCore = { + request: vi.fn(), + } as any + + cardsResource = new CardsResource(mockCore) + }) + + describe('retrieve', () => { + it('should retrieve a card by ID and customer ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockCard, + }) + + const result = await cardsResource.retrieve(123, 456) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/card/123', + query: { customerId: 456 }, + }) + expect(result).toEqual(mockCard) + }) + + it('should handle string IDs', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockCard, + }) + + await cardsResource.retrieve('123' as any, '456' as any) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/card/123', + query: { customerId: '456' }, + }) + }) + }) + + describe('delete', () => { + it('should delete a card by ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await cardsResource.delete(123) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/card/123', + }) + }) + + it('should handle string ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await cardsResource.delete('123' as any) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/card/123', + }) + }) + }) + + describe('list', () => { + it('should list cards without parameters', async () => { + const mockCards = [mockCard] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockCards, + pagination: mockPagination, + }) + + const result = await cardsResource.list() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/card', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockCards) + expect(result.pagination).toEqual(mockPagination) + }) + + it('should list cards with all parameters', async () => { + const params = { + page: 2, + limit: 20, + customerId: 456, + status: 'active' as const, + cardholderName: 'John', + last4: '1234', + fingerprint: 'fp_123', + issuer: 'Test Bank', + verified: true, + cvvVerified: true, + walletType: 'apple_pay' as const, + type: 'visa' as const, + bin: '424242', + issuerCountry: 'US', + issuerCountryAlpha3: 'USA', + issuerCountryNumeric: '840', + tag: 'premium', + searchId: 'search_123', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [mockCard], + pagination: { + currentPageNumber: 2, + pageCount: 5, + totalItemCount: 100, + itemCountPerPage: 20, + }, + }) + + await cardsResource.list(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/card', + query: params, + }) + }) + + it('should handle empty results', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 1, + pageCount: 0, + totalItemCount: 0, + itemCountPerPage: 10, + }, + }) + + const result = await cardsResource.list({ customerId: 999 }) + + expect(result.data).toEqual([]) + expect(result.totalCount).toBe(0) + expect(result.hasMore).toBe(false) + }) + + it('should support pagination with fetchNextPage', async () => { + const firstPageData = [{ ...mockCard, id: 1 }] + const secondPageData = [{ ...mockCard, id: 2 }] + + let callCount = 0 + mockCore.request = vi.fn().mockImplementation(async (options) => { + callCount++ + if (callCount === 1 || options.query?.page === 1) { + return { + data: firstPageData, + pagination: { + currentPageNumber: 1, + pageCount: 2, + totalItemCount: 2, + itemCountPerPage: 1, + }, + } + } + return { + data: secondPageData, + pagination: { + currentPageNumber: 2, + pageCount: 2, + totalItemCount: 2, + itemCountPerPage: 1, + }, + } + }) + + const result = await cardsResource.list({ perPage: 1 }) + + expect(result.hasMore).toBe(true) + expect(result.data).toEqual(firstPageData) + + // Test async iteration + const allItems = [] + for await (const item of result) { + allItems.push(item) + } + + expect(allItems).toHaveLength(2) + expect(allItems[0].id).toBe(1) + expect(allItems[1].id).toBe(2) + }) + + it('should pass through tag parameter', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 1, + pageCount: 0, + totalItemCount: 0, + itemCountPerPage: 10, + }, + }) + + await cardsResource.list({ cardStatus: 'all' }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/card', + query: { cardStatus: 'all' }, + }) + }) + }) +}) diff --git a/test/resources/checkout.test.ts b/test/resources/checkout.test.ts new file mode 100644 index 0000000..5d76f0b --- /dev/null +++ b/test/resources/checkout.test.ts @@ -0,0 +1,534 @@ +import type { PlatformProvider } from '../../src/platform/types' +import type { CheckoutCreateFormParams, CheckoutCreateParams, CheckoutResponse } from '../../src/resources' +import type { XMoneyCore } from '../../src/types' +import { Buffer } from 'node:buffer' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { CheckoutResource } from '../../src/resources' + +describe('checkoutResource', () => { + let mockCore: XMoneyCore + let mockPlatform: PlatformProvider + let checkoutResource: CheckoutResource + + beforeEach(() => { + mockCore = { + config: { + apiKey: 'test-api-key-32-chars-padding!!!', + }, + request: vi.fn(), + } as any + + mockPlatform = { + buffer: { + from: vi.fn((input: string, encoding?: string) => { + if (encoding === 'base64') { + return Buffer.from(input, 'base64') + } + return Buffer.from(input, 'utf8') + }), + toString: vi.fn((buffer: Uint8Array, encoding: string) => { + return Buffer.from(buffer).toString(encoding as any) + }), + concat: vi.fn((arrays: Uint8Array[]) => { + return Buffer.concat(arrays.map(arr => Buffer.from(arr))) + }), + alloc: vi.fn(), + byteLength: vi.fn(), + }, + crypto: { + createHmacSha512: vi.fn(() => ({ + update: vi.fn(), + digest: vi.fn(() => Buffer.from('mock-hmac-digest')), + })), + createDecipherAes256Cbc: vi.fn(() => ({ + update: vi.fn((_data: Uint8Array) => Buffer.from('decrypted-')), + final: vi.fn(() => Buffer.from('data')), + })), + }, + } as any + + checkoutResource = new CheckoutResource(mockCore, mockPlatform) + }) + + describe('create', () => { + const validParams: CheckoutCreateParams = { + publicKey: 'pk_test_site123', + cardTransactionMode: 'authAndCapture', + backUrl: 'https://example.com/callback', + customer: { + identifier: 'cust_123', + firstName: 'John', + lastName: 'Doe', + email: 'john@example.com', + phone: '+1234567890', + country: 'US', + city: 'New York', + tags: ['vip', 'frequent'], + }, + order: { + orderId: 'order_123', + type: 'purchase', + amount: 10000, + currency: 'USD', + description: 'Test order', + }, + invoiceEmail: 'invoice@example.com', + saveCard: true, + } + + it('should create checkout with valid parameters', () => { + const result = checkoutResource.create(validParams) + + expect(result).toHaveProperty('payload') + expect(result).toHaveProperty('checksum') + expect(typeof result.payload).toBe('string') + expect(typeof result.checksum).toBe('string') + + // Verify the payload is base64 encoded + const decodedPayload = Buffer.from(result.payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + + expect(payloadData).toMatchObject({ + siteId: 'site123', + saveCard: true, + cardTransactionMode: 'authAndCapture', + backUrl: 'https://example.com/callback', + customer: validParams.customer, + order: validParams.order, + invoiceEmail: 'invoice@example.com', + }) + + // Verify HMAC was called + expect(mockPlatform.crypto.createHmacSha512).toHaveBeenCalledWith('test-api-key-32-chars-padding!!!') + }) + + it('should extract siteId from publicKey', () => { + const result = checkoutResource.create({ + ...validParams, + publicKey: 'pk_live_mysite456', + }) + + const decodedPayload = Buffer.from(result.payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + + expect(payloadData.siteId).toBe('mysite456') + }) + + it('should default saveCard to false if not provided', () => { + const params = { ...validParams } + delete params.saveCard + + const result = checkoutResource.create(params) + + const decodedPayload = Buffer.from(result.payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + + expect(payloadData.saveCard).toBe(false) + }) + + it('should work with minimal required parameters', () => { + const minimalParams: CheckoutCreateParams = { + publicKey: 'pk_test_site789', + cardTransactionMode: 'auth', + backUrl: 'https://example.com/back', + customer: { + identifier: 'cust_min', + }, + order: { + orderId: 'order_min', + type: 'purchase', + amount: 1000, + currency: 'EUR', + description: 'Minimal order', + }, + } + + const result = checkoutResource.create(minimalParams) + + expect(result).toHaveProperty('payload') + expect(result).toHaveProperty('checksum') + + const decodedPayload = Buffer.from(result.payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + + expect(payloadData).toMatchObject({ + siteId: 'site789', + saveCard: false, + cardTransactionMode: 'auth', + backUrl: 'https://example.com/back', + customer: { identifier: 'cust_min' }, + order: minimalParams.order, + }) + }) + + it('should throw error if publicKey is missing', () => { + const params = { ...validParams, publicKey: '' } + + expect(() => checkoutResource.create(params)) + .toThrow('Public key is required for hosted checkout') + }) + + it('should throw error if publicKey has invalid format', () => { + const params = { ...validParams, publicKey: 'invalid_key_format' } + + expect(() => checkoutResource.create(params)) + .toThrow('Invalid public key format. Expected: pk__') + }) + + it('should accept pk_test_ prefix', () => { + const params = { ...validParams, publicKey: 'pk_test_validsite' } + + expect(() => checkoutResource.create(params)).not.toThrow() + }) + + it('should accept pk_live_ prefix', () => { + const params = { ...validParams, publicKey: 'pk_live_validsite' } + + expect(() => checkoutResource.create(params)).not.toThrow() + }) + }) + + describe('form', () => { + const validParams: CheckoutCreateFormParams = { + publicKey: 'pk_test_site123', + cardTransactionMode: 'authAndCapture', + backUrl: 'https://example.com/callback', + customer: { + identifier: 'cust_123', + email: 'john@example.com', + }, + order: { + orderId: 'order_123', + type: 'purchase', + amount: 10000, + currency: 'USD', + description: 'Test order', + }, + } + + it('should generate form HTML with default URL', () => { + const html = checkoutResource.form(validParams) + + expect(html).toContain('
{ + const html = checkoutResource.form({ + ...validParams, + url: 'https://custom-checkout.example.com', + }) + + expect(html).toContain('action="https://custom-checkout.example.com"') + }) + + it('should handle null URL and use default', () => { + const html = checkoutResource.form({ + ...validParams, + url: null, + }) + + expect(html).toContain('action="https://secure.twispay.com"') + }) + + it('should include valid payload and checksum in form', () => { + const html = checkoutResource.form(validParams) + + // Extract payload and checksum from HTML + const payloadMatch = html.match(/name="jsonRequest" value="([^"]+)"/) + const checksumMatch = html.match(/name="checksum" value="([^"]+)"/) + + expect(payloadMatch).toBeTruthy() + expect(checksumMatch).toBeTruthy() + + // Verify payload is valid base64 + const payload = payloadMatch![1] + expect(() => Buffer.from(payload, 'base64')).not.toThrow() + + // Verify payload contains expected data + const decodedPayload = Buffer.from(payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + expect(payloadData.siteId).toBe('site123') + }) + }) + + describe('decrypt', () => { + it('should decrypt valid response', () => { + const mockIv = Buffer.from('1234567890123456') + const mockEncrypted = Buffer.from('encrypted-data') + const encryptedResponse = `${mockIv.toString('base64')},${mockEncrypted.toString('base64')}` + + // Mock the decryption to return valid JSON + const expectedResponse: CheckoutResponse = { + orderId: 12345, + customerId: 54321, + identifier: 'cust_123', + transactionId: 67890, + transactionStatus: 'complete-ok', + amount: 10000, + currency: 'USD', + externalOrderId: 'ext-order-123', + transactionMethod: 'card', + customData: null, + customFields: null, + timestamp: 0, + cardId: undefined, + } + + mockPlatform.buffer.concat = vi.fn(() => + Buffer.from(JSON.stringify(expectedResponse)), + ) + + const result = checkoutResource.decrypt(encryptedResponse) + + expect(result).toEqual(expectedResponse) + expect(mockPlatform.crypto.createDecipherAes256Cbc).toHaveBeenCalledWith( + 'test-api-key-32-chars-padding!!!', + expect.any(Uint8Array), + ) + }) + + it('should handle response with additional fields', () => { + const mockIv = Buffer.from('1234567890123456') + const mockEncrypted = Buffer.from('encrypted-data') + const encryptedResponse = `${mockIv.toString('base64')},${mockEncrypted.toString('base64')}` + + const expectedResponse: CheckoutResponse = { + orderId: 12345, + transactionId: 67890, + transactionStatus: 'complete-ok', + amount: 10000, + currency: 'USD', + externalOrderId: '', + transactionMethod: '', + customerId: 0, + identifier: '', + customData: { key: 'value' }, + customFields: { customField: 'custom-value' }, + timestamp: 0, + cardId: undefined, + } + + mockPlatform.buffer.concat = vi.fn(() => + Buffer.from(JSON.stringify(expectedResponse)), + ) + + const result = checkoutResource.decrypt(encryptedResponse) + + expect(result).toEqual(expectedResponse) + expect(result.customFields).toStrictEqual(expectedResponse.customFields) + expect(result.customData).toEqual({ key: 'value' }) + }) + + it('should throw error for invalid format (missing comma)', () => { + const invalidResponse = 'base64stringwithoutcomma' + + expect(() => checkoutResource.decrypt(invalidResponse)) + .toThrow('Invalid encrypted response format') + }) + + it('should throw error for empty IV', () => { + const invalidResponse = ',somebase64data' + + expect(() => checkoutResource.decrypt(invalidResponse)) + .toThrow('Invalid encrypted response format') + }) + + it('should throw error for empty encrypted data', () => { + const invalidResponse = 'somebase64iv,' + + expect(() => checkoutResource.decrypt(invalidResponse)) + .toThrow('Invalid encrypted response format') + }) + + it('should throw error for invalid JSON after decryption', () => { + const mockIv = Buffer.from('1234567890123456') + const mockEncrypted = Buffer.from('encrypted-data') + const encryptedResponse = `${mockIv.toString('base64')},${mockEncrypted.toString('base64')}` + + mockPlatform.buffer.concat = vi.fn(() => + Buffer.from('invalid-json-{'), + ) + + expect(() => checkoutResource.decrypt(encryptedResponse)) + .toThrow('Failed to parse decrypted response') + }) + + it('should handle empty JSON object', () => { + const mockIv = Buffer.from('1234567890123456') + const mockEncrypted = Buffer.from('encrypted-data') + const encryptedResponse = `${mockIv.toString('base64')},${mockEncrypted.toString('base64')}` + + mockPlatform.buffer.concat = vi.fn(() => + Buffer.from('{}'), + ) + + const result = checkoutResource.decrypt(encryptedResponse) + + expect(result).toEqual({}) + }) + }) + + describe('generateChecksum', () => { + it('should generate checksum using HMAC-SHA512', () => { + const mockHmac = { + update: vi.fn(), + digest: vi.fn(() => Buffer.from('test-digest')), + } + + mockPlatform.crypto.createHmacSha512 = vi.fn(() => mockHmac) + + // Access private method through create + const result = checkoutResource.create({ + publicKey: 'pk_test_site123', + cardTransactionMode: 'auth', + backUrl: 'https://example.com', + customer: { identifier: 'test' }, + order: { + orderId: 'test', + type: 'purchase', + amount: 1000, + currency: 'USD', + description: 'Test', + }, + }) + + expect(mockPlatform.crypto.createHmacSha512).toHaveBeenCalledWith('test-api-key-32-chars-padding!!!') + expect(mockHmac.update).toHaveBeenCalled() + expect(mockHmac.digest).toHaveBeenCalled() + expect(result.checksum).toBe('dGVzdC1kaWdlc3Q=') // base64 of 'test-digest' + }) + }) + + describe('integration scenarios', () => { + it('should handle complete checkout flow', () => { + // Step 1: Create checkout + const checkoutParams: CheckoutCreateParams = { + publicKey: 'pk_live_production_site', + cardTransactionMode: 'authAndCapture', + backUrl: 'https://shop.example.com/payment/return', + invoiceEmail: 'billing@example.com', + saveCard: true, + customer: { + identifier: 'customer_12345', + firstName: 'Jane', + lastName: 'Smith', + email: 'jane.smith@example.com', + phone: '+44123456789', + country: 'GB', + city: 'London', + tags: ['premium', 'uk-customer'], + }, + order: { + orderId: 'ORD-2025-001', + type: 'purchase', + amount: 25000, // £250.00 + currency: 'GBP', + description: 'Premium subscription - 1 year', + }, + } + + const createResult = checkoutResource.create(checkoutParams) + expect(createResult.payload).toBeTruthy() + expect(createResult.checksum).toBeTruthy() + + // Step 2: Generate form + const formHtml = checkoutResource.form(checkoutParams) + expect(formHtml).toContain(' + Buffer.from(JSON.stringify(mockResponse)), + ) + + const encryptedResponse = 'mock-iv-base64,mock-encrypted-base64' + const decryptedResponse = checkoutResource.decrypt(encryptedResponse) + + expect(decryptedResponse.transactionStatus).toBe('complete-ok') + expect(decryptedResponse.transactionId).toBe(54321) + }) + + it('should handle recurring payment setup', () => { + const recurringParams: CheckoutCreateParams = { + publicKey: 'pk_test_recurring_site', + cardTransactionMode: 'auth', + backUrl: 'https://saas.example.com/subscription/callback', + saveCard: true, // Important for recurring + customer: { + identifier: 'subscriber_789', + email: 'subscriber@example.com', + }, + order: { + orderId: 'SUB-2025-001', + type: 'recurring', + amount: 999, // $9.99 + currency: 'USD', + description: 'Monthly subscription', + }, + } + + const result = checkoutResource.create(recurringParams) + + const decodedPayload = Buffer.from(result.payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + + expect(payloadData.order.type).toBe('recurring') + expect(payloadData.saveCard).toBe(true) + }) + + it('should handle credit transaction', () => { + const creditParams: CheckoutCreateParams = { + publicKey: 'pk_live_credit_site', + cardTransactionMode: 'credit', + backUrl: 'https://store.example.com/refund/complete', + customer: { + identifier: 'refund_customer_456', + }, + order: { + orderId: 'REFUND-2025-001', + type: 'credit', + amount: 5000, // $50.00 refund + currency: 'USD', + description: 'Order refund - damaged goods', + }, + } + + const result = checkoutResource.create(creditParams) + + const decodedPayload = Buffer.from(result.payload, 'base64').toString('utf8') + const payloadData = JSON.parse(decodedPayload) + + expect(payloadData.cardTransactionMode).toBe('credit') + expect(payloadData.order.type).toBe('credit') + }) + }) +}) diff --git a/test/resources/customers.test.ts b/test/resources/customers.test.ts new file mode 100644 index 0000000..fc2c262 --- /dev/null +++ b/test/resources/customers.test.ts @@ -0,0 +1,349 @@ +import type { Customer, CustomerCreateParams, CustomerUpdateParams } from '../../src/resources' +import type { XMoneyCore } from '../../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { PaginatedList, SearchResult } from '../../src/core' +import { CustomersResource } from '../../src/resources' + +describe('customersResource', () => { + let mockCore: XMoneyCore + let customersResource: CustomersResource + + const mockCustomer: Customer = { + id: 123, + siteId: 456, + identifier: 'ref_123', + firstName: 'John', + lastName: 'Doe', + country: 'US', + state: 'CA', + city: 'San Francisco', + zipCode: '94105', + address: '123 Market St', + phone: '+14155551234', + email: 'john.doe@example.com', + isWhitelisted: 0, + isWhitelistedUntil: undefined, + creationDate: `${new Date('2025-01-01').toISOString().slice(0, -5)}+00:00`, + creationTimestamp: new Date('2025-01-01').getTime(), + tags: [ + { + tag: 'premium', + creationDate: `${new Date('2025-01-01').toISOString().slice(0, -5)}+00:00`, + creationTimestamp: new Date('2025-01-01').getTime(), + }, + ], + } + + beforeEach(() => { + mockCore = { + request: vi.fn(), + } as any + + customersResource = new CustomersResource(mockCore) + }) + + describe('create', () => { + it('should create a customer with required fields', async () => { + const params: CustomerCreateParams = { + email: 'test@example.com', + firstName: 'Test', + lastName: 'User', + identifier: 'ref_456', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { id: 789 }, + }) + + const result = await customersResource.create(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/customer', + body: params, + }) + expect(result).toEqual({ id: 789 }) + }) + + it('should create a customer with all fields', async () => { + const params: CustomerCreateParams = { + email: 'test@example.com', + firstName: 'Test', + lastName: 'User', + identifier: 'ref_456', + tag: ['web'], + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { id: 999 }, + }) + + const result = await customersResource.create(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/customer', + body: params, + }) + expect(result).toEqual({ id: 999 }) + }) + }) + + describe('retrieve', () => { + it('should retrieve a customer by ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockCustomer, + }) + + const result = await customersResource.retrieve(123) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/customer/123', + }) + expect(result).toEqual(mockCustomer) + }) + + it('should handle string ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockCustomer, + }) + + await customersResource.retrieve('123' as any) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/customer/123', + }) + }) + }) + + describe('update', () => { + it('should update a customer', async () => { + const params: CustomerUpdateParams = { + email: 'newemail@example.com', + firstName: 'Jane', + lastName: 'Smith', + } + + mockCore.request = vi.fn().mockResolvedValue({}) + + await customersResource.update(123, params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/customer/123', + body: params, + }) + }) + + it('should update with tags', async () => { + const params: CustomerUpdateParams = { + identifier: 'new_ref', + tag: ['vip'], + } + + mockCore.request = vi.fn().mockResolvedValue({}) + + await customersResource.update(456, params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/customer/456', + body: params, + }) + }) + + it('should handle empty update', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await customersResource.update(123, {}) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/customer/123', + body: {}, + }) + }) + }) + + describe('delete', () => { + it('should delete a customer', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await customersResource.delete(123) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/customer/123', + }) + }) + + it('should handle string ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await customersResource.delete('123' as any) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/customer/123', + }) + }) + }) + + describe('list', () => { + it('should list customers without parameters', async () => { + const mockCustomers = [mockCustomer] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockCustomers, + pagination: mockPagination, + }) + + const result = await customersResource.list() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/customer', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockCustomers) + }) + + it('should list customers with all parameters', async () => { + const params = { + page: 2, + limit: 20, + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + referenceId: 'ref_123', + status: 'active' as const, + tag: 'premium', + searchId: 'search_123', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [mockCustomer], + pagination: { + currentPageNumber: 2, + pageCount: 5, + totalItemCount: 100, + itemCountPerPage: 20, + }, + }) + + await customersResource.list(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/customer', + query: params, + }) + }) + }) + + describe('search', () => { + it('should search customers', async () => { + const searchParams = { + email: 'john@example.com', + firstName: 'John', + lastName: 'Doe', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_456' }, + }) + + const result = await customersResource.search(searchParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/customer-search', + body: searchParams, + }) + expect(result).toBeInstanceOf(SearchResult) + expect(result.searchId).toBe('search_456') + }) + + it('should fetch search results', async () => { + const searchParams = { email: 'test@example.com' } + + mockCore.request = vi.fn() + .mockResolvedValueOnce({ data: { searchId: 'search_789' } }) + .mockResolvedValueOnce({ + data: [mockCustomer], + pagination: { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + }, + searchParams, + }) + + const searchResult = await customersResource.search(searchParams) + const paginatedList = await searchResult.fetch() + + expect(paginatedList).toBeInstanceOf(PaginatedList) + expect(paginatedList.data).toEqual([mockCustomer]) + expect(mockCore.request).toHaveBeenCalledTimes(2) + expect(mockCore.request).toHaveBeenLastCalledWith({ + method: 'GET', + path: '/customer', + query: { searchId: 'search_789' }, + }) + }) + + it('should handle search with all parameters', async () => { + const searchParams = { + email: 'john@example.com', + firstName: 'John', + lastName: 'Doe', + referenceId: 'ref_123', + status: 'active' as const, + tag: 'vip', + limit: 50, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_complex' }, + }) + + await customersResource.search(searchParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/customer-search', + body: searchParams, + }) + }) + + it('should exclude searchId and page from search params', async () => { + // TypeScript should prevent this, but testing runtime behavior + const invalidParams = { + email: 'test@example.com', + searchId: 'should_be_ignored', + page: 5, + } as any + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_new' }, + }) + + await customersResource.search(invalidParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/customer-search', + body: invalidParams, + }) + }) + }) +}) diff --git a/test/resources/notifications.test.ts b/test/resources/notifications.test.ts new file mode 100644 index 0000000..8f1f051 --- /dev/null +++ b/test/resources/notifications.test.ts @@ -0,0 +1,338 @@ +import type { Notification } from '../../src/resources' +import type { XMoneyCore } from '../../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { PaginatedList } from '../../src/core' +import { NotificationsResource } from '../../src/resources' + +describe('notificationsResource', () => { + let mockCore: XMoneyCore + let notificationsResource: NotificationsResource + + const mockNotification: Notification = { + id: 5001, + siteId: 456, + resourceId: 1001, + resourceType: 'transaction', + message: 'transactionCapture', + creationDate: `${new Date('2025-01-01').toISOString().slice(0, -5)}+00:00`, + creationTimestamp: new Date('2025-01-01').getTime(), + } + + beforeEach(() => { + mockCore = { + request: vi.fn(), + } as any + + notificationsResource = new NotificationsResource(mockCore) + }) + + describe('list', () => { + it('should list notifications without parameters', async () => { + const mockNotifications = [mockNotification] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockNotifications, + pagination: mockPagination, + }) + + const result = await notificationsResource.list() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockNotifications) + expect(result.pagination).toEqual(mockPagination) + }) + + it('should list notifications with all parameters', async () => { + const params = { + page: 2, + limit: 25, + type: 'webhook' as const, + event: 'transaction.successful', + status: 'successful' as const, + url: 'https://example.com/webhooks', + searchId: 'search_notif_123', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [mockNotification], + pagination: { + currentPageNumber: 2, + pageCount: 5, + totalItemCount: 125, + itemCountPerPage: 25, + }, + }) + + await notificationsResource.list(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification', + query: params, + }) + }) + + it('should handle empty results', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 1, + pageCount: 0, + totalItemCount: 0, + itemCountPerPage: 10, + }, + }) + + const result = await notificationsResource.list({ message: ['transactionFail'] }) + + expect(result.data).toEqual([]) + expect(result.totalCount).toBe(0) + expect(result.hasMore).toBe(false) + }) + + it('should handle different notification statuses', async () => { + const statuses = ['orderNew', 'orderChange', 'orderExtend', 'orderRetrying', 'orderInProgress', 'orderCancel', 'transactionNew', 'transactionCapture', 'transactionFail'] as const + + for (const message of statuses) { + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 1, + pageCount: 0, + totalItemCount: 0, + itemCountPerPage: 10, + }, + }) + + await notificationsResource.list({ message: [message] }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification', + query: { message: [message] }, + }) + } + }) + + it('should support pagination', async () => { + const firstPageData = [{ ...mockNotification, id: 1 }] + const secondPageData = [{ ...mockNotification, id: 2 }] + + let callCount = 0 + mockCore.request = vi.fn().mockImplementation(async (options) => { + callCount++ + if (callCount === 1 || options.query?.page === 1) { + return { + data: firstPageData, + pagination: { + currentPageNumber: 1, + pageCount: 2, + totalItemCount: 2, + itemCountPerPage: 1, + }, + } + } + return { + data: secondPageData, + pagination: { + currentPageNumber: 2, + pageCount: 2, + totalItemCount: 2, + itemCountPerPage: 1, + }, + } + }) + + const result = await notificationsResource.list({ perPage: 1 }) + + expect(result.hasMore).toBe(true) + expect(result.data).toEqual(firstPageData) + + // Test async iteration + const allItems = [] + for await (const item of result) { + allItems.push(item) + } + + expect(allItems).toHaveLength(2) + expect(allItems[0].id).toBe(1) + expect(allItems[1].id).toBe(2) + }) + }) + + describe('listForOrders', () => { + it('should list notifications for orders without parameters', async () => { + const mockNotifications = [mockNotification] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockNotifications, + pagination: mockPagination, + }) + + const result = await notificationsResource.listForOrders() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification-for-order', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockNotifications) + }) + + it('should list notifications for orders with parameters', async () => { + const params = { + page: 3, + limit: 50, + type: 'email' as const, + event: 'order.created', + status: 'failed' as const, + url: 'https://api.example.com/orders', + searchId: 'search_orders_notif', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 3, + pageCount: 3, + totalItemCount: 150, + itemCountPerPage: 50, + }, + }) + + await notificationsResource.listForOrders(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification-for-order', + query: params, + }) + }) + }) + + describe('listForTransactions', () => { + it('should list notifications for transactions without parameters', async () => { + const mockNotifications = [mockNotification] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockNotifications, + pagination: mockPagination, + }) + + const result = await notificationsResource.listForTransactions() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification-for-transaction', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockNotifications) + }) + + it('should list notifications for transactions with parameters', async () => { + const params = { + page: 1, + limit: 100, + type: 'webhook' as const, + event: 'transaction.refunded', + status: 'pending' as const, + url: 'https://webhook.site/test', + searchId: 'search_trans_notif', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [mockNotification], + pagination: { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 100, + }, + }) + + await notificationsResource.listForTransactions(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification-for-transaction', + query: params, + }) + }) + + it('should handle failed notification with retry info', async () => { + const failedNotification: Notification = { + ...mockNotification, + message: 'transactionFail', + // response: { + // status: 500, + // headers: {}, + // body: 'Internal Server Error', + // }, + } + + mockCore.request = vi.fn().mockResolvedValue({ + code: 500, + data: [failedNotification], + pagination: { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + }, + }) + + const result = await notificationsResource.listForTransactions({ message: ['transactionFail'] }) + + expect(result.data[0].message).toBe('transactionFail') + // expect(result.data[0].attempts).toBe(3) + // expect(result.data[0].nextAttemptAt).toEqual(new Date('2025-01-01T13:00:00Z')) + // expect(result.data[0].response?.status).toBe(500) + }) + + it('should handle different event types', async () => { + const messages = ['orderNew', 'orderChange', 'orderExtend', 'orderRetrying', 'orderInProgress', 'orderCancel', 'transactionNew', 'transactionCapture', 'transactionFail'] as const + + for (const message of messages) { + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 1, + pageCount: 0, + totalItemCount: 0, + itemCountPerPage: 10, + }, + }) + + await notificationsResource.list({ message: [message] }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/notification', + query: { message: [message] }, + }) + } + }) + }) +}) diff --git a/test/resources/orders.test.ts b/test/resources/orders.test.ts new file mode 100644 index 0000000..54a740e --- /dev/null +++ b/test/resources/orders.test.ts @@ -0,0 +1,440 @@ +import type { CreateOrderResponse, Order, OrderCreateParams, OrderListParams, OrderRebillParams, OrderUpdateCardParams } from '../../src/resources' +import type { XMoneyCore } from '../../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { PaginatedList, SearchResult } from '../../src/core' +import { OrdersResource } from '../../src/resources' + +describe('ordersResource', () => { + let mockCore: XMoneyCore + let ordersResource: OrdersResource + + const mockOrder: Order = { + id: 12345, + siteId: 456, + customerId: 789, + externalOrderId: 'ref_12345', + orderType: 'purchase', + orderStatus: 'complete-ok', + amount: 10000, + currency: 'USD', + description: 'Test order description', + invoiceEmail: 'customer@example.com', + createdAt: `${new Date('2025-01-01').toISOString().slice(0, -5)}+00:00`, + intervalType: 'month', + intervalValue: 1, + retryPayment: 'P1M,P2M', + nextDueDate: `${new Date('2025-02-01').toISOString().slice(0, -5)}+00:00`, + transactionMethod: 'card', + tags: [ + { + tag: 'web', + creationDate: `${new Date('2025-01-01').toISOString().slice(0, -5)}+00:00`, + creationTimestamp: 1735689600, + }, + ], + } + + beforeEach(() => { + mockCore = { + request: vi.fn(), + } as any + + ordersResource = new OrdersResource(mockCore) + }) + + describe('create', () => { + it('should create an order with required fields', async () => { + const params: OrderCreateParams = { + customerId: 789, + ip: '127.0.0.1', + amount: 5000, + currency: 'USD', + orderType: 'purchase', + externalOrderId: 'ref_new_order', + } + + const mockResponse: CreateOrderResponse = { + orderId: 54321, + transactionId: 2001, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockResponse, + }) + + const result = await ordersResource.create(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/order', + body: params, + }) + expect(result).toEqual(mockResponse) + }) + + it('should create an order with all fields', async () => { + const params: OrderCreateParams = { + amount: 10000, + currency: 'EUR', + externalOrderId: 'ref_full_order', + orderType: 'recurring', + customerId: 789, + invoiceEmail: 'customer@example.com', + cardHolderName: 'Jane Smith', + cardId: '111', + ip: '10.0.0.1', + saveCard: true, + externalCustomData: JSON.stringify({ orderId: 'internal_123' }), + } + + const mockResponse: CreateOrderResponse = { + orderId: 99999, + transactionId: 3001, + cardId: 222, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockResponse, + }) + + const result = await ordersResource.create(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/order', + body: params, + }) + expect(result).toEqual(mockResponse) + }) + }) + + describe('retrieve', () => { + it('should retrieve an order by numeric ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockOrder, + }) + + const result = await ordersResource.retrieve(12345) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/order/12345', + }) + expect(result).toEqual(mockOrder) + }) + + it('should retrieve an order by string orderId', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockOrder, + }) + + const result = await ordersResource.retrieve('order_12345') + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/order/order_12345', + }) + expect(result).toEqual(mockOrder) + }) + }) + + describe('cancel', () => { + it('should cancel an order without parameters', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await ordersResource.cancel(12345) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/order/12345', + }) + }) + + it('should cancel an order with refund parameters', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await ordersResource.cancel('order_12345', { + reason: 'fraud-confirm', + message: 'Suspicious activity detected', + terminateOrder: 'yes', + }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/order/order_12345', + body: { + reason: 'fraud-confirm', + message: 'Suspicious activity detected', + terminateOrder: 'yes', + }, + }) + }) + }) + + describe('rebill', () => { + it('should rebill an order', async () => { + const params: OrderRebillParams = { + customerId: 789, + amount: 7500, + transactionOption: JSON.stringify({ + digitalWallet: { + walletType: 'googlePay', + data: 'eyJtZXNzYWdlSWQiOiJBUEEwMV8xMjM0NTY3ODkwIiwiZW5jcnlwdGVkTWVzc2FnZSI6InNvbWVFbmNyeXB0ZWREYXRhIn0=', + }, + isSoftDecline: 'yes', + splitPayment: { + splitSchema: [ + { + toSite: 1001, + amount: 150.50, + description: 'Payment for merchant A', + tag: ['electronics', 'online-store', 'premium'], + }, + { + toSite: 1002, + amount: 49.99, + description: 'Payment for merchant B', + tag: ['books', 'education'], + }, + ], + }, + }), + } + + const mockResponse = { + id: 67890, + transactionId: 4001, + cardId: 333, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockResponse, + }) + + const result = await ordersResource.rebill(12345, params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PATCH', + path: '/order-rebill/12345', + body: params, + }) + expect(result).toEqual(mockResponse) + }) + + it('should rebill with minimal parameters', async () => { + const params: OrderRebillParams = { + customerId: 789, + amount: 2000, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { id: 11111 }, + }) + + await ordersResource.rebill('order_12345', params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PATCH', + path: '/order-rebill/order_12345', + body: params, + }) + }) + }) + + describe('updateCard', () => { + it('should update card for an order', async () => { + const params: OrderUpdateCardParams = { + customerId: '789', + ip: '10.0.0.1', + amount: 6000, + currency: 'USD', + cardNumber: '4111111111111111', + cardExpiryDate: '01/99', + cardCvv: '123', + transactionDescription: 'Order update card', + cardHolderName: 'Alice Johnson', + cardHolderCountry: 'US', + cardHolderState: 'CA', + cardType: 'visa', + cardDescriptor: 'Order Update', + } + + const mockResponse = { + id: 88888, + transactionId: 5001, + cardId: 444, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockResponse, + }) + + const result = await ordersResource.updateCard(12345, params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PATCH', + path: '/order-update-card/12345', + body: params, + }) + expect(result).toEqual(mockResponse) + }) + + it('should update card with minimal parameters', async () => { + const params: OrderUpdateCardParams = { + customerId: '789', + ip: '10.0.0.1', + amount: 3000, + currency: 'USD', + cardNumber: '4111111111111111', + cardExpiryDate: '01/99', + cardCvv: '123', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { id: 99999 }, + }) + + await ordersResource.updateCard('order_12345', params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PATCH', + path: '/order-update-card/order_12345', + body: params, + }) + }) + }) + + describe('list', () => { + it('should list orders without parameters', async () => { + const mockOrders = [mockOrder] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockOrders, + pagination: mockPagination, + }) + + const result = await ordersResource.list() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/order', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockOrders) + }) + + it('should list orders with all parameters', async () => { + const params = { + page: 3, + limit: 50, + customerId: 789, + cardId: 111, + referenceId: 'ref_search', + status: 'complete' as const, + valid: true, + cancelled: false, + type: 'single_payment' as const, + tag: 'web', + searchId: 'search_orders_123', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [mockOrder], + pagination: { + currentPageNumber: 3, + pageCount: 10, + totalItemCount: 500, + itemCountPerPage: 50, + }, + }) + + await ordersResource.list(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/order', + query: params, + }) + }) + }) + + describe('search', () => { + it('should search orders', async () => { + const searchParams = { + customerId: 789, + status: 'complete' as const, + type: 'single_payment' as const, + limit: 25, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_orders_456' }, + }) + + const result = await ordersResource.search(searchParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/order-search', + body: searchParams, + }) + expect(result).toBeInstanceOf(SearchResult) + expect(result.searchId).toBe('search_orders_456') + }) + + it('should fetch search results', async () => { + const searchParams = { externalOrderId: 'ref_pattern' } + + mockCore.request = vi.fn() + .mockResolvedValueOnce({ data: { searchId: 'search_orders_789' } }) + .mockResolvedValueOnce({ + data: [mockOrder], + pagination: { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + }, + searchParams, + }) + + const searchResult = await ordersResource.search(searchParams) + const paginatedList = await searchResult.fetch() + + expect(paginatedList).toBeInstanceOf(PaginatedList) + expect(paginatedList.data).toEqual([mockOrder]) + expect(mockCore.request).toHaveBeenCalledTimes(2) + expect(mockCore.request).toHaveBeenLastCalledWith({ + method: 'GET', + path: '/order', + query: { searchId: 'search_orders_789' }, + }) + }) + + it('should handle search with date filters', async () => { + const searchParams: OrderListParams = { + createdAtFrom: new Date('2025-01-01'), + createdAtTo: new Date('2025-12-31'), + tag: 'seasonal', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_date_range' }, + }) + + await ordersResource.search(searchParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/order-search', + body: searchParams, + }) + }) + }) +}) diff --git a/test/resources/transactions.test.ts b/test/resources/transactions.test.ts new file mode 100644 index 0000000..06936c8 --- /dev/null +++ b/test/resources/transactions.test.ts @@ -0,0 +1,401 @@ +import type { Transaction, TransactionListParams, TransactionSummary } from '../../src/resources' +import type { XMoneyCore } from '../../src/types' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { PaginatedList, SearchResult } from '../../src/core' +import { TransactionsResource } from '../../src/resources' + +describe('transactionsResource', () => { + let mockCore: XMoneyCore + let transactionsResource: TransactionsResource + + const mockTransactionSummary: TransactionSummary = { + id: 1001, + siteId: 456, + orderId: 12345, + customerId: 789, + transactionType: 'credit', + transactionMethod: 'card', + transactionStatus: 'complete-ok', + ip: '10.0.0.1', + amount: '10000', + currency: 'USD', + amountInEur: '8822.43', + description: 'Payment for order #12345', + customerCountry: 'US', + creationDate: `${new Date('2025-01-01').toISOString().slice(0, -5)}+00:00`, + creationTimestamp: new Date('2025-01-01').getTime(), + transactionSource: 'card-change', + adminId: 789, + fraudScore: 0, + cardProviderId: 123, + cardProvider: 'argus', + cardProviderName: 'Argus Payments', + cardHolderName: 'John Doe', + cardHolderCountry: 'US', + cardHolderState: 'CA', + cardType: 'visa', + cardNumber: '4111111111111111', + cardExpiryDate: '01/99', + email: 'customer@example.com', + cardId: 111, + backUrl: 'https://example.com/callback', + externalCustomData: JSON.stringify({ referenceId: 'trans_ref_1001' }), + cardDescriptor: 'XMoney Transaction', + } + + const mockTransaction: Transaction = { + ...mockTransactionSummary, + parentTransactionId: undefined, + relatedTransactionIds: [], + card: { + id: 111, + customerId: 789, + type: 'visa', + cardNumber: '4111111111111111', + expiryMonth: '01', + expiryYear: '2999', + nameOnCard: 'John Doe', + cardHolderCountry: 'US', + cardHolderState: 'CA', + cardProvider: 'argus', + hasToken: false, + cardStatus: 'active', + binInfo: { + bin: '411111', + brand: 'visa', + type: 'debit', + level: 'standard', + countryCode: 'US', + bank: 'Bank of America', + }, + }, + components: [], + } + + beforeEach(() => { + mockCore = { + request: vi.fn(), + } as any + + transactionsResource = new TransactionsResource(mockCore) + }) + + describe('retrieve', () => { + it('should retrieve a transaction by ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockTransaction, + }) + + const result = await transactionsResource.retrieve(1001) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/transaction/1001', + }) + expect(result).toEqual(mockTransaction) + }) + + it('should handle string ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: mockTransaction, + }) + + await transactionsResource.retrieve('1001' as any) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/transaction/1001', + }) + }) + }) + + describe('capture', () => { + it('should capture a transaction with full amount', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.capture(1001, { amount: 10000 }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/transaction/1001', + body: { amount: 10000 }, + }) + }) + + it('should capture a transaction with partial amount', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.capture(1001, { amount: 5000 }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/transaction/1001', + body: { amount: 5000 }, + }) + }) + + it('should handle string ID', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.capture('1001' as any, { amount: 7500 }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/transaction/1001', + body: { amount: 7500 }, + }) + }) + }) + + describe('refund', () => { + it('should refund a transaction without parameters (full refund)', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.refund(1001) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/transaction/1001', + }) + }) + + it('should refund with partial amount', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.refund(1001, { amount: 3000 }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/transaction/1001', + body: { amount: 3000 }, + }) + }) + + it('should refund with reason', async () => { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.refund(1001, { + amount: 5000, + reason: 'fraud-confirm', + }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/transaction/1001', + body: { + amount: 5000, + reason: 'fraud-confirm', + }, + }) + }) + + it('should handle all refund reasons', async () => { + const reasons = ['customer-demand', 'duplicated-transaction', 'fraud-confirm', 'highly-suspicious', 'test-transaction', 'card-expired'] as const + + for (const reason of reasons) { + mockCore.request = vi.fn().mockResolvedValue({}) + + await transactionsResource.refund(1001, { reason }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: '/transaction/1001', + body: { reason }, + }) + } + }) + }) + + describe('list', () => { + it('should list transactions without parameters', async () => { + const mockTransactions = [mockTransactionSummary] + const mockPagination = { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: mockTransactions, + pagination: mockPagination, + }) + + const result = await transactionsResource.list() + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/transaction', + }) + expect(result).toBeInstanceOf(PaginatedList) + expect(result.data).toEqual(mockTransactions) + }) + + it('should list transactions with all parameters', async () => { + const params = { + page: 2, + limit: 25, + customerId: 789, + cardId: 111, + orderId: 12345, + referenceId: 'trans_ref', + status: 'successful' as const, + type: 'preauth' as const, + gateway: 'stripe', + responseCode: '00', + tag: 'important', + searchId: 'search_trans_123', + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: [mockTransactionSummary], + pagination: { + currentPageNumber: 2, + pageCount: 10, + totalItemCount: 250, + itemCountPerPage: 25, + }, + }) + + await transactionsResource.list(params) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/transaction', + query: params, + }) + }) + + it('should handle empty results', async () => { + mockCore.request = vi.fn().mockResolvedValue({ + data: [], + pagination: { + currentPageNumber: 1, + pageCount: 0, + totalItemCount: 0, + itemCountPerPage: 10, + }, + }) + + const result = await transactionsResource.list({ transactionStatus: ['complete-failed'] }) + + expect(result.data).toEqual([]) + expect(result.totalCount).toBe(0) + expect(result.hasMore).toBe(false) + }) + }) + + describe('search', () => { + it('should search transactions', async () => { + const searchParams: TransactionListParams = { + customerId: 789, + transactionStatus: ['complete-ok'], + transactionType: 'credit', + perPage: 50, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_trans_456' }, + }) + + const result = await transactionsResource.search(searchParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/transaction-search', + body: searchParams, + }) + expect(result).toBeInstanceOf(SearchResult) + expect(result.searchId).toBe('search_trans_456') + }) + + it('should fetch search results', async () => { + const searchParams: TransactionListParams = { + walletProvider: 'paypal', + transactionStatus: ['complete-failed'], + } + + mockCore.request = vi.fn() + .mockResolvedValueOnce({ data: { searchId: 'search_trans_789' } }) + .mockResolvedValueOnce({ + data: [mockTransactionSummary], + pagination: { + currentPageNumber: 1, + pageCount: 1, + totalItemCount: 1, + itemCountPerPage: 10, + }, + searchParams, + }) + + const searchResult = await transactionsResource.search(searchParams) + const paginatedList = await searchResult.fetch() + + expect(paginatedList).toBeInstanceOf(PaginatedList) + expect(paginatedList.data).toEqual([mockTransactionSummary]) + expect(mockCore.request).toHaveBeenCalledTimes(2) + expect(mockCore.request).toHaveBeenLastCalledWith({ + method: 'GET', + path: '/transaction', + query: { searchId: 'search_trans_789' }, + }) + }) + + it('should handle search with date filters', async () => { + const searchParams: TransactionListParams = { + createdAtFrom: new Date('2025-01-01'), + createdAtTo: new Date('2025-12-31'), + amountFrom: 1000, + amountTo: 50000, + } + + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: 'search_date_amount' }, + }) + + await transactionsResource.search(searchParams) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/transaction-search', + body: searchParams, + }) + }) + + it('should handle all transaction types in search', async () => { + const transactionTypes = ['deposit', 'credit', 'refund', 'chargeback', 'representment'] as const + + for (const transactionType of transactionTypes) { + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: `search_${transactionType}` }, + }) + + await transactionsResource.search({ transactionType }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/transaction-search', + body: { transactionType }, + }) + } + }) + + it('should handle all transaction statuses in search', async () => { + const transactionStatuses = ['complete-ok', 'cancel-ok', 'refund-ok', 'void-ok', 'charge-back', 'complete-failed', 'in-progress', '3d-pending'] as const + + for (const transactionStatus of transactionStatuses) { + mockCore.request = vi.fn().mockResolvedValue({ + data: { searchId: `search_${transactionStatus}` }, + }) + + await transactionsResource.search({ transactionStatus: [transactionStatus] }) + + expect(mockCore.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/transaction-search', + body: { transactionStatus: [transactionStatus] }, + }) + } + }) + }) +}) diff --git a/test/utils/date-transformer.test.ts b/test/utils/date-transformer.test.ts new file mode 100644 index 0000000..d20873d --- /dev/null +++ b/test/utils/date-transformer.test.ts @@ -0,0 +1,214 @@ +import { describe, expect, it } from 'vitest' +import { DateTransformer } from '../../src/utils' + +describe('dateTransformer', () => { + describe('toApi', () => { + it('should handle null and undefined', () => { + expect(DateTransformer.toApi(null)).toBe(null) + expect(DateTransformer.toApi(undefined)).toBe(undefined) + }) + + it('should handle non-object values', () => { + expect(DateTransformer.toApi('string')).toBe('string') + expect(DateTransformer.toApi(123)).toBe(123) + expect(DateTransformer.toApi(true)).toBe(true) + }) + + it('should transform Date objects to ISO strings with timezone', () => { + const date = new Date('2023-01-01T12:00:00.000Z') + const result = DateTransformer.toApi(date) + expect(result).toBe('2023-01-01T12:00:00+00:00') + }) + + it('should transform Date properties in objects', () => { + const date = new Date('2023-01-01T12:00:00.000Z') + const input = { + createdAt: date, + name: 'test', + updatedAt: date, + } + const result = DateTransformer.toApi(input) + expect(result).toEqual({ + createdAt: '2023-01-01T12:00:00+00:00', + name: 'test', + updatedAt: '2023-01-01T12:00:00+00:00', + }) + }) + + it('should handle nested objects', () => { + const date = new Date('2023-01-01T12:00:00.000Z') + const input = { + data: { + createdAt: date, + nested: { + updatedAt: date, + }, + }, + } + const result = DateTransformer.toApi(input) + expect(result).toEqual({ + data: { + createdAt: '2023-01-01T12:00:00+00:00', + nested: { + updatedAt: '2023-01-01T12:00:00+00:00', + }, + }, + }) + }) + + it('should handle arrays', () => { + const date = new Date('2023-01-01T12:00:00.000Z') + const input = [ + { createdAt: date }, + { updatedAt: date }, + ] + const result = DateTransformer.toApi(input) + expect(result).toEqual([ + { createdAt: '2023-01-01T12:00:00+00:00' }, + { updatedAt: '2023-01-01T12:00:00+00:00' }, + ]) + }) + + it('should handle arrays of dates', () => { + const date1 = new Date('2023-01-01T12:00:00.000Z') + const date2 = new Date('2023-12-31T23:59:59.000Z') + const input = [date1, date2] + const result = DateTransformer.toApi(input) + expect(result).toEqual([ + '2023-01-01T12:00:00+00:00', + '2023-12-31T23:59:59+00:00', + ]) + }) + }) + + describe('fromApi', () => { + it('should handle null and undefined', () => { + expect(DateTransformer.fromApi(null)).toBe(null) + expect(DateTransformer.fromApi(undefined)).toBe(undefined) + }) + + it('should handle non-object values', () => { + expect(DateTransformer.fromApi('string')).toBe('string') + expect(DateTransformer.fromApi(123)).toBe(123) + expect(DateTransformer.fromApi(true)).toBe(true) + }) + + it('should convert date strings to Date objects for known fields', () => { + const input = { + createdAt: '2023-01-01T12:00:00+00:00', + updatedAt: '2023-01-01T12:00:00.000Z', + name: 'test', + } + const result = DateTransformer.fromApi(input) + expect(result.createdAt).toBeInstanceOf(Date) + expect(result.updatedAt).toBeInstanceOf(Date) + expect((result.createdAt as unknown as Date).toISOString()).toBe('2023-01-01T12:00:00.000Z') + expect((result.updatedAt as unknown as Date).toISOString()).toBe('2023-01-01T12:00:00.000Z') + expect(result.name).toBe('test') + }) + + it('should handle all known date fields', () => { + const dateString = '2023-01-01T12:00:00+00:00' + const input = { + createdAt: dateString, + createdAtFrom: dateString, + createdAtTo: dateString, + creationDate: dateString, + updatedAt: dateString, + deletedAt: dateString, + componentDate: dateString, + dueDate: dateString, + startDate: dateString, + endDate: dateString, + otherField: dateString, + } + const result = DateTransformer.fromApi(input) + + // All known date fields should be converted + expect(result.createdAt).toBeInstanceOf(Date) + expect(result.createdAtFrom).toBeInstanceOf(Date) + expect(result.createdAtTo).toBeInstanceOf(Date) + expect(result.creationDate).toBeInstanceOf(Date) + expect(result.updatedAt).toBeInstanceOf(Date) + expect(result.deletedAt).toBeInstanceOf(Date) + expect(result.expiryDate).toBeInstanceOf(Date) + expect(result.componentDate).toBeInstanceOf(Date) + expect(result.cardExpiryDate).toBeInstanceOf(Date) + expect(result.dueDate).toBeInstanceOf(Date) + expect(result.startDate).toBeInstanceOf(Date) + expect(result.endDate).toBeInstanceOf(Date) + + // Unknown field should remain as string + expect(result.otherField).toBe(dateString) + }) + + it('should not convert non-date strings', () => { + const input = { + createdAt: 'not-a-date', + updatedAt: 'invalid-date', + } + const result = DateTransformer.fromApi(input) + expect(result.createdAt).toBe('not-a-date') + expect(result.updatedAt).toBe('invalid-date') + }) + + it('should not convert non-string values in date fields', () => { + const input = { + createdAt: 123, + updatedAt: null, + deletedAt: undefined, + expiryDate: true, + } + const result = DateTransformer.fromApi(input) + expect(result.createdAt).toBe(123) + expect(result.updatedAt).toBe(null) + expect(result.deletedAt).toBe(undefined) + expect(result.expiryDate).toBe(true) + }) + + it('should handle nested objects', () => { + const dateString = '2023-01-01T12:00:00+00:00' + const input = { + data: { + createdAt: dateString, + nested: { + updatedAt: dateString, + }, + }, + } + const result = DateTransformer.fromApi(input) + expect(result.data.createdAt).toBeInstanceOf(Date) + expect(result.data.nested.updatedAt).toBeInstanceOf(Date) + }) + + it('should handle arrays', () => { + const dateString = '2023-01-01T12:00:00+00:00' + const input = [ + { createdAt: dateString }, + { updatedAt: dateString }, + ] + const result = DateTransformer.fromApi(input) + expect(result[0].createdAt).toBeInstanceOf(Date) + expect(result[1].updatedAt).toBeInstanceOf(Date) + }) + }) + + describe('round-trip conversion', () => { + it('should maintain date values through round-trip conversion', () => { + const original = { + createdAt: new Date('2023-01-01T12:00:00.000Z'), + updatedAt: new Date('2023-12-31T23:59:59.000Z'), + name: 'test', + count: 42, + } + + const apiFormat = DateTransformer.toApi(original) + const restored = DateTransformer.fromApi(apiFormat) + + expect(restored.createdAt.toISOString()).toBe(original.createdAt.toISOString()) + expect(restored.updatedAt.toISOString()).toBe(original.updatedAt.toISOString()) + expect(restored.name).toBe(original.name) + expect(restored.count).toBe(original.count) + }) + }) +}) diff --git a/test/utils/index.test.ts b/test/utils/index.test.ts new file mode 100644 index 0000000..8ec482f --- /dev/null +++ b/test/utils/index.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from 'vitest' +import { DateTransformer } from '../../src/utils' + +describe('utils exports', () => { + it('should export DateTransformer', () => { + expect(DateTransformer).toBeDefined() + expect(typeof DateTransformer.toApi).toBe('function') + expect(typeof DateTransformer.fromApi).toBe('function') + }) +}) diff --git a/tsconfig.build.json b/tsconfig.build.json deleted file mode 100644 index 64f86c6..0000000 --- a/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/tsconfig.json b/tsconfig.json index 41c8576..7e753fa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,36 +1,19 @@ { "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "es2017", - "sourceMap": true, - "outDir": "./lib", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, + "target": "ESNext", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, "strict": true, + "strictNullChecks": true, + "declaration": true, + "noEmit": true, + "outDir": "./dist", "esModuleInterop": true, - "strictBindCallApply": false, - "alwaysStrict": true, - "allowUnreachableCode": false, - "noImplicitAny": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, - "importHelpers": true, - "strictPropertyInitialization": false, - "resolveJsonModule": true, - "typeRoots": [ - "src/@types", - "node_modules/@types" - ], + "verbatimModuleSyntax": true, + "skipDefaultLibCheck": true, + "skipLibCheck": true } -} \ No newline at end of file +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..6b3fd21 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: ['**/node_modules/**', '**/dist/**', '**/stripe-node/**'], + include: ['test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + testTimeout: 10000, + server: { + deps: { + inline: ['vitest-package-exports'], + }, + }, + }, +})